Compare commits
465 Commits
Author | SHA1 | Date |
---|---|---|
Naveen Albert | 8dd77df69b | |
Naveen Albert | b05865d8e9 | |
Sean Bright | 18ed610d4e | |
Sean Bright | 4ecbac98d8 | |
Stanislav Abramenkov | 83a4d37d9f | |
Sean Bright | 1965574944 | |
Maximilian Fridrich | 3d71b494d8 | |
Sean Bright | 6938b7f962 | |
George Joseph | 755bf72329 | |
George Joseph | 12d2bd23d5 | |
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,149 @@
|
|||
name: PRSubmitActions
|
||||
run-name: "PRSubmitActions: Test ${{github.event.action}}"
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [PRSubmitTests]
|
||||
types:
|
||||
- requested
|
||||
- completed
|
||||
env:
|
||||
ACTION: ${{ github.event.action }}
|
||||
CONCLUSION: ${{ github.event.workflow_run.conclusion }}
|
||||
REPO: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
PRSubmitActions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get PR Number
|
||||
id: getpr
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
retries: 5
|
||||
script: |
|
||||
let search = `repo:${context.repo.owner}/${context.repo.repo} ${context.payload.workflow_run.head_sha}`;
|
||||
let prs = await github.rest.search.issuesAndPullRequests({
|
||||
q: search,
|
||||
});
|
||||
if (prs.data.total_count == 0) {
|
||||
core.setFailed(`Unable to get PR for ${context.payload.workflow_run.head_sha}`);
|
||||
return;
|
||||
}
|
||||
let pr_number = prs.data.items[0].number;
|
||||
core.setOutput('pr_number', pr_number);
|
||||
return;
|
||||
|
||||
- name: Set Label
|
||||
id: setlabel
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ steps.getpr.outputs.PR_NUMBER }}
|
||||
LABEL_TIP: ${{ vars.PR_SUBMIT_TESTING_IN_PROGRESS }}
|
||||
LABEL_PASS: ${{ vars.PR_SUBMIT_TESTS_PASSED }}
|
||||
LABEL_FAIL: ${{ vars.PR_SUBMIT_TESTS_FAILED }}
|
||||
with:
|
||||
retries: 5
|
||||
script: |
|
||||
let label;
|
||||
if (process.env.ACTION === 'requested') {
|
||||
label = process.env.LABEL_TIP;
|
||||
} else {
|
||||
if ( process.env.CONCLUSION === 'success' ) {
|
||||
label = process.env.LABEL_PASS;
|
||||
} else {
|
||||
label = process.env.LABEL_FAIL;
|
||||
}
|
||||
}
|
||||
core.info(`Setting label ${label}`);
|
||||
github.rest.issues.setLabels({
|
||||
issue_number: process.env.PR_NUMBER,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: [ label ]
|
||||
});
|
||||
return;
|
||||
|
||||
- name: Get cherry-pick branches
|
||||
if: github.event.action == 'completed'
|
||||
id: getbranches
|
||||
uses: asterisk/asterisk-ci-actions/GetCherryPickBranchesFromPR@main
|
||||
with:
|
||||
repo: ${{env.REPO}}
|
||||
pr_number: ${{steps.getpr.outputs.PR_NUMBER}}
|
||||
cherry_pick_regex: ${{vars.CHERRY_PICK_REGEX}}
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
- name: Add cherry-pick reminder
|
||||
if: github.event.action == 'completed'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{steps.getpr.outputs.PR_NUMBER}}
|
||||
CHERRY_PICK_REMINDER: ${{vars.CHERRY_PICK_REMINDER}}
|
||||
BRANCHES_OUTPUT: ${{toJSON(steps.getbranches.outputs)}}
|
||||
BRANCH_COUNT: ${{steps.getbranches.outputs.branch_count}}
|
||||
FORCED_NONE: ${{steps.getbranches.outputs.forced_none}}
|
||||
with:
|
||||
retries: 5
|
||||
script: |
|
||||
if (process.env.FORCED_NONE === 'true' ||
|
||||
process.env.BRANCH_COUNT > 0) {
|
||||
core.info("No cherry-pick reminder needed.");
|
||||
return;
|
||||
}
|
||||
let comments = await github.rest.issues.listComments({
|
||||
issue_number: process.env.PR_NUMBER,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
});
|
||||
let found = false;
|
||||
for (const c of comments.data) {
|
||||
if (c.body.startsWith("<!--CPR-->")) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
core.info("Cherry-pick reminder already exists.");
|
||||
return;
|
||||
}
|
||||
core.info("Adding cherry-pick reminder.");
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: process.env.PR_NUMBER,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: process.env.CHERRY_PICK_REMINDER
|
||||
})
|
||||
return;
|
||||
|
||||
- name: Add reviewers
|
||||
if: github.event.action == 'completed'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{steps.getpr.outputs.PR_NUMBER}}
|
||||
REVIEWERS: ${{vars.PR_REVIEWERS}}
|
||||
with:
|
||||
retries: 5
|
||||
github-token: ${{ secrets.ASTERISKTEAM_PAT }}
|
||||
script: |
|
||||
let rs = JSON.parse(process.env.REVIEWERS.length ? process.env.REVIEWERS : '[]');
|
||||
let users = [];
|
||||
let teams = [];
|
||||
for (const r of rs) {
|
||||
if (r.indexOf("/") > 0) {
|
||||
teams.push(r.split('/')[1]);
|
||||
} else {
|
||||
users.push(r);
|
||||
}
|
||||
}
|
||||
if (teams.length > 0 || users.length > 0) {
|
||||
core.info(`Adding user reviewers ${users}`);
|
||||
core.info(`Adding team reviewers ${teams}`);
|
||||
await github.rest.pulls.requestReviewers({
|
||||
pull_number: process.env.PR_NUMBER,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
reviewers: users,
|
||||
team_reviewers: teams
|
||||
});
|
||||
}
|
||||
return;
|
|
@ -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
|
||||
=================================
|
||||
|
||||
To learn about and report Asterisk bugs, please visit
|
||||
To learn about and report Asterisk bugs, please visit
|
||||
the official Asterisk Bug Tracker at:
|
||||
|
||||
https://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
|
||||
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
|
||||
resist the temptation to post it to the bug tracker.
|
||||
Feature requests should be posted to the asterisk-dev
|
||||
mailing list, located at:
|
||||
|
||||
http://lists.digium.com
|
||||
http://lists.digium.com
|
||||
|
||||
Thank you!
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -45,7 +45,7 @@ redistribution of Asterisk source code obtained from Digium, you
|
|||
should contact our licensing department to determine the necessary
|
||||
steps you must take. For more information on this policy, please read:
|
||||
|
||||
https://www.sangoma.com/wp-content/uploads/Sangoma-Trademark-Policy.pdf
|
||||
https://www.sangoma.com/wp-content/uploads/Sangoma-Trademark-Policy-1.pdf
|
||||
|
||||
If you have any questions regarding our licensing policy, please
|
||||
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=) 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 ),)
|
||||
main: third-party
|
||||
|
@ -403,7 +403,7 @@ defaults.h: makeopts .lastclean build_tools/make_defaults_h
|
|||
@cmp -s $@.tmp $@ || mv $@.tmp $@
|
||||
@rm -f $@.tmp
|
||||
|
||||
main/version.c: FORCE menuselect.makeopts .lastclean
|
||||
main/version.c: FORCE include/asterisk/buildopts.h menuselect.makeopts .lastclean
|
||||
@build_tools/make_version_c > $@.tmp
|
||||
@cmp -s $@.tmp $@ || mv $@.tmp $@
|
||||
@rm -f $@.tmp
|
||||
|
@ -545,7 +545,7 @@ INSTALLDIRS="$(ASTLIBDIR)" "$(ASTMODDIR)" "$(ASTSBINDIR)" "$(ASTCACHEDIR)" "$(AS
|
|||
"$(ASTDATADIR)/firmware/iax" "$(ASTDATADIR)/images" "$(ASTDATADIR)/keys" \
|
||||
"$(ASTDATADIR)/phoneprov" "$(ASTDATADIR)/rest-api" "$(ASTDATADIR)/static-http" \
|
||||
"$(ASTDATADIR)/sounds" "$(ASTDATADIR)/moh" "$(ASTMANDIR)/man8" "$(AGI_DIR)" "$(ASTDBDIR)" \
|
||||
"$(ASTDATADIR)/third-party" "${ASTDATADIR}/keys/stir_shaken"
|
||||
"$(ASTDATADIR)/third-party" "${ASTDATADIR}/keys/stir_shaken" "${ASTDATADIR}/keys/stir_shaken/cache"
|
||||
|
||||
installdirs:
|
||||
@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/astgenkey "$(DESTDIR)$(ASTSBINDIR)/"
|
||||
$(INSTALL) -m 755 contrib/scripts/autosupport "$(DESTDIR)$(ASTSBINDIR)/"
|
||||
if [ ! -f /sbin/launchd ]; then \
|
||||
./build_tools/install_subst contrib/scripts/safe_asterisk "$(DESTDIR)$(ASTSBINDIR)/safe_asterisk"; \
|
||||
fi
|
||||
ifneq ($(HAVE_SBIN_LAUNCHD),1)
|
||||
./build_tools/install_subst contrib/scripts/safe_asterisk "$(DESTDIR)$(ASTSBINDIR)/safe_asterisk";
|
||||
endif
|
||||
|
||||
ifneq ($(DISABLE_XMLDOC),yes)
|
||||
$(INSTALL) -m 644 doc/core-*.xml "$(DESTDIR)$(ASTDATADIR)/documentation"
|
||||
|
@ -1119,7 +1119,8 @@ ifeq ($(PYTHON),:)
|
|||
else
|
||||
@$(INSTALL) -d doc/rest-api
|
||||
$(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
|
||||
|
||||
check-alembic: makeopts
|
||||
|
|
|
@ -213,10 +213,10 @@ endif
|
|||
# extern const size_t _binary_abc_def_xml_size;
|
||||
%.o: %.xml
|
||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
$(CMD_PREFIX) $(CC) -g -Wl,-znoexecstack -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
|
||||
%.o: %.xslt
|
||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
$(CMD_PREFIX) $(CC) -g -Wl,-znoexecstack -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
|
||||
dist-clean:: clean
|
||||
|
|
|
@ -377,9 +377,8 @@ is set to no.
|
|||
|
||||
In Asterisk 12 and later, live_dangerously defaults to no.
|
||||
|
||||
|
||||
[voip-security-webinar]: https://www.asterisk.org/security/webinar/
|
||||
[blog-sip-security]: http://blogs.digium.com/2009/03/28/sip-security/
|
||||
[voip-security-webinar]: https://docs.asterisk.org/Deployment/Important-Security-Considerations/Asterisk-Security-Webinars/
|
||||
[blog-sip-security]: https://web.archive.org/web/20171030134647/http://blogs.digium.com/2009/03/28/sip-security/
|
||||
[Strong Password Generator]: https://www.strongpasswordgenerator.com
|
||||
[Filtering Data]: #filtering-data
|
||||
[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
|
||||
[Manager Class Authorizations]: #manager-class-authorizations
|
||||
[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.
|
||||
|
||||
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].
|
||||
|
||||
There is a book on Asterisk published by O'Reilly under the Creative Commons
|
||||
|
@ -48,7 +48,7 @@ ANY special hardware, not even a sound card) to install and run Asterisk.
|
|||
|
||||
Supported telephony hardware includes:
|
||||
* All Analog and Digital Interface cards from [Sangoma]
|
||||
* QuickNet Internet PhoneJack and LineJack (http://www.quicknet.net)
|
||||
* QuickNet Internet PhoneJack and LineJack
|
||||
* any full duplex sound card supported by ALSA, OSS, or PortAudio
|
||||
* any ISDN card supported by mISDN on Linux
|
||||
* The Xorcom Astribank channel bank
|
||||
|
@ -258,7 +258,7 @@ Asterisk is a trademark of Sangoma Technologies Corporation
|
|||
|
||||
[home page]: https://www.asterisk.org
|
||||
[support]: https://www.asterisk.org/support
|
||||
[wiki]: https://wiki.asterisk.org/
|
||||
[documentation]: https://docs.asterisk.org/
|
||||
[mailing list]: http://lists.digium.com/mailman/listinfo/asterisk-users
|
||||
[chan_dahdi.conf]: configs/samples/chan_dahdi.conf.sample
|
||||
[voip-info.org]: http://www.voip-info.org/wiki-Asterisk
|
||||
|
@ -269,4 +269,4 @@ Asterisk is a trademark of Sangoma Technologies Corporation
|
|||
[CHANGES]: CHANGES
|
||||
[configs]: configs
|
||||
[doc]: doc
|
||||
[Important Security Considerations]: https://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 ;-) */
|
||||
if(ie->discriminator == Q931CallingPartyNumberIE)
|
||||
{
|
||||
int numoffset=1;
|
||||
OOTRACEDBGB1(" CallingPartyNumber IE = {\n");
|
||||
if(ie->length < OO_MAX_NUMBER_LENGTH)
|
||||
if(!(0x80 & ie->data[0])) numoffset = 2;
|
||||
|
||||
if( (ie->length >= numoffset) &&
|
||||
(ie->length < OO_MAX_NUMBER_LENGTH) )
|
||||
{
|
||||
int numoffset=1;
|
||||
if(!(0x80 & ie->data[0])) numoffset = 2;
|
||||
memcpy(number, ie->data+numoffset,ie->length-numoffset);
|
||||
number[ie->length-numoffset]='\0';
|
||||
OOTRACEDBGB2(" %s\n", number);
|
||||
|
@ -204,7 +206,7 @@ EXTERN int ooQ931Decode
|
|||
ooCallSetCallingPartyNumber(call, number);
|
||||
}
|
||||
else{
|
||||
OOTRACEERR3("Error:Calling party number too long. (%s, %s)\n",
|
||||
OOTRACEERR3("Error:Calling party number outside range. (%s, %s)\n",
|
||||
call->callType, call->callToken);
|
||||
}
|
||||
OOTRACEDBGB1(" }\n");
|
||||
|
@ -214,7 +216,8 @@ EXTERN int ooQ931Decode
|
|||
if(ie->discriminator == Q931CalledPartyNumberIE)
|
||||
{
|
||||
OOTRACEDBGB1(" CalledPartyNumber IE = {\n");
|
||||
if(ie->length < OO_MAX_NUMBER_LENGTH)
|
||||
if( (ie->length >= 1) &&
|
||||
(ie->length < OO_MAX_NUMBER_LENGTH) )
|
||||
{
|
||||
memcpy(number, ie->data+1,ie->length-1);
|
||||
number[ie->length-1]='\0';
|
||||
|
@ -223,7 +226,7 @@ EXTERN int ooQ931Decode
|
|||
ooCallSetCalledPartyNumber(call, number);
|
||||
}
|
||||
else{
|
||||
OOTRACEERR3("Error:Calling party number too long. (%s, %s)\n",
|
||||
OOTRACEERR3("Error:Calling party number outside range. (%s, %s)\n",
|
||||
call->callType, call->callToken);
|
||||
}
|
||||
OOTRACEDBGB1(" }\n");
|
||||
|
|
|
@ -84,7 +84,7 @@ struct mysql_conn {
|
|||
AST_RWLIST_ENTRY(mysql_conn) list;
|
||||
ast_mutex_t lock;
|
||||
MYSQL handle;
|
||||
char host[50];
|
||||
char host[MAXHOSTNAMELEN];
|
||||
char name[50];
|
||||
char user[50];
|
||||
char pass[50];
|
||||
|
|
|
@ -92,6 +92,12 @@
|
|||
<para>Is the maximum duration of a word to accept.</para>
|
||||
<para>If exceeded, then the result is detection as a MACHINE</para>
|
||||
</parameter>
|
||||
<parameter name="audioFile" required="false">
|
||||
<para>Is an audio file to play to the caller while AMD is in progress.</para>
|
||||
<para>By default, no audio file is played.</para>
|
||||
<para>If an audio file is configured in amd.conf, then that file will be used
|
||||
if one is not specified here. That file may be overridden by this argument.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This application attempts to detect answering machines at the beginning
|
||||
|
@ -155,6 +161,9 @@ static int dfltBetweenWordsSilence = 50;
|
|||
static int dfltMaximumNumberOfWords = 2;
|
||||
static int dfltSilenceThreshold = 256;
|
||||
static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
|
||||
static char *dfltAudioFile = NULL;
|
||||
|
||||
static ast_mutex_t config_lock;
|
||||
|
||||
/* Set to the lowest ms value provided in amd.conf or application parameters */
|
||||
static int dfltMaxWaitTimeForFrame = 50;
|
||||
|
@ -179,7 +188,7 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
char amdCause[256] = "", amdStatus[256] = "";
|
||||
char *parse = ast_strdupa(data);
|
||||
|
||||
/* Lets set the initial values of the variables that will control the algorithm.
|
||||
/* Let's set the initial values of the variables that will control the algorithm.
|
||||
The initial values are the default ones. If they are passed as arguments
|
||||
when invoking the application, then the default values will be overwritten
|
||||
by the ones passed as parameters. */
|
||||
|
@ -193,6 +202,7 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
int silenceThreshold = dfltSilenceThreshold;
|
||||
int maximumWordLength = dfltMaximumWordLength;
|
||||
int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
|
||||
const char *audioFile = NULL;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(argInitialSilence);
|
||||
|
@ -204,8 +214,15 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
AST_APP_ARG(argMaximumNumberOfWords);
|
||||
AST_APP_ARG(argSilenceThreshold);
|
||||
AST_APP_ARG(argMaximumWordLength);
|
||||
AST_APP_ARG(audioFile);
|
||||
);
|
||||
|
||||
ast_mutex_lock(&config_lock);
|
||||
if (!ast_strlen_zero(dfltAudioFile)) {
|
||||
audioFile = ast_strdupa(dfltAudioFile);
|
||||
}
|
||||
ast_mutex_unlock(&config_lock);
|
||||
|
||||
ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
|
||||
S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
|
||||
S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
|
||||
|
@ -233,6 +250,9 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
silenceThreshold = atoi(args.argSilenceThreshold);
|
||||
if (!ast_strlen_zero(args.argMaximumWordLength))
|
||||
maximumWordLength = atoi(args.argMaximumWordLength);
|
||||
if (!ast_strlen_zero(args.audioFile)) {
|
||||
audioFile = args.audioFile;
|
||||
}
|
||||
} else {
|
||||
ast_debug(1, "AMD using the default parameters.\n");
|
||||
}
|
||||
|
@ -280,6 +300,11 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
/* Set our start time so we can tie the loop to real world time and not RTP updates */
|
||||
amd_tvstart = ast_tvnow();
|
||||
|
||||
/* Optional audio file to play to caller while AMD is doing its thing. */
|
||||
if (!ast_strlen_zero(audioFile)) {
|
||||
ast_streamfile(chan, audioFile, ast_channel_language(chan));
|
||||
}
|
||||
|
||||
/* Now we go into a loop waiting for frames from the channel */
|
||||
while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
|
||||
int ms = 0;
|
||||
|
@ -462,10 +487,14 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
/* Free the DSP used to detect silence */
|
||||
ast_dsp_free(silenceDetector);
|
||||
|
||||
/* If we were playing something to pass the time, stop it now. */
|
||||
if (!ast_strlen_zero(audioFile)) {
|
||||
ast_stopstream(chan);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static int amd_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
isAnsweringMachine(chan, data);
|
||||
|
@ -516,7 +545,16 @@ static int load_config(int reload)
|
|||
dfltMaximumNumberOfWords = atoi(var->value);
|
||||
} else if (!strcasecmp(var->name, "maximum_word_length")) {
|
||||
dfltMaximumWordLength = atoi(var->value);
|
||||
|
||||
} else if (!strcasecmp(var->name, "playback_file")) {
|
||||
ast_mutex_lock(&config_lock);
|
||||
if (dfltAudioFile) {
|
||||
ast_free(dfltAudioFile);
|
||||
dfltAudioFile = NULL;
|
||||
}
|
||||
if (!ast_strlen_zero(var->value)) {
|
||||
dfltAudioFile = ast_strdup(var->value);
|
||||
}
|
||||
ast_mutex_unlock(&config_lock);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
|
||||
app, cat, var->name, var->lineno);
|
||||
|
@ -529,7 +567,7 @@ static int load_config(int reload)
|
|||
|
||||
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",
|
||||
dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
|
||||
dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
|
||||
|
@ -539,6 +577,12 @@ static int load_config(int reload)
|
|||
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_mutex_lock(&config_lock);
|
||||
if (dfltAudioFile) {
|
||||
ast_free(dfltAudioFile);
|
||||
}
|
||||
ast_mutex_unlock(&config_lock);
|
||||
ast_mutex_destroy(&config_lock);
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
|
@ -554,6 +598,7 @@ static int unload_module(void)
|
|||
*/
|
||||
static int load_module(void)
|
||||
{
|
||||
ast_mutex_init(&config_lock);
|
||||
if (load_config(0) || ast_register_application_xml(app, amd_exec)) {
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
</syntax>
|
||||
<description>
|
||||
<para>Connects to the given TCP service, then transmits channel audio over that socket. In turn, audio is received from the socket and sent to the channel. Only audio frames will be transmitted.</para>
|
||||
<para>Protocol is specified at https://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>
|
||||
</description>
|
||||
</application>
|
||||
|
@ -180,7 +180,7 @@ static int audiosocket_run(struct ast_channel *chan, const char *id, int svc)
|
|||
chanName = ast_channel_name(chan);
|
||||
|
||||
while (1) {
|
||||
|
||||
ms = -1;
|
||||
targetChan = ast_waitfor_nandfds(&chan, 1, &svc, 1, NULL, &outfd, &ms);
|
||||
if (targetChan) {
|
||||
f = ast_read(chan);
|
||||
|
|
|
@ -95,8 +95,17 @@ static const char app[] = "Authenticate";
|
|||
maxdigits have been entered (without requiring the user to press the <literal>#</literal> key).
|
||||
Defaults to 0 - no limit - wait for the user press the <literal>#</literal> key.</para>
|
||||
</parameter>
|
||||
<parameter name="prompt" required="false">
|
||||
<para>Override the agent-pass prompt file.</para>
|
||||
<parameter name="prompt" required="false" argsep="&">
|
||||
<para>Override the "agent-pass" sound file. Can be
|
||||
an ampersand separated list of filenames. If the filename
|
||||
is a relative filename (it does not begin with a slash), it
|
||||
will be searched for in the Asterisk sounds directory. If the
|
||||
filename is able to be parsed as a URL, Asterisk will
|
||||
download the file and then begin playback on it. To include a
|
||||
literal <literal>&</literal> in the URL you can enclose
|
||||
the URL in single quotes.</para>
|
||||
<argument name="prompt" required="true" />
|
||||
<argument name="prompt2" multiple="true" />
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
|
|
|
@ -100,6 +100,9 @@
|
|||
<para>Automatically exit the bridge and return to the PBX after
|
||||
<emphasis>duration</emphasis> seconds.</para>
|
||||
</option>
|
||||
<option name="n">
|
||||
<para>Do not automatically answer the channel.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
|
@ -108,7 +111,7 @@
|
|||
The channel will then wait in the holding bridge until some event occurs
|
||||
which removes it from the holding bridge.</para>
|
||||
<note><para>This application will answer calls which haven't already
|
||||
been answered.</para></note>
|
||||
been answered, unless the n option is provided.</para></note>
|
||||
</description>
|
||||
</application>
|
||||
***/
|
||||
|
@ -186,6 +189,7 @@ enum bridgewait_flags {
|
|||
MUXFLAG_MOHCLASS = (1 << 0),
|
||||
MUXFLAG_ENTERTAINMENT = (1 << 1),
|
||||
MUXFLAG_TIMEOUT = (1 << 2),
|
||||
MUXFLAG_NOANSWER = (1 << 3),
|
||||
};
|
||||
|
||||
enum bridgewait_args {
|
||||
|
@ -199,6 +203,7 @@ AST_APP_OPTIONS(bridgewait_opts, {
|
|||
AST_APP_OPTION_ARG('e', MUXFLAG_ENTERTAINMENT, OPT_ARG_ENTERTAINMENT),
|
||||
AST_APP_OPTION_ARG('m', MUXFLAG_MOHCLASS, OPT_ARG_MOHCLASS),
|
||||
AST_APP_OPTION_ARG('S', MUXFLAG_TIMEOUT, OPT_ARG_TIMEOUT),
|
||||
AST_APP_OPTION('n', MUXFLAG_NOANSWER),
|
||||
});
|
||||
|
||||
static int bridgewait_timeout_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
||||
|
@ -458,7 +463,7 @@ static int bridgewait_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
|
||||
/* Answer the channel if needed */
|
||||
if (ast_channel_state(chan) != AST_STATE_UP) {
|
||||
if (ast_channel_state(chan) != AST_STATE_UP && !ast_test_flag(&flags, MUXFLAG_NOANSWER)) {
|
||||
ast_answer(chan);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,619 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2022, Naveen Albert
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Channel audio broadcasting
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/audiohook.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/autochan.h"
|
||||
#include "asterisk/format_cache.h"
|
||||
#include "asterisk/cli.h" /* use ESS macro */
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="Broadcast" language="en_US">
|
||||
<synopsis>
|
||||
Transmit or receive audio to or from multiple channels simultaneously
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="options">
|
||||
<optionlist>
|
||||
<option name="b">
|
||||
<para>In addition to broadcasting to target channels, also
|
||||
broadcast to any channels to which target channels are bridged.</para>
|
||||
</option>
|
||||
<option name="l">
|
||||
<para>Allow usage of a long queue to store audio frames.</para>
|
||||
<note><para>This may introduce some delay in the received audio feed, but will improve the audio quality.</para></note>
|
||||
</option>
|
||||
<option name="o">
|
||||
<para>Do not mix streams when combining audio from target channels (only applies with s option).</para>
|
||||
</option>
|
||||
<option name="r">
|
||||
<para>Feed frames to barge channels in "reverse" by injecting them into the primary channel's read queue instead.</para>
|
||||
<para>This option is required for barge to work in a n-party bridge (but not for 2-party bridges). Alternately, you
|
||||
can add an intermediate channel by using a non-optimized Local channel, so that the target channel is bridged with
|
||||
a single channel that is connected to the bridge, but it is recommended this option be used instead.</para>
|
||||
<para>Note that this option will always feed injected audio to the other party, regardless of whether the target
|
||||
channel is bridged or not.</para>
|
||||
</option>
|
||||
<option name="s">
|
||||
<para>Rather than broadcast audio to a bunch of channels, receive the combined audio from the target channels.</para>
|
||||
</option>
|
||||
<option name="w">
|
||||
<para>Broadcast audio received on this channel to other channels.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
<parameter name="channels" required="true" argsep=",">
|
||||
<para>List of channels for broadcast targets.</para>
|
||||
<para>Channel names must be the full channel names, not merely device names.</para>
|
||||
<para>Broadcasting will continue until the broadcasting channel hangs up or all target channels have hung up.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This application can be used to broadcast audio to multiple channels at once.
|
||||
Any audio received on this channel will be transmitted to all of the specified channels and, optionally, their bridged peers.</para>
|
||||
<para>It can also be used to aggregate audio from multiple channels at once.
|
||||
Any audio on any of the specified channels, and optionally their bridged peers, will be transmitted to this channel.</para>
|
||||
<para>Execution of the application continues until either the broadcasting channel hangs up
|
||||
or all specified channels have hung up.</para>
|
||||
<para>This application is used for one-to-many and many-to-one audio applications where
|
||||
bridge mixing cannot be done synchronously on all the involved channels.
|
||||
This is primarily useful for injecting the same audio stream into multiple channels at once,
|
||||
or doing the reverse, combining the audio from multiple channels into a single stream.
|
||||
This contrasts with using a separate injection channel for each target channel and/or
|
||||
using a conference bridge.</para>
|
||||
<para>The channel running the Broadcast application must do so synchronously. The specified channels,
|
||||
however, may be doing other things.</para>
|
||||
<example title="Broadcast received audio to three channels and their bridged peers">
|
||||
same => n,Broadcast(wb,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Broadcast received audio to three channels, only">
|
||||
same => n,Broadcast(w,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Combine audio from three channels and their bridged peers to us">
|
||||
same => n,Broadcast(s,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Combine audio from three channels to us">
|
||||
same => n,Broadcast(so,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Two-way audio with a bunch of channels">
|
||||
same => n,Broadcast(wbso,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<para>Note that in the last example above, this is NOT the same as a conference bridge.
|
||||
The specified channels are not audible to each other, only to the channel running the
|
||||
Broadcast application. The two-way audio is only between the broadcasting channel and
|
||||
each of the specified channels, individually.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ChanSpy</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static const char app_broadcast[] = "Broadcast";
|
||||
|
||||
enum {
|
||||
OPTION_READONLY = (1 << 0), /* Don't mix the two channels */
|
||||
OPTION_BARGE = (1 << 1), /* Barge mode (whisper to both channels) */
|
||||
OPTION_LONG_QUEUE = (1 << 2), /* Allow usage of a long queue to store audio frames. */
|
||||
OPTION_WHISPER = (1 << 3),
|
||||
OPTION_SPY = (1 << 4),
|
||||
OPTION_REVERSE_FEED = (1 << 5),
|
||||
OPTION_ANSWER_WARN = (1 << 6), /* Internal flag, not set by user */
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(spy_opts, {
|
||||
AST_APP_OPTION('b', OPTION_BARGE),
|
||||
AST_APP_OPTION('l', OPTION_LONG_QUEUE),
|
||||
AST_APP_OPTION('o', OPTION_READONLY),
|
||||
AST_APP_OPTION('r', OPTION_REVERSE_FEED),
|
||||
AST_APP_OPTION('s', OPTION_SPY),
|
||||
AST_APP_OPTION('w', OPTION_WHISPER),
|
||||
});
|
||||
|
||||
struct multi_autochan {
|
||||
char *name;
|
||||
struct ast_autochan *autochan;
|
||||
struct ast_autochan *bridge_autochan;
|
||||
struct ast_audiohook whisper_audiohook;
|
||||
struct ast_audiohook bridge_whisper_audiohook;
|
||||
struct ast_audiohook spy_audiohook;
|
||||
unsigned int connected:1;
|
||||
unsigned int bridge_connected:1;
|
||||
unsigned int spying:1;
|
||||
AST_LIST_ENTRY(multi_autochan) entry; /*!< Next record */
|
||||
};
|
||||
|
||||
AST_RWLIST_HEAD(multi_autochan_list, multi_autochan);
|
||||
|
||||
struct multi_spy {
|
||||
struct multi_autochan_list *chanlist;
|
||||
unsigned int readonly:1;
|
||||
};
|
||||
|
||||
static void *spy_alloc(struct ast_channel *chan, void *data)
|
||||
{
|
||||
return data; /* just store the data pointer in the channel structure */
|
||||
}
|
||||
|
||||
static void spy_release(struct ast_channel *chan, void *data)
|
||||
{
|
||||
return; /* nothing to do */
|
||||
}
|
||||
|
||||
static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
|
||||
{
|
||||
struct multi_spy *multispy = data;
|
||||
struct multi_autochan_list *chanlist = multispy->chanlist;
|
||||
struct multi_autochan *mac;
|
||||
struct ast_frame *f;
|
||||
short *data1, *data2;
|
||||
int res, i;
|
||||
|
||||
/* All the frames we get are slin, so they will all have the same number of samples. */
|
||||
static const int num_samples = 160;
|
||||
short combine_buf[num_samples];
|
||||
struct ast_frame wf = {
|
||||
.frametype = AST_FRAME_VOICE,
|
||||
.offset = 0,
|
||||
.subclass.format = ast_format_slin,
|
||||
.datalen = num_samples * 2,
|
||||
.samples = num_samples,
|
||||
.src = __FUNCTION__,
|
||||
};
|
||||
|
||||
memset(&combine_buf, 0, sizeof(combine_buf));
|
||||
wf.data.ptr = combine_buf;
|
||||
|
||||
AST_RWLIST_WRLOCK(chanlist);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(chanlist, mac, entry) {
|
||||
ast_audiohook_lock(&mac->spy_audiohook);
|
||||
if (mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_audiohook_unlock(&mac->spy_audiohook); /* Channel is already gone more than likely, the broadcasting channel will clean this up. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (multispy->readonly) { /* Option 'o' was set, so don't mix channel audio */
|
||||
f = ast_audiohook_read_frame(&mac->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, ast_format_slin);
|
||||
} else {
|
||||
f = ast_audiohook_read_frame(&mac->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);
|
||||
}
|
||||
ast_audiohook_unlock(&mac->spy_audiohook);
|
||||
|
||||
if (!f) {
|
||||
continue; /* No frame? No problem. */
|
||||
}
|
||||
|
||||
/* Mix the samples. */
|
||||
for (i = 0, data1 = combine_buf, data2 = f->data.ptr; i < num_samples; i++, data1++, data2++) {
|
||||
ast_slinear_saturated_add(data1, data2);
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(chanlist);
|
||||
|
||||
res = ast_write(chan, &wf);
|
||||
ast_frfree(&wf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct ast_generator spygen = {
|
||||
.alloc = spy_alloc,
|
||||
.release = spy_release,
|
||||
.generate = spy_generate,
|
||||
};
|
||||
|
||||
static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook, struct ast_flags *flags)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_autochan_channel_lock(autochan);
|
||||
ast_debug(1, "Attaching spy channel %s to %s\n", spychan_name, ast_channel_name(autochan->chan));
|
||||
|
||||
if (ast_test_flag(flags, OPTION_READONLY)) {
|
||||
ast_set_flag(audiohook, AST_AUDIOHOOK_MUTE_WRITE);
|
||||
} else {
|
||||
ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
|
||||
}
|
||||
if (ast_test_flag(flags, OPTION_LONG_QUEUE)) {
|
||||
ast_debug(2, "Using a long queue to store audio frames in spy audiohook\n");
|
||||
} else {
|
||||
ast_set_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE);
|
||||
}
|
||||
res = ast_audiohook_attach(autochan->chan, audiohook);
|
||||
ast_autochan_channel_unlock(autochan);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int attach_barge(struct ast_autochan *spyee_autochan, struct ast_autochan **spyee_bridge_autochan,
|
||||
struct ast_audiohook *bridge_whisper_audiohook, const char *spyer_name, const char *name, struct ast_flags *flags)
|
||||
{
|
||||
int retval = 0;
|
||||
struct ast_autochan *internal_bridge_autochan;
|
||||
struct ast_channel *spyee_chan;
|
||||
RAII_VAR(struct ast_channel *, bridged, NULL, ast_channel_cleanup);
|
||||
|
||||
ast_autochan_channel_lock(spyee_autochan);
|
||||
spyee_chan = ast_channel_ref(spyee_autochan->chan);
|
||||
ast_autochan_channel_unlock(spyee_autochan);
|
||||
|
||||
/* Note that ast_channel_bridge_peer only returns non-NULL for 2-party bridges, not n-party bridges (e.g. ConfBridge) */
|
||||
bridged = ast_channel_bridge_peer(spyee_chan);
|
||||
ast_channel_unref(spyee_chan);
|
||||
if (!bridged) {
|
||||
ast_debug(9, "Channel %s is not yet bridged, unable to setup barge\n", ast_channel_name(spyee_chan));
|
||||
/* If we're bridged, but it's not a 2-party bridge, then we probably should have used OPTION_REVERSE_FEED. */
|
||||
if (ast_test_flag(flags, OPTION_ANSWER_WARN) && ast_channel_is_bridged(spyee_chan)) {
|
||||
ast_clear_flag(flags, OPTION_ANSWER_WARN); /* Don't warn more than once. */
|
||||
ast_log(LOG_WARNING, "Barge failed: channel is bridged, but not to a 2-party bridge. Use the 'r' option.\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_audiohook_init(bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Broadcast", 0);
|
||||
internal_bridge_autochan = ast_autochan_setup(bridged);
|
||||
if (!internal_bridge_autochan) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (start_spying(internal_bridge_autochan, spyer_name, bridge_whisper_audiohook, flags)) {
|
||||
ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee '%s'. Barge mode disabled.\n", name);
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
*spyee_bridge_autochan = internal_bridge_autochan;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void multi_autochan_free(struct multi_autochan *mac)
|
||||
{
|
||||
if (mac->connected) {
|
||||
if (mac->whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_debug(2, "Whisper audiohook no longer running\n");
|
||||
}
|
||||
ast_audiohook_lock(&mac->whisper_audiohook);
|
||||
ast_audiohook_detach(&mac->whisper_audiohook);
|
||||
ast_audiohook_unlock(&mac->whisper_audiohook);
|
||||
ast_audiohook_destroy(&mac->whisper_audiohook);
|
||||
}
|
||||
if (mac->bridge_connected) {
|
||||
if (mac->bridge_whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_debug(2, "Whisper (bridged) audiohook no longer running\n");
|
||||
}
|
||||
ast_audiohook_lock(&mac->bridge_whisper_audiohook);
|
||||
ast_audiohook_detach(&mac->bridge_whisper_audiohook);
|
||||
ast_audiohook_unlock(&mac->bridge_whisper_audiohook);
|
||||
ast_audiohook_destroy(&mac->bridge_whisper_audiohook);
|
||||
}
|
||||
if (mac->spying) {
|
||||
if (mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_debug(2, "Spy audiohook no longer running\n");
|
||||
}
|
||||
ast_audiohook_lock(&mac->spy_audiohook);
|
||||
ast_audiohook_detach(&mac->spy_audiohook);
|
||||
ast_audiohook_unlock(&mac->spy_audiohook);
|
||||
ast_audiohook_destroy(&mac->spy_audiohook);
|
||||
}
|
||||
if (mac->name) {
|
||||
int total = mac->connected + mac->bridge_connected + mac->spying;
|
||||
ast_debug(1, "Removing channel %s from target list (%d hook%s)\n", mac->name, total, ESS(total));
|
||||
ast_free(mac->name);
|
||||
}
|
||||
if (mac->autochan) {
|
||||
ast_autochan_destroy(mac->autochan);
|
||||
}
|
||||
if (mac->bridge_autochan) {
|
||||
ast_autochan_destroy(mac->bridge_autochan);
|
||||
}
|
||||
ast_free(mac);
|
||||
}
|
||||
|
||||
static int do_broadcast(struct ast_channel *chan, struct ast_flags *flags, const char *channels)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_frame *f;
|
||||
struct ast_silence_generator *silgen = NULL;
|
||||
struct multi_spy multispy;
|
||||
struct multi_autochan_list chanlist;
|
||||
struct multi_autochan *mac;
|
||||
int numchans = 0;
|
||||
int readonly = ast_test_flag(flags, OPTION_READONLY) ? 1 : 0;
|
||||
char *next, *chansdup = ast_strdupa(channels);
|
||||
|
||||
AST_RWLIST_HEAD_INIT(&chanlist);
|
||||
ast_channel_set_flag(chan, AST_FLAG_SPYING);
|
||||
|
||||
ast_set_flag(flags, OPTION_ANSWER_WARN); /* Initialize answer warn to 1 */
|
||||
|
||||
/* Hey, look ma, no list lock needed! Sometimes, it's nice to not have to share... */
|
||||
|
||||
/* Build a list of targets */
|
||||
while ((next = strsep(&chansdup, ","))) {
|
||||
struct ast_channel *ochan;
|
||||
if (ast_strlen_zero(next)) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(next, ast_channel_name(chan))) {
|
||||
ast_log(LOG_WARNING, "Refusing to broadcast to ourself: %s\n", next);
|
||||
continue;
|
||||
}
|
||||
ochan = ast_channel_get_by_name(next);
|
||||
if (!ochan) {
|
||||
ast_log(LOG_WARNING, "No such channel: %s\n", next);
|
||||
continue;
|
||||
}
|
||||
/* Append to end of list. */
|
||||
if (!(mac = ast_calloc(1, sizeof(*mac)))) {
|
||||
ast_log(LOG_WARNING, "Multi autochan allocation failure\n");
|
||||
continue;
|
||||
}
|
||||
mac->name = ast_strdup(next);
|
||||
mac->autochan = ast_autochan_setup(ochan);
|
||||
if (!mac->name || !mac->autochan) {
|
||||
multi_autochan_free(mac);
|
||||
continue;
|
||||
}
|
||||
if (ast_test_flag(flags, OPTION_WHISPER)) {
|
||||
mac->connected = 1;
|
||||
ast_audiohook_init(&mac->whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Broadcast", 0);
|
||||
/* Inject audio from our channel to this target. */
|
||||
if (start_spying(mac->autochan, next, &mac->whisper_audiohook, flags)) {
|
||||
ast_log(LOG_WARNING, "Unable to attach whisper audiohook to %s\n", next);
|
||||
multi_autochan_free(mac);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (ast_test_flag(flags, OPTION_SPY)) {
|
||||
mac->spying = 1;
|
||||
ast_audiohook_init(&mac->spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "Broadcast", 0);
|
||||
if (start_spying(mac->autochan, next, &mac->spy_audiohook, flags)) {
|
||||
ast_log(LOG_WARNING, "Unable to attach spy audiohook to %s\n", next);
|
||||
multi_autochan_free(mac);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
AST_RWLIST_INSERT_TAIL(&chanlist, mac, entry);
|
||||
numchans++;
|
||||
ochan = ast_channel_unref(ochan);
|
||||
}
|
||||
|
||||
ast_verb(4, "Broadcasting to %d channel%s on %s\n", numchans, ESS(numchans), ast_channel_name(chan));
|
||||
ast_debug(1, "Broadcasting: (TX->1) whisper=%d, (TX->2) barge=%d, (RX<-%d) spy=%d (%s)\n",
|
||||
ast_test_flag(flags, OPTION_WHISPER) ? 1 : 0,
|
||||
ast_test_flag(flags, OPTION_BARGE) ? 1 : 0,
|
||||
readonly ? 1 : 2,
|
||||
ast_test_flag(flags, OPTION_SPY) ? 1 : 0,
|
||||
readonly ? "single" : "both");
|
||||
|
||||
if (ast_test_flag(flags, OPTION_SPY)) {
|
||||
multispy.chanlist = &chanlist;
|
||||
multispy.readonly = readonly;
|
||||
ast_activate_generator(chan, &spygen, &multispy);
|
||||
} else {
|
||||
/* We're not expecting to read any audio, just broadcast audio to a bunch of other channels. */
|
||||
silgen = ast_channel_start_silence_generator(chan);
|
||||
}
|
||||
|
||||
while (numchans && ast_waitfor(chan, -1) > 0) {
|
||||
int fres = 0;
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
ast_debug(1, "Channel %s must have hung up\n", ast_channel_name(chan));
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
if (f->frametype != AST_FRAME_VOICE) { /* Ignore any non-voice frames */
|
||||
ast_frfree(f);
|
||||
continue;
|
||||
}
|
||||
/* Write the frame to all our targets. */
|
||||
AST_RWLIST_WRLOCK(&chanlist);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chanlist, mac, entry) {
|
||||
/* Note that if no media is received, execution is suspended, but assuming continuous or
|
||||
* or frequent audio on the broadcasting channel, we'll quickly enough detect hung up targets.
|
||||
* This isn't really an issue, just something that might be confusing at first, but this is
|
||||
* due to the limitation with audiohooks of using the channel for timing. */
|
||||
if ((ast_test_flag(flags, OPTION_WHISPER) && mac->whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
|
||||
|| (ast_test_flag(flags, OPTION_SPY) && mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
|
||||
|| (mac->bridge_connected && ast_test_flag(flags, OPTION_BARGE) && mac->bridge_whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)) {
|
||||
/* Even if we're spying only and not actually broadcasting audio, we need to detect channel hangup. */
|
||||
AST_RWLIST_REMOVE_CURRENT(entry);
|
||||
ast_debug(2, "Looks like %s has hung up\n", mac->name);
|
||||
multi_autochan_free(mac);
|
||||
numchans--;
|
||||
ast_debug(2, "%d channel%s remaining in broadcast on %s\n", numchans, ESS(numchans), ast_channel_name(chan));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_WHISPER)) {
|
||||
ast_audiohook_lock(&mac->whisper_audiohook);
|
||||
fres |= ast_audiohook_write_frame(&mac->whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
|
||||
ast_audiohook_unlock(&mac->whisper_audiohook);
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_BARGE)) {
|
||||
/* This hook lets us inject audio into the channel that the spyee is currently
|
||||
* bridged with. If the spyee isn't bridged with anything yet, nothing will
|
||||
* be attached and we'll need to continue attempting to attach the barge
|
||||
* audio hook.
|
||||
* The exception to this is if we are emulating barge by doing it "directly",
|
||||
* that is injecting the frames onto this channel's read queue, rather than
|
||||
* its bridged peer's write queue, then skip this. We only do one or the other. */
|
||||
if (!ast_test_flag(flags, OPTION_REVERSE_FEED) && !mac->bridge_connected && !attach_barge(mac->autochan, &mac->bridge_autochan,
|
||||
&mac->bridge_whisper_audiohook, ast_channel_name(chan), mac->name, flags)) {
|
||||
ast_debug(2, "Attached barge channel for %s\n", mac->name);
|
||||
mac->bridge_connected = 1;
|
||||
}
|
||||
|
||||
if (mac->bridge_connected) {
|
||||
ast_audiohook_lock(&mac->bridge_whisper_audiohook);
|
||||
fres |= ast_audiohook_write_frame(&mac->bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
|
||||
ast_audiohook_unlock(&mac->bridge_whisper_audiohook);
|
||||
} else if (ast_test_flag(flags, OPTION_REVERSE_FEED)) {
|
||||
/* So, this is really clever...
|
||||
* If we're connected to an n-party bridge instead of a 2-party bridge,
|
||||
* attach_barge will ALWAYS fail because we're connected to a bridge, not
|
||||
* a single peer channel.
|
||||
* Recall that the objective is for injected audio to be audible to both
|
||||
* sides of the channel. So really, the typical way of doing this by
|
||||
* directly injecting frames separately onto both channels is kind of
|
||||
* bizarre to begin with, when you think about it.
|
||||
*
|
||||
* In other words, this is how ChanSpy and this module by default work:
|
||||
* We have audio F to inject onto channels A and B, which are <= bridged =>:
|
||||
* READ <- A -> WRITE <==> READ <- B -> WRITE
|
||||
* F --^ F --^
|
||||
*
|
||||
* So that makes the same audio audible to both channels A and B, but
|
||||
* in kind of a roundabout way. What if the bridged peer changes at
|
||||
* some point, for example?
|
||||
*
|
||||
* While that method works for 2-party bridges, it doesn't work at all
|
||||
* for an n-party bridge, so we do the thing that seems obvious to begin with:
|
||||
* dump the frames onto THIS channel's read queue, and the channels will
|
||||
* make their way into the bridge like any other audio from this channel,
|
||||
* and everything just works perfectly, no matter what kind of bridging
|
||||
* scenario is being used. At that point, we don't even care if we're
|
||||
* bridged or not, and really, why should we?
|
||||
*
|
||||
* In other words, we do this:
|
||||
* READ <- A -> WRITE <==> READ <- B -> WRITE
|
||||
* F --^ F --^
|
||||
*/
|
||||
ast_audiohook_lock(&mac->whisper_audiohook);
|
||||
fres |= ast_audiohook_write_frame(&mac->whisper_audiohook, AST_AUDIOHOOK_DIRECTION_READ, f);
|
||||
ast_audiohook_unlock(&mac->whisper_audiohook);
|
||||
}
|
||||
}
|
||||
if (fres) {
|
||||
ast_log(LOG_WARNING, "Failed to write to audiohook for %s\n", mac->name);
|
||||
fres = 0;
|
||||
}
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&chanlist);
|
||||
ast_frfree(f);
|
||||
}
|
||||
|
||||
if (!numchans) {
|
||||
ast_debug(1, "Exiting due to all target channels having left the broadcast\n");
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_SPY)) {
|
||||
ast_deactivate_generator(chan);
|
||||
} else {
|
||||
ast_channel_stop_silence_generator(chan, silgen);
|
||||
}
|
||||
|
||||
/* Cleanup any remaining targets */
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chanlist, mac, entry) {
|
||||
AST_RWLIST_REMOVE_CURRENT(entry);
|
||||
multi_autochan_free(mac);
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
|
||||
ast_channel_clear_flag(chan, AST_FLAG_SPYING);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int broadcast_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
struct ast_flags flags;
|
||||
struct ast_format *write_format;
|
||||
int res = -1;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(options);
|
||||
AST_APP_ARG(channels); /* Channel list last, so we can have multiple */
|
||||
);
|
||||
char *parse = NULL;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Broadcast requires at least one channel\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
parse = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (ast_strlen_zero(args.channels)) {
|
||||
ast_log(LOG_WARNING, "Must specify at least one channel for broadcast\n");
|
||||
return -1;
|
||||
}
|
||||
if (args.options) {
|
||||
ast_app_parse_options(spy_opts, &flags, NULL, args.options);
|
||||
} else {
|
||||
ast_clear_flag(&flags, AST_FLAGS_ALL);
|
||||
}
|
||||
|
||||
if (!ast_test_flag(&flags, OPTION_BARGE) && !ast_test_flag(&flags, OPTION_SPY) && !ast_test_flag(&flags, OPTION_WHISPER)) {
|
||||
ast_log(LOG_WARNING, "At least one of the b, s, or w option must be specified (provided options have no effect)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_format = ao2_bump(ast_channel_writeformat(chan));
|
||||
if (ast_set_write_format(chan, ast_format_slin) < 0) {
|
||||
ast_log(LOG_ERROR, "Failed to set write format to slin.\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
res = do_broadcast(chan, &flags, args.channels);
|
||||
|
||||
/* Restore previous write format */
|
||||
if (ast_set_write_format(chan, write_format)) {
|
||||
ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", ast_channel_name(chan));
|
||||
}
|
||||
|
||||
cleanup:
|
||||
ao2_ref(write_format, -1);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app_broadcast);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application_xml(app_broadcast, broadcast_exec);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel Audio Broadcasting");
|
|
@ -117,6 +117,7 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
|||
struct ast_str *tmp_availcause = ast_str_alloca(2048);
|
||||
struct ast_channel *tempchan;
|
||||
struct ast_custom_function *cdr_prop_func = ast_custom_function_find("CDR_PROP");
|
||||
struct ast_format_cap *caps = NULL;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(reqchans);
|
||||
AST_APP_ARG(options);
|
||||
|
@ -126,6 +127,10 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
|||
|
||||
AST_STANDARD_APP_ARGS(args, info);
|
||||
|
||||
ao2_lock(chan);
|
||||
caps = ao2_bump(ast_channel_nativeformats(chan));
|
||||
ao2_unlock(chan);
|
||||
|
||||
if (args.options) {
|
||||
if (strchr(args.options, 'a')) {
|
||||
option_all_avail = 1;
|
||||
|
@ -174,10 +179,11 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
|||
snprintf(trychan, sizeof(trychan), "%s/%s", tech, number);
|
||||
status = inuse = ast_device_state(trychan);
|
||||
}
|
||||
ast_str_append(&tmp_availstat, 0, "%s%d",
|
||||
ast_str_strlen(tmp_availstat) ? "&" : "", status);
|
||||
ast_str_append(&tmp_availstat, 0, "%s%d", ast_str_strlen(tmp_availstat) ? "&" : "", status);
|
||||
|
||||
if ((inuse <= (int) AST_DEVICE_NOT_INUSE)
|
||||
&& (tempchan = ast_request(tech, 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_strlen(tmp_availchan) ? "&" : "", ast_channel_name(tempchan));
|
||||
|
||||
|
@ -199,8 +205,11 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ao2_cleanup(caps);
|
||||
|
||||
pbx_builtin_setvar_helper(chan, "AVAILCHAN", ast_str_buffer(tmp_availchan));
|
||||
/* Store the originally used channel too */
|
||||
pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", ast_str_buffer(tmp_availorig));
|
||||
|
|
|
@ -245,6 +245,11 @@
|
|||
</enum>
|
||||
</enumlist>
|
||||
</option>
|
||||
<option name="D">
|
||||
<para>Interleave the audio coming from the channel and the audio coming to the channel in
|
||||
the output audio as a dual channel stream, rather than mix it. Does nothing if 'o'
|
||||
is also set.</para>
|
||||
</option>
|
||||
<option name="e">
|
||||
<argument name="ext" required="true" />
|
||||
<para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
|
||||
|
@ -393,6 +398,7 @@ enum {
|
|||
OPTION_EXITONHANGUP = (1 << 18), /* Hang up when the spied-on channel hangs up. */
|
||||
OPTION_UNIQUEID = (1 << 19), /* The chanprefix is a channel uniqueid or fully specified channel name. */
|
||||
OPTION_LONG_QUEUE = (1 << 20), /* Allow usage of a long queue to store audio frames. */
|
||||
OPTION_INTERLEAVED = (1 << 21), /* Interleave the Read and Write frames in the output frame. */
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -411,6 +417,7 @@ AST_APP_OPTIONS(spy_opts, {
|
|||
AST_APP_OPTION('B', OPTION_BARGE),
|
||||
AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE),
|
||||
AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
|
||||
AST_APP_OPTION('D', OPTION_INTERLEAVED),
|
||||
AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
|
||||
AST_APP_OPTION('E', OPTION_EXITONHANGUP),
|
||||
AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
|
||||
|
@ -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)) {
|
||||
/* Option 'o' was set, so don't mix channel audio */
|
||||
f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, ast_format_slin);
|
||||
} else if (ast_test_flag(&csth->flags, OPTION_INTERLEAVED)) {
|
||||
/* Option 'D' was set, so mix the spy frame as an interleaved dual channel frame. */
|
||||
int i;
|
||||
struct ast_frame *fr_read = NULL;
|
||||
struct ast_frame *fr_write = NULL;
|
||||
short read_buf[samples];
|
||||
short write_buf[samples];
|
||||
short stereo_buf[samples * 2];
|
||||
struct ast_frame stereo_frame = {
|
||||
.frametype = AST_FRAME_VOICE,
|
||||
.datalen = sizeof(stereo_buf),
|
||||
.samples = samples,
|
||||
};
|
||||
|
||||
f = ast_audiohook_read_frame_all(&csth->spy_audiohook, samples, ast_format_slin, &fr_read, &fr_write);
|
||||
if (f) {
|
||||
ast_frame_free(f, 0);
|
||||
f = NULL;
|
||||
}
|
||||
|
||||
if (fr_read) {
|
||||
memcpy(read_buf, fr_read->data.ptr, sizeof(read_buf));
|
||||
} else {
|
||||
/* silent out the output frame if we can't read the input */
|
||||
memset(read_buf, 0, sizeof(read_buf));
|
||||
}
|
||||
|
||||
if (fr_write) {
|
||||
memcpy(write_buf, fr_write->data.ptr, sizeof(write_buf));
|
||||
} else {
|
||||
memset(write_buf, 0, sizeof(write_buf));
|
||||
}
|
||||
|
||||
for (i = 0; i < samples; i++) {
|
||||
stereo_buf[i*2] = read_buf[i];
|
||||
stereo_buf[i*2+1] = write_buf[i];
|
||||
}
|
||||
|
||||
stereo_frame.data.ptr = stereo_buf;
|
||||
stereo_frame.subclass.format = ast_format_cache_get_slin_by_rate(samples);
|
||||
|
||||
f = ast_frdup(&stereo_frame);
|
||||
|
||||
if (fr_read) {
|
||||
ast_frame_free(fr_read, 0);
|
||||
}
|
||||
if (fr_write) {
|
||||
ast_frame_free(fr_write, 0);
|
||||
}
|
||||
|
||||
} else {
|
||||
f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);
|
||||
}
|
||||
|
|
|
@ -393,6 +393,37 @@
|
|||
ConfbridgeListRoomsComplete.</para>
|
||||
</description>
|
||||
</manager>
|
||||
<managerEvent language="en_US" name="ConfbridgeListRooms">
|
||||
<managerEventInstance class="EVENT_FLAG_REPORTING">
|
||||
<synopsis>Raised as part of the ConfbridgeListRooms action response list.</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Conference">
|
||||
<para>The name of the Confbridge conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Parties">
|
||||
<para>Number of users in the conference.</para>
|
||||
<para>This includes both active and waiting users.</para>
|
||||
</parameter>
|
||||
<parameter name="Marked">
|
||||
<para>Number of marked users in the conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Locked">
|
||||
<para>Is the conference locked?</para>
|
||||
<enumlist>
|
||||
<enum name="Yes"/>
|
||||
<enum name="No"/>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="Muted">
|
||||
<para>Is the conference muted?</para>
|
||||
<enumlist>
|
||||
<enum name="Yes"/>
|
||||
<enum name="No"/>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<manager name="ConfbridgeMute" language="en_US">
|
||||
<synopsis>
|
||||
Mute a Confbridge user.
|
||||
|
@ -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 = 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, "")) {
|
||||
ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
|
||||
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 = 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))) {
|
||||
ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
|
||||
return -1;
|
||||
|
@ -3991,6 +4022,7 @@ static int action_confbridgelist_item(struct mansession *s, const char *id_text,
|
|||
"MarkedUser: %s\r\n"
|
||||
"WaitMarked: %s\r\n"
|
||||
"EndMarked: %s\r\n"
|
||||
"EndMarkedAny: %s\r\n"
|
||||
"Waiting: %s\r\n"
|
||||
"Muted: %s\r\n"
|
||||
"Talking: %s\r\n"
|
||||
|
@ -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_WAITMARKED)),
|
||||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)),
|
||||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKEDANY)),
|
||||
AST_YESNO(waiting),
|
||||
AST_YESNO(user->muted),
|
||||
AST_YESNO(user->talking),
|
||||
|
|
134
apps/app_dial.c
134
apps/app_dial.c
|
@ -88,9 +88,12 @@
|
|||
</argument>
|
||||
<xi:include xpointer="xpointer(/docs/info[@name='Dial_Resource'])" />
|
||||
</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>If not specified, this defaults to 136 years.</para>
|
||||
<para>If a second argument is specified, this controls the number of seconds we attempt to dial the specified devices
|
||||
without receiving early media or ringing. If neither progress, ringing, nor voice frames have been received when this
|
||||
timeout expires, the call will be treated as a CHANUNAVAIL. This can be used to skip destinations that may not be responsive.</para>
|
||||
</parameter>
|
||||
<parameter name="options" required="false">
|
||||
<optionlist>
|
||||
|
@ -242,6 +245,10 @@
|
|||
<para>Asterisk will ignore any connected line update requests or any redirecting party
|
||||
update requests it may receive on this dial attempt.</para>
|
||||
</option>
|
||||
<option name="j">
|
||||
<para>Use the initial stream topology of the caller for outgoing channels, even if the caller topology has changed.</para>
|
||||
<para>NOTE: For this option to work, it has to be present in all invocations of Dial that the caller channel goes through.</para>
|
||||
</option>
|
||||
<option name="k">
|
||||
<para>Allow the called party to enable parking of the call by sending
|
||||
the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
|
||||
|
@ -750,6 +757,7 @@ enum {
|
|||
#define OPT_RING_WITH_EARLY_MEDIA (1LLU << 43)
|
||||
#define OPT_HANGUPCAUSE (1LLU << 44)
|
||||
#define OPT_HEARPULSING (1LLU << 45)
|
||||
#define OPT_TOPOLOGY_PRESERVE (1LLU << 46)
|
||||
|
||||
enum {
|
||||
OPT_ARG_ANNOUNCE = 0,
|
||||
|
@ -795,6 +803,7 @@ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
|
|||
AST_APP_OPTION('H', OPT_CALLER_HANGUP),
|
||||
AST_APP_OPTION('i', OPT_IGNORE_FORWARDING),
|
||||
AST_APP_OPTION('I', OPT_IGNORE_CONNECTEDLINE),
|
||||
AST_APP_OPTION('j', OPT_TOPOLOGY_PRESERVE),
|
||||
AST_APP_OPTION('k', OPT_CALLEE_PARK),
|
||||
AST_APP_OPTION('K', OPT_CALLER_PARK),
|
||||
AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
|
||||
|
@ -855,6 +864,16 @@ struct chanlist {
|
|||
|
||||
AST_LIST_HEAD_NOLOCK(dial_head, chanlist);
|
||||
|
||||
static void topology_ds_destroy(void *data) {
|
||||
struct ast_stream_topology *top = data;
|
||||
ast_stream_topology_free(top);
|
||||
}
|
||||
|
||||
static const struct ast_datastore_info topology_ds_info = {
|
||||
.type = "app_dial_topology_preserve",
|
||||
.destroy = topology_ds_destroy,
|
||||
};
|
||||
|
||||
static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str **featurecode);
|
||||
|
||||
static void chanlist_free(struct chanlist *outgoing)
|
||||
|
@ -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,
|
||||
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[],
|
||||
struct privacy_args *pa,
|
||||
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;
|
||||
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 chanlist *outgoing = AST_LIST_FIRST(out_chans);
|
||||
/* 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
|
||||
* will just fail if it gets that far.
|
||||
*/
|
||||
*to = -1;
|
||||
*to_answer = -1;
|
||||
strcpy(pa->status, "CONGESTION");
|
||||
ast_channel_publish_dial(in, outgoing->chan, NULL, pa->status);
|
||||
SCOPE_EXIT_RTN_VALUE(NULL, "%s: can't be made compat with %s\n",
|
||||
|
@ -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);
|
||||
|
||||
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;
|
||||
int pos = 0; /* how many channels do we handle */
|
||||
int numlines = prestart;
|
||||
|
@ -1321,14 +1342,15 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
} else {
|
||||
ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
|
||||
}
|
||||
*to = 0;
|
||||
*to_answer = 0;
|
||||
if (is_cc_recall) {
|
||||
ast_cc_failed(cc_recall_core_id, "Everyone is busy/congested for the recall. How sad");
|
||||
}
|
||||
SCOPE_EXIT_RTN_VALUE(NULL, "%s: No outgoing channels available\n", ast_channel_name(in));
|
||||
}
|
||||
winner = ast_waitfor_n(watchers, pos, to);
|
||||
winner = ast_waitfor_n(watchers, pos, to_answer);
|
||||
AST_LIST_TRAVERSE(out_chans, o, node) {
|
||||
int res = 0;
|
||||
struct ast_frame *f;
|
||||
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);
|
||||
}
|
||||
|
||||
do_forward(o, &num, peerflags, single, caller_entertained, &orig,
|
||||
do_forward(o, &num, peerflags, single, caller_entertained, &orig_answer_to,
|
||||
forced_clid, stored_clid);
|
||||
|
||||
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.
|
||||
*/
|
||||
++num_ringing;
|
||||
*to_progress = -1;
|
||||
progress_to_dup = -1;
|
||||
if (ignore_cc || cc_frame_received || num_ringing == numlines) {
|
||||
ast_verb(3, "%s is ringing\n", ast_channel_name(c));
|
||||
/* Setup early media if appropriate */
|
||||
|
@ -1585,6 +1609,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
ast_indicate(in, AST_CONTROL_PROGRESS);
|
||||
}
|
||||
}
|
||||
*to_progress = -1;
|
||||
progress_to_dup = -1;
|
||||
if (!sent_progress) {
|
||||
struct timeval now, then;
|
||||
int64_t diff;
|
||||
|
@ -1607,7 +1633,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
"Sending MF '%s' to %s as result of "
|
||||
"receiving a PROGRESS message.\n",
|
||||
mf_progress, hearpulsing ? "parties" : "called party");
|
||||
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);
|
||||
}
|
||||
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 "
|
||||
"receiving a PROGRESS message.\n",
|
||||
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);
|
||||
}
|
||||
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 "
|
||||
"receiving a PROGRESS message.\n",
|
||||
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");
|
||||
|
@ -1637,7 +1667,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
"Sending MF '%s' to %s as result of "
|
||||
"receiving a WINK message.\n",
|
||||
mf_wink, (hearpulsing ? "parties" : "called party"));
|
||||
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);
|
||||
}
|
||||
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 "
|
||||
"receiving a WINK message.\n",
|
||||
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);
|
||||
}
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "Called channel %s hung up post-wink before all digits could be sent\n", ast_channel_name(c));
|
||||
goto wait_over;
|
||||
}
|
||||
}
|
||||
ast_indicate(in, AST_CONTROL_WINK);
|
||||
break;
|
||||
|
@ -1762,9 +1796,13 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
case AST_FRAME_VIDEO:
|
||||
case AST_FRAME_VOICE:
|
||||
case AST_FRAME_IMAGE:
|
||||
case AST_FRAME_DTMF_BEGIN:
|
||||
case AST_FRAME_DTMF_END:
|
||||
if (caller_entertained) {
|
||||
break;
|
||||
}
|
||||
*to_progress = -1;
|
||||
progress_to_dup = -1;
|
||||
/* Fall through */
|
||||
case AST_FRAME_TEXT:
|
||||
if (single && ast_write(in, f)) {
|
||||
|
@ -1793,7 +1831,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
#endif
|
||||
if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
|
||||
/* Got hung up */
|
||||
*to = -1;
|
||||
*to_answer = -1;
|
||||
strcpy(pa->status, "CANCEL");
|
||||
pa->canceled = 1;
|
||||
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");
|
||||
if (onedigit_goto(in, context, (char) f->subclass.integer, 1)) {
|
||||
ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
|
||||
*to = 0;
|
||||
*to_answer = 0;
|
||||
*result = f->subclass.integer;
|
||||
strcpy(pa->status, "CANCEL");
|
||||
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) &&
|
||||
detect_disconnect(in, f->subclass.integer, &featurecode)) {
|
||||
ast_verb(3, "User requested call disconnect.\n");
|
||||
*to = 0;
|
||||
*to_answer = 0;
|
||||
strcpy(pa->status, "CANCEL");
|
||||
pa->canceled = 1;
|
||||
publish_dial_end_event(in, out_chans, NULL, pa->status);
|
||||
|
@ -1947,9 +1985,15 @@ skip_frame:;
|
|||
}
|
||||
}
|
||||
|
||||
if (!*to || ast_check_hangup(in)) {
|
||||
ast_verb(3, "Nobody picked up in %d ms\n", orig);
|
||||
wait_over:
|
||||
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");
|
||||
} 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) {
|
||||
|
@ -2321,7 +2365,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
struct chanlist *outgoing;
|
||||
struct chanlist *tmp;
|
||||
struct ast_channel *peer = NULL;
|
||||
int to; /* timeout */
|
||||
int to_answer, to_progress; /* timeouts */
|
||||
struct cause_args num = { chan, 0, 0, 0 };
|
||||
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;
|
||||
int max_forwards;
|
||||
struct ast_datastore *topology_ds = NULL;
|
||||
SCOPE_ENTER(1, "%s: Data: %s\n", ast_channel_name(chan), data);
|
||||
|
||||
/* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */
|
||||
|
@ -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));
|
||||
|
||||
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);
|
||||
|
||||
|
@ -2920,14 +2979,31 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
|
||||
if (ast_strlen_zero(args.timeout)) {
|
||||
to = -1;
|
||||
to_answer = -1;
|
||||
to_progress = -1;
|
||||
} else {
|
||||
to = atoi(args.timeout);
|
||||
if (to > 0)
|
||||
to *= 1000;
|
||||
else {
|
||||
ast_log(LOG_WARNING, "Invalid timeout specified: '%s'. Setting timeout to infinite\n", args.timeout);
|
||||
to = -1;
|
||||
char *anstimeout = strsep(&args.timeout, "^");
|
||||
if (!ast_strlen_zero(anstimeout)) {
|
||||
to_answer = atoi(anstimeout);
|
||||
if (to_answer > 0) {
|
||||
to_answer *= 1000;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Invalid answer timeout specified: '%s'. Setting timeout to infinite\n", args.timeout);
|
||||
to_answer = -1;
|
||||
}
|
||||
} else {
|
||||
to_answer = -1;
|
||||
}
|
||||
if (!ast_strlen_zero(args.timeout)) {
|
||||
to_progress = atoi(args.timeout);
|
||||
if (to_progress > 0) {
|
||||
to_progress *= 1000;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Invalid progress timeout specified: '%s'. Setting timeout to infinite\n", args.timeout);
|
||||
to_progress = -1;
|
||||
}
|
||||
} else {
|
||||
to_progress = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
(ast_test_flag64(&opts, OPT_HEARPULSING) ? 1 : 0),
|
||||
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 (result) {
|
||||
res = result;
|
||||
} else if (to) { /* Musta gotten hung up */
|
||||
} else if (to_answer) { /* Musta gotten hung up */
|
||||
res = -1;
|
||||
} else { /* Nobody answered, next please? */
|
||||
res = 0;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "asterisk/say.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/adsi.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="Directory" language="en_US">
|
||||
|
@ -103,23 +104,36 @@
|
|||
receiver to their ear while entering DTMF.</para>
|
||||
<argument name="n" required="true" />
|
||||
</option>
|
||||
<option name="c">
|
||||
<para>Load the specified config file instead of voicemail.conf</para>
|
||||
<argument name="filename" required="true" />
|
||||
</option>
|
||||
<option name="s">
|
||||
<para>Skip calling the extension, instead set it in the <variable>DIRECTORY_EXTEN</variable>
|
||||
channel variable.</para>
|
||||
</option>
|
||||
<option name="d">
|
||||
<para>Enable ADSI support for screen phone searching and retrieval
|
||||
of directory results.</para>
|
||||
<para>Additionally, the channel must be ADSI-enabled and you must
|
||||
have an ADSI-compatible (Type III) CPE for this to work.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
<note><para>Only one of the <replaceable>f</replaceable>, <replaceable>l</replaceable>, or <replaceable>b</replaceable>
|
||||
options may be specified. <emphasis>If more than one is specified</emphasis>, then Directory will act as
|
||||
if <replaceable>b</replaceable> was specified. The number
|
||||
of characters for the user to type defaults to <literal>3</literal>.</para></note>
|
||||
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This application will present the calling channel with a directory of extensions from which they can search
|
||||
by name. The list of names and corresponding extensions is retrieved from the
|
||||
voicemail configuration file, <filename>voicemail.conf</filename>.</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
|
||||
received and the extension to jump to exists:</para>
|
||||
<para><literal>0</literal> - Jump to the 'o' extension, if it exists.</para>
|
||||
<para><literal>*</literal> - Jump to the 'a' extension, if it exists.</para>
|
||||
<para>This application will set the following channel variable before completion:</para>
|
||||
<para>This application will set the following channel variables before completion:</para>
|
||||
<variablelist>
|
||||
<variable name="DIRECTORY_RESULT">
|
||||
<para>Reason Directory application exited.</para>
|
||||
|
@ -131,6 +145,10 @@
|
|||
<value name="USEREXIT">User exited with '#' during selection</value>
|
||||
<value name="FAILED">The application failed</value>
|
||||
</variable>
|
||||
<variable name="DIRECTORY_EXTEN">
|
||||
<para>If the skip calling option is set this will be set to the selected extension
|
||||
provided one is selected.</para>
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
</application>
|
||||
|
@ -153,6 +171,9 @@ enum {
|
|||
OPT_PAUSE = (1 << 5),
|
||||
OPT_NOANSWER = (1 << 6),
|
||||
OPT_ALIAS = (1 << 7),
|
||||
OPT_CONFIG_FILE = (1 << 8),
|
||||
OPT_SKIP = (1 << 9),
|
||||
OPT_ADSI = (1 << 10),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -160,8 +181,9 @@ enum {
|
|||
OPT_ARG_LASTNAME = 1,
|
||||
OPT_ARG_EITHER = 2,
|
||||
OPT_ARG_PAUSE = 3,
|
||||
OPT_ARG_FILENAME = 4,
|
||||
/* This *must* be the last value in this enum! */
|
||||
OPT_ARG_ARRAY_SIZE = 4,
|
||||
OPT_ARG_ARRAY_SIZE = 5,
|
||||
};
|
||||
|
||||
struct directory_item {
|
||||
|
@ -183,8 +205,74 @@ AST_APP_OPTIONS(directory_app_options, {
|
|||
AST_APP_OPTION('m', OPT_SELECTFROMMENU),
|
||||
AST_APP_OPTION('n', OPT_NOANSWER),
|
||||
AST_APP_OPTION('a', OPT_ALIAS),
|
||||
AST_APP_OPTION_ARG('c', OPT_CONFIG_FILE, OPT_ARG_FILENAME),
|
||||
AST_APP_OPTION('s', OPT_SKIP),
|
||||
AST_APP_OPTION('d', OPT_ADSI), /* (Would've used 'a', but that was taken already) */
|
||||
});
|
||||
|
||||
static int adsi_search_input(struct ast_channel *chan)
|
||||
{
|
||||
unsigned char buf[256];
|
||||
int bytes = 0;
|
||||
unsigned char keys[6];
|
||||
|
||||
memset(keys, 0, sizeof(keys));
|
||||
|
||||
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
|
||||
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
|
||||
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
|
||||
bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Query: ***", "");
|
||||
bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Search", "Search", "#", 1);
|
||||
bytes += ast_adsi_set_keys(buf + bytes, keys);
|
||||
bytes += ast_adsi_voice_mode(buf + bytes, 0);
|
||||
|
||||
ast_debug(3, "Sending ADSI search input screen on %s\n", ast_channel_name(chan));
|
||||
|
||||
return ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
|
||||
}
|
||||
|
||||
static int adsi_confirm_match(struct ast_channel *chan, int seq, int total, const char *exten, const char *name, int showexten)
|
||||
{
|
||||
unsigned char buf[4096];
|
||||
int alignments[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
|
||||
char *lines[5] = {NULL, NULL, NULL, NULL, NULL};
|
||||
int x, bytes = 0;
|
||||
unsigned char keys[8];
|
||||
char matchbuf[32];
|
||||
|
||||
snprintf(matchbuf, sizeof(matchbuf), "%d of %d", seq + 1, total); /* Make it 1-indexed for user consumption */
|
||||
|
||||
lines[0] = " "; /* Leave the first line empty so the following lines stand out more */
|
||||
lines[1] = matchbuf;
|
||||
lines[2] = (char*) name;
|
||||
|
||||
if (showexten) {
|
||||
/* If say extension option is set, show it for ADSI as well */
|
||||
lines[3] = (char*) exten;
|
||||
}
|
||||
|
||||
/* Don't use ast_adsi_print here, this way we can send it all at once instead of in 2 transmissions */
|
||||
for (x = 0; lines[x]; x++) {
|
||||
bytes += ast_adsi_display(buf + bytes, ADSI_INFO_PAGE, x + 1, alignments[x], 0, lines[x], "");
|
||||
}
|
||||
bytes += ast_adsi_set_line(buf + bytes, ADSI_INFO_PAGE, 1);
|
||||
|
||||
keys[3] = ADSI_KEY_APPS + 3;
|
||||
keys[4] = ADSI_KEY_APPS + 4;
|
||||
keys[5] = ADSI_KEY_APPS + 5;
|
||||
/* You might think we only need to set the keys up the first time, but nope, we've got to do it each time. */
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Dial", "Dial", "1", 0);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Next", "Next", "*", 0);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 0);
|
||||
bytes += ast_adsi_set_keys(buf + bytes, keys);
|
||||
bytes += ast_adsi_voice_mode(buf + bytes, 0);
|
||||
|
||||
ast_debug(3, "Sending ADSI confirmation menu for %s\n", name);
|
||||
|
||||
return ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
|
||||
}
|
||||
|
||||
static int compare(const char *text, const char *template)
|
||||
{
|
||||
char digit;
|
||||
|
@ -318,6 +406,9 @@ static int select_entry(struct ast_channel *chan, const char *dialcontext, const
|
|||
if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
|
||||
/* We still want to set the exten though */
|
||||
ast_channel_exten_set(chan, item->exten);
|
||||
} else if (ast_test_flag(flags, OPT_SKIP)) {
|
||||
/* Skip calling the extension, only set it in the channel variable. */
|
||||
pbx_builtin_setvar_helper(chan, "DIRECTORY_EXTEN", item->exten);
|
||||
} else if (ast_goto_if_exists(chan, S_OR(dialcontext, item->context), item->exten, 1)) {
|
||||
ast_log(LOG_WARNING,
|
||||
"Can't find extension '%s' in context '%s'. "
|
||||
|
@ -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++) {
|
||||
item = *ptr;
|
||||
|
||||
if (ast_test_flag(flags, OPT_ADSI) && adsi_confirm_match(chan, i, count, item->exten, item->name, ast_test_flag(flags, OPT_SAYEXTENSION))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (loop = 3 ; loop > 0; loop--) {
|
||||
if (!res)
|
||||
res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
|
||||
|
@ -458,7 +553,7 @@ static int select_item_menu(struct ast_channel *chan, struct directory_item **it
|
|||
|
||||
AST_THREADSTORAGE(commonbuf);
|
||||
|
||||
static struct ast_config *realtime_directory(char *context)
|
||||
static struct ast_config *realtime_directory(char *context, const char *filename)
|
||||
{
|
||||
struct ast_config *cfg;
|
||||
struct ast_config *rtdata = NULL;
|
||||
|
@ -475,14 +570,14 @@ static struct ast_config *realtime_directory(char *context)
|
|||
}
|
||||
|
||||
/* Load flat file config. */
|
||||
cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
|
||||
cfg = ast_config_load(filename, config_flags);
|
||||
|
||||
if (!cfg) {
|
||||
/* Loading config failed. */
|
||||
ast_log(LOG_WARNING, "Loading config failed.\n");
|
||||
return NULL;
|
||||
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
|
||||
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", VOICEMAIL_CONFIG);
|
||||
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", filename);
|
||||
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))
|
||||
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");
|
||||
return -1;
|
||||
}
|
||||
|
@ -913,6 +1010,18 @@ static int directory_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
digits[7] = digit + '0';
|
||||
|
||||
if (ast_test_flag(&flags, OPT_ADSI)) {
|
||||
if (!ast_adsi_available(chan)) {
|
||||
ast_log(LOG_WARNING, "ADSI not available on %s\n", ast_channel_name(chan));
|
||||
ast_clear_flag(&flags, OPT_ADSI);
|
||||
} else {
|
||||
res = ast_adsi_load_session(chan, NULL, 0, 1);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_channel_state(chan) != AST_STATE_UP) {
|
||||
if (!ast_test_flag(&flags, OPT_NOANSWER)) {
|
||||
/* Otherwise answer unless we're supposed to read while on-hook */
|
||||
|
@ -920,6 +1029,9 @@ static int directory_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
}
|
||||
for (;;) {
|
||||
if (ast_test_flag(&flags, OPT_ADSI) && adsi_search_input(chan)) {
|
||||
return -1;
|
||||
}
|
||||
if (!ast_strlen_zero(dirintro) && !res) {
|
||||
res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
|
||||
} else if (!res) {
|
||||
|
|
|
@ -836,8 +836,9 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
|
|||
}
|
||||
}
|
||||
} else {
|
||||
ast_verb(3, "Skip playback of caller name / norecording\n");
|
||||
tmpuser->state = 2;
|
||||
ast_debug(1, "Taking call with no prompt\n");
|
||||
ast_frfree(f);
|
||||
return tmpuser->ochan;
|
||||
}
|
||||
break;
|
||||
case AST_CONTROL_BUSY:
|
||||
|
@ -964,11 +965,6 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!tpargs->enable_callee_prompt && tmpuser) {
|
||||
ast_debug(1, "Taking call with no prompt\n");
|
||||
ast_frfree(f);
|
||||
return tmpuser->ochan;
|
||||
}
|
||||
if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
|
||||
int cmp_len;
|
||||
|
||||
|
@ -1072,6 +1068,7 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
|
|||
ast_copy_string(num, nm->number, sizeof(num));
|
||||
for (number = num; number; number = rest) {
|
||||
struct ast_channel *outbound;
|
||||
struct ast_format_cap *caps;
|
||||
|
||||
rest = strchr(number, '&');
|
||||
if (rest) {
|
||||
|
@ -1101,8 +1098,15 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
|
|||
? "/n" : "/m");
|
||||
}
|
||||
|
||||
outbound = ast_request("Local", ast_channel_nativeformats(caller), NULL, caller,
|
||||
tmpuser->dialarg, &dg);
|
||||
/* Capture nativeformats reference in case it gets changed */
|
||||
ast_channel_lock(caller);
|
||||
caps = ao2_bump(ast_channel_nativeformats(caller));
|
||||
ast_channel_unlock(caller);
|
||||
|
||||
outbound = ast_request("Local", caps, NULL, caller, tmpuser->dialarg, &dg);
|
||||
|
||||
ao2_cleanup(caps);
|
||||
|
||||
if (!outbound) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n",
|
||||
tmpuser->dialarg, ast_cause2str(dg));
|
||||
|
|
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright 2022, Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief If Branch Implementation
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/channel.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="If" language="en_US">
|
||||
<synopsis>
|
||||
Start an if branch.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="expr" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Start an If branch. Execution will continue inside the branch
|
||||
if expr is true.</para>
|
||||
<note><para>This application (and related applications) set variables
|
||||
internally during execution.</para></note>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="ElseIf" language="en_US">
|
||||
<synopsis>
|
||||
Start an else if branch.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="expr" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Start an optional ElseIf branch. Execution will continue inside the branch
|
||||
if expr is true and if previous If and ElseIf branches evaluated to false.</para>
|
||||
<para>Please note that execution inside a true If branch will fallthrough into
|
||||
ElseIf unless the If segment is terminated with an ExitIf call. This is only
|
||||
necessary with ElseIf but not with Else.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="Else" language="en_US">
|
||||
<synopsis>
|
||||
Define an optional else branch.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="expr" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Start an Else branch. Execution will jump here if all previous
|
||||
If and ElseIf branches evaluated to false.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="EndIf" language="en_US">
|
||||
<synopsis>
|
||||
End an if branch.
|
||||
</synopsis>
|
||||
<syntax />
|
||||
<description>
|
||||
<para>Ends the branch begun by the preceding <literal>If()</literal> application.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="ExitIf" language="en_US">
|
||||
<synopsis>
|
||||
End an If branch.
|
||||
</synopsis>
|
||||
<syntax />
|
||||
<description>
|
||||
<para>Exits an <literal>If()</literal> branch, whether or not it has completed.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static char *if_app = "If";
|
||||
static char *elseif_app = "ElseIf";
|
||||
static char *else_app = "Else";
|
||||
static char *stop_app = "EndIf";
|
||||
static char *exit_app = "ExitIf";
|
||||
|
||||
#define VAR_SIZE 64
|
||||
|
||||
static const char *get_index(struct ast_channel *chan, const char *prefix, int idx)
|
||||
{
|
||||
char varname[VAR_SIZE];
|
||||
|
||||
snprintf(varname, VAR_SIZE, "%s_%d", prefix, idx);
|
||||
return pbx_builtin_getvar_helper(chan, varname);
|
||||
}
|
||||
|
||||
static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
|
||||
{
|
||||
struct ast_exten *e;
|
||||
struct ast_context *c2;
|
||||
int idx;
|
||||
|
||||
for (e = ast_walk_context_extensions(c, NULL); e; e = ast_walk_context_extensions(c, e)) {
|
||||
if (ast_extension_match(ast_get_extension_name(e), exten)) {
|
||||
int needmatch = ast_get_extension_matchcid(e);
|
||||
if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
|
||||
(!needmatch)) {
|
||||
/* This is the matching extension we want */
|
||||
struct ast_exten *p;
|
||||
for (p = ast_walk_extension_priorities(e, NULL); p; p = ast_walk_extension_priorities(e, p)) {
|
||||
if (priority != ast_get_extension_priority(p))
|
||||
continue;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No match; run through includes */
|
||||
for (idx = 0; idx < ast_context_includes_count(c); idx++) {
|
||||
const struct ast_include *i = ast_context_includes_get(c, idx);
|
||||
|
||||
for (c2 = ast_walk_contexts(NULL); c2; c2 = ast_walk_contexts(c2)) {
|
||||
if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
|
||||
e = find_matching_priority(c2, exten, priority, callerid);
|
||||
if (e)
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int find_matching_endif(struct ast_channel *chan, const char *otherapp)
|
||||
{
|
||||
struct ast_context *c;
|
||||
int res = -1;
|
||||
|
||||
if (ast_rdlock_contexts()) {
|
||||
ast_log(LOG_ERROR, "Failed to lock contexts list\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (c = ast_walk_contexts(NULL); c; c = ast_walk_contexts(c)) {
|
||||
struct ast_exten *e;
|
||||
|
||||
if (!ast_rdlock_context(c)) {
|
||||
if (!strcmp(ast_get_context_name(c), ast_channel_context(chan))) {
|
||||
/* This is the matching context we want */
|
||||
|
||||
int cur_priority = ast_channel_priority(chan) + 1, level = 1;
|
||||
|
||||
for (e = find_matching_priority(c, ast_channel_exten(chan), cur_priority,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
|
||||
e;
|
||||
e = find_matching_priority(c, ast_channel_exten(chan), ++cur_priority,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
|
||||
|
||||
if (!strcasecmp(ast_get_extension_app(e), "IF")) {
|
||||
level++;
|
||||
} else if (!strcasecmp(ast_get_extension_app(e), "ENDIF")) {
|
||||
level--;
|
||||
}
|
||||
|
||||
if (!otherapp && level == 0) {
|
||||
res = cur_priority;
|
||||
break;
|
||||
} else if (otherapp && level == 1 && !strcasecmp(ast_get_extension_app(e), otherapp)) {
|
||||
res = cur_priority;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_unlock_context(c);
|
||||
if (res > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_unlock_contexts();
|
||||
return res;
|
||||
}
|
||||
|
||||
static int if_helper(struct ast_channel *chan, const char *data, int end)
|
||||
{
|
||||
int res = 0;
|
||||
const char *if_pri = NULL;
|
||||
char *my_name = NULL;
|
||||
const char *label = NULL;
|
||||
char varname[VAR_SIZE + 3]; /* + IF_ */
|
||||
char end_varname[sizeof(varname) + 4]; /* + END_ + sizeof(varname) */
|
||||
const char *prefix = "IF";
|
||||
size_t size = 0;
|
||||
int used_index_i = -1, x = 0;
|
||||
char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
|
||||
|
||||
if (!chan) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (x = 0 ;; x++) {
|
||||
if (get_index(chan, prefix, x)) {
|
||||
used_index_i = x;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(used_index, sizeof(used_index), "%d", used_index_i);
|
||||
snprintf(new_index, sizeof(new_index), "%d", used_index_i + 1);
|
||||
|
||||
size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
|
||||
my_name = ast_alloca(size);
|
||||
memset(my_name, 0, size);
|
||||
snprintf(my_name, size, "%s_%s_%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
|
||||
|
||||
ast_channel_lock(chan);
|
||||
if (end > 1) {
|
||||
label = used_index;
|
||||
} else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
|
||||
label = new_index;
|
||||
pbx_builtin_setvar_helper(chan, my_name, label);
|
||||
}
|
||||
snprintf(varname, sizeof(varname), "%s_%s", prefix, label);
|
||||
if ((if_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
|
||||
if_pri = ast_strdupa(if_pri);
|
||||
snprintf(end_varname,sizeof(end_varname),"END_%s",varname);
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
if ((end <= 1 && !pbx_checkcondition(ast_strdupa(data))) || (end > 1)) {
|
||||
/* Condition Met (clean up helper vars) */
|
||||
const char *goto_str;
|
||||
int pri, endifpri;
|
||||
pbx_builtin_setvar_helper(chan, varname, NULL);
|
||||
pbx_builtin_setvar_helper(chan, my_name, NULL);
|
||||
snprintf(end_varname,sizeof(end_varname),"END_%s",varname);
|
||||
ast_channel_lock(chan);
|
||||
/* For EndIf, simply go to the next priority.
|
||||
* We do not add 1 to ast_channel_priority because the dialplan will
|
||||
* auto-increment the priority when we return, so just keep the priority as is.
|
||||
* For ExitIf or false If() condition, we need to find the end of the current
|
||||
* If branch (at same indentation) and branch there. */
|
||||
endifpri = end == 2 ? ast_channel_priority(chan) : find_matching_endif(chan, NULL);
|
||||
if ((goto_str = pbx_builtin_getvar_helper(chan, end_varname))) {
|
||||
ast_parseable_goto(chan, goto_str);
|
||||
pbx_builtin_setvar_helper(chan, end_varname, NULL);
|
||||
} else if (end <= 1 && (pri = find_matching_endif(chan, "ElseIf")) > 0 && pri < endifpri) {
|
||||
pri--; /* back up a priority, since it returned the priority after the ElseIf */
|
||||
/* If is false, and ElseIf exists, so jump to ElseIf */
|
||||
ast_verb(3, "Taking conditional false branch, jumping to priority %d\n", pri);
|
||||
ast_channel_priority_set(chan, pri);
|
||||
} else if (end <= 1 && (pri = find_matching_endif(chan, "Else")) > 0 && pri < endifpri) {
|
||||
/* don't need to back up a priority, because we don't actually need to execute Else, just jump to the priority after. Directly executing Else will exit the conditional. */
|
||||
/* If is false, and Else exists, so jump to Else */
|
||||
ast_verb(3, "Taking absolute false branch, jumping to priority %d\n", pri);
|
||||
ast_channel_priority_set(chan, pri);
|
||||
} else {
|
||||
pri = endifpri;
|
||||
if (pri > 0) {
|
||||
ast_verb(3, "Exiting conditional, jumping to priority %d\n", pri);
|
||||
ast_channel_priority_set(chan, pri);
|
||||
} else if (end == 4) { /* Condition added because of end > 0 instead of end == 4 */
|
||||
ast_log(LOG_WARNING, "Couldn't find matching EndIf? (If at %s@%s priority %d)\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
|
||||
}
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (end <= 1 && !if_pri) {
|
||||
char *goto_str;
|
||||
size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
|
||||
goto_str = ast_alloca(size);
|
||||
memset(goto_str, 0, size);
|
||||
snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
|
||||
pbx_builtin_setvar_helper(chan, varname, goto_str);
|
||||
} else if (end > 1 && if_pri) {
|
||||
/* END of branch */
|
||||
snprintf(end_varname, sizeof(end_varname), "END_%s", varname);
|
||||
if (!pbx_builtin_getvar_helper(chan, end_varname)) {
|
||||
char *goto_str;
|
||||
size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
|
||||
goto_str = ast_alloca(size);
|
||||
memset(goto_str, 0, size);
|
||||
snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan)+1);
|
||||
pbx_builtin_setvar_helper(chan, end_varname, goto_str);
|
||||
}
|
||||
ast_parseable_goto(chan, if_pri);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int if_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 0);
|
||||
}
|
||||
|
||||
static int elseif_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 1);
|
||||
}
|
||||
|
||||
static int end_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 2);
|
||||
}
|
||||
|
||||
static int else_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 3);
|
||||
}
|
||||
|
||||
static int exit_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 4);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(if_app);
|
||||
res |= ast_unregister_application(elseif_app);
|
||||
res |= ast_unregister_application(stop_app);
|
||||
res |= ast_unregister_application(else_app);
|
||||
res |= ast_unregister_application(exit_app);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application_xml(if_app, if_exec);
|
||||
res |= ast_register_application_xml(elseif_app, elseif_exec);
|
||||
res |= ast_register_application_xml(stop_app, end_exec);
|
||||
res |= ast_register_application_xml(else_app, else_exec);
|
||||
res |= ast_register_application_xml(exit_app, exit_exec);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "If Branch and Conditional Execution");
|
|
@ -262,7 +262,7 @@ static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive
|
|||
char *save_macro_priority;
|
||||
char *save_macro_offset;
|
||||
int save_in_subroutine;
|
||||
struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL);
|
||||
struct ast_datastore *macro_store;
|
||||
int had_infinite_include_error = 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_channel_lock(chan);
|
||||
|
||||
macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL);
|
||||
|
||||
do {
|
||||
if (macro_store) {
|
||||
break;
|
||||
|
@ -291,7 +295,6 @@ static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive
|
|||
} while (0);
|
||||
|
||||
/* does the user want a deeper rabbit hole? */
|
||||
ast_channel_lock(chan);
|
||||
if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) {
|
||||
sscanf(s, "%30d", &maxdepth);
|
||||
}
|
||||
|
|
|
@ -639,6 +639,82 @@
|
|||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<managerEvent language="en_US" name="MeetmeList">
|
||||
<managerEventInstance class="EVENT_FLAG_CALL">
|
||||
<synopsis>Raised in response to a MeetmeList command.</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Conference">
|
||||
<para>Conference ID.</para>
|
||||
</parameter>
|
||||
<parameter name="UserNumber">
|
||||
<para>User ID.</para>
|
||||
</parameter>
|
||||
<parameter name="CallerIDNum">
|
||||
<para>Caller ID number.</para>
|
||||
</parameter>
|
||||
<parameter name="CallerIDName">
|
||||
<para>Caller ID name.</para>
|
||||
</parameter>
|
||||
<parameter name="ConnectedLineNum">
|
||||
<para>Connected Line number.</para>
|
||||
</parameter>
|
||||
<parameter name="ConnectedLineName">
|
||||
<para>Connected Line name.</para>
|
||||
</parameter>
|
||||
<parameter name="Channel">
|
||||
<para>Channel name</para>
|
||||
</parameter>
|
||||
<parameter name="Admin">
|
||||
<para>Whether or not the user is an admin.</para>
|
||||
</parameter>
|
||||
<parameter name="Role">
|
||||
<para>User role. Can be "Listen only", "Talk only", or "Talk and listen".</para>
|
||||
</parameter>
|
||||
<parameter name="MarkedUser">
|
||||
<para>Whether or not the user is a marked user.</para>
|
||||
</parameter>
|
||||
<parameter name="Muted">
|
||||
<para>Whether or not the user is currently muted.</para>
|
||||
</parameter>
|
||||
<parameter name="Talking">
|
||||
<para>Whether or not the user is currently talking.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<see-also>
|
||||
<ref type="manager">MeetmeList</ref>
|
||||
<ref type="application">MeetMe</ref>
|
||||
</see-also>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<managerEvent language="en_US" name="MeetmeListRooms">
|
||||
<managerEventInstance class="EVENT_FLAG_CALL">
|
||||
<synopsis>Raised in response to a MeetmeListRooms command.</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Conference">
|
||||
<para>Conference ID.</para>
|
||||
</parameter>
|
||||
<parameter name="Parties">
|
||||
<para>Number of parties in the conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Marked">
|
||||
<para>Number of marked users in the conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Activity">
|
||||
<para>Total duration of conference in HH:MM:SS format.</para>
|
||||
</parameter>
|
||||
<parameter name="Creation">
|
||||
<para>How the conference was created: "Dyanmic" or "Static".</para>
|
||||
</parameter>
|
||||
<parameter name="Locked">
|
||||
<para>Whether or not the conference is locked.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<see-also>
|
||||
<ref type="manager">MeetmeListRooms</ref>
|
||||
<ref type="application">MeetMe</ref>
|
||||
</see-also>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
***/
|
||||
|
||||
#define CONFIG_FILE_NAME "meetme.conf"
|
||||
|
@ -6073,7 +6149,7 @@ struct run_station_args {
|
|||
|
||||
static void answer_trunk_chan(struct ast_channel *chan)
|
||||
{
|
||||
ast_answer(chan);
|
||||
ast_raw_answer(chan);
|
||||
ast_indicate(chan, -1);
|
||||
}
|
||||
|
||||
|
@ -6918,8 +6994,18 @@ static void *dial_trunk(void *data)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* Wait for dial to end, while servicing the channel */
|
||||
while (ast_waitfor(trunk_ref->chan, 100)) {
|
||||
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))) {
|
||||
case AST_DIAL_RESULT_ANSWERED:
|
||||
trunk_ref->trunk->chan = ast_dial_answered(dial);
|
||||
|
@ -6956,8 +7042,6 @@ static void *dial_trunk(void *data)
|
|||
last_state = current_state;
|
||||
}
|
||||
|
||||
/* avoid tight loop... sleep for 1/10th second */
|
||||
ast_safe_sleep(trunk_ref->chan, 100);
|
||||
}
|
||||
|
||||
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);
|
||||
/* 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
|
||||
* conference is created before continuing on here. */
|
||||
ast_autoservice_start(chan);
|
||||
* conference is created before continuing on here.
|
||||
* Don't autoservice the channel or we'll have multiple threads
|
||||
* handling it. dial_trunk services the channel.
|
||||
*/
|
||||
ast_mutex_init(&cond_lock);
|
||||
ast_cond_init(&cond, NULL);
|
||||
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_destroy(&cond_lock);
|
||||
ast_cond_destroy(&cond);
|
||||
ast_autoservice_stop(chan);
|
||||
|
||||
if (!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");
|
||||
|
|
|
@ -235,6 +235,7 @@ static const char sendmf_name[] = "SendMF";
|
|||
* \param buflen Size of buffer
|
||||
* \param timeout ms to wait for all digits before giving up
|
||||
* \param features Any additional DSP features to use
|
||||
* \param laxkp Receive digits even if KP not received
|
||||
* \param override Start over if we receive additional KPs
|
||||
* \param no_kp Don't include KP in the output
|
||||
* \param no_st Don't include start digits in the output
|
||||
|
|
|
@ -90,6 +90,16 @@
|
|||
<para>Play a periodic beep while this call is being recorded.</para>
|
||||
<argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument>
|
||||
</option>
|
||||
<option name="c">
|
||||
<para>Use the real Caller ID from the channel for the voicemail Caller ID.</para>
|
||||
<para>By default, the Connected Line is used. If you want the channel caller's
|
||||
real number, you may need to specify this option.</para>
|
||||
</option>
|
||||
<option name="d">
|
||||
<para>Delete the recording file as soon as MixMonitor is done with it.</para>
|
||||
<para>For example, if you use the m option to dispatch the recording to a voicemail box,
|
||||
you can specify this option to delete the original copy of it afterwards.</para>
|
||||
</option>
|
||||
<option name="v">
|
||||
<para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
|
||||
(range <literal>-4</literal> to <literal>4</literal>)</para>
|
||||
|
@ -407,6 +417,8 @@ enum mixmonitor_flags {
|
|||
MUXFLAG_BEEP_STOP = (1 << 13),
|
||||
MUXFLAG_DEPRECATED_RWSYNC = (1 << 14),
|
||||
MUXFLAG_NO_RWSYNC = (1 << 15),
|
||||
MUXFLAG_AUTO_DELETE = (1 << 16),
|
||||
MUXFLAG_REAL_CALLERID = (1 << 17),
|
||||
};
|
||||
|
||||
enum mixmonitor_args {
|
||||
|
@ -427,6 +439,8 @@ AST_APP_OPTIONS(mixmonitor_opts, {
|
|||
AST_APP_OPTION('a', MUXFLAG_APPEND),
|
||||
AST_APP_OPTION('b', MUXFLAG_BRIDGED),
|
||||
AST_APP_OPTION_ARG('B', MUXFLAG_BEEP, OPT_ARG_BEEP_INTERVAL),
|
||||
AST_APP_OPTION('c', MUXFLAG_REAL_CALLERID),
|
||||
AST_APP_OPTION('d', MUXFLAG_AUTO_DELETE),
|
||||
AST_APP_OPTION('p', MUXFLAG_BEEP_START),
|
||||
AST_APP_OPTION('P', MUXFLAG_BEEP_STOP),
|
||||
AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
|
||||
|
@ -860,6 +874,19 @@ static void *mixmonitor_thread(void *obj)
|
|||
ast_debug(3, "No recipients to forward monitor to, moving on.\n");
|
||||
}
|
||||
|
||||
if (ast_test_flag(mixmonitor, MUXFLAG_AUTO_DELETE)) {
|
||||
ast_debug(3, "Deleting our copies of recording files\n");
|
||||
if (!ast_strlen_zero(fs_ext)) {
|
||||
ast_filedelete(mixmonitor->filename, fs_ext);
|
||||
}
|
||||
if (!ast_strlen_zero(fs_read_ext)) {
|
||||
ast_filedelete(mixmonitor->filename_read, fs_ext);
|
||||
}
|
||||
if (!ast_strlen_zero(fs_write_ext)) {
|
||||
ast_filedelete(mixmonitor->filename_write, fs_ext);
|
||||
}
|
||||
}
|
||||
|
||||
mixmonitor_free(mixmonitor);
|
||||
|
||||
ast_module_unref(ast_module_info->self);
|
||||
|
@ -1015,20 +1042,37 @@ static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
|
|||
|
||||
if (!ast_strlen_zero(recipients)) {
|
||||
char callerid[256];
|
||||
struct ast_party_connected_line *connected;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
|
||||
/* We use the connected line of the invoking channel for caller ID. */
|
||||
/* 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);
|
||||
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");
|
||||
if (ast_test_flag(mixmonitor, MUXFLAG_REAL_CALLERID)) {
|
||||
struct ast_party_caller *caller;
|
||||
caller = ast_channel_caller(chan);
|
||||
ast_debug(3, "Caller ID = %d - %s : %d - %s\n", caller->id.name.valid,
|
||||
caller->id.name.str, caller->id.number.valid,
|
||||
caller->id.number.str);
|
||||
ast_callerid_merge(callerid, sizeof(callerid),
|
||||
S_COR(caller->id.name.valid, caller->id.name.str, NULL),
|
||||
S_COR(caller->id.number.valid, caller->id.number.str, NULL),
|
||||
"Unknown");
|
||||
} else {
|
||||
struct ast_party_connected_line *connected;
|
||||
connected = ast_channel_connected(chan);
|
||||
ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
|
||||
connected->id.name.str, connected->id.number.valid,
|
||||
connected->id.number.str);
|
||||
ast_callerid_merge(callerid, sizeof(callerid),
|
||||
S_COR(connected->id.name.valid, connected->id.name.str, NULL),
|
||||
S_COR(connected->id.number.valid, connected->id.number.str, NULL),
|
||||
"Unknown");
|
||||
}
|
||||
|
||||
ast_string_field_set(mixmonitor, call_context, ast_channel_context(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;
|
||||
}
|
||||
|
||||
/*! \brief Mute / unmute an individual MixMonitor by id */
|
||||
static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data,
|
||||
enum ast_audiohook_flags flag, int clearmute)
|
||||
{
|
||||
struct ast_datastore *datastore = NULL;
|
||||
char *parse = "";
|
||||
struct mixmonitor_ds *mixmonitor_ds;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(mixmonid);
|
||||
);
|
||||
|
||||
if (!ast_strlen_zero(data)) {
|
||||
parse = ast_strdupa(data);
|
||||
}
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
ast_channel_lock(chan);
|
||||
|
||||
datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info,
|
||||
S_OR(args.mixmonid, NULL));
|
||||
if (!datastore) {
|
||||
ast_channel_unlock(chan);
|
||||
return -1;
|
||||
}
|
||||
mixmonitor_ds = datastore->data;
|
||||
|
||||
ast_mutex_lock(&mixmonitor_ds->lock);
|
||||
|
||||
if (mixmonitor_ds->audiohook) {
|
||||
if (clearmute) {
|
||||
ast_clear_flag(mixmonitor_ds->audiohook, flag);
|
||||
} else {
|
||||
ast_set_flag(mixmonitor_ds->audiohook, flag);
|
||||
}
|
||||
}
|
||||
|
||||
ast_mutex_unlock(&mixmonitor_ds->lock);
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Mute / unmute a MixMonitor channel */
|
||||
static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
|
||||
{
|
||||
|
@ -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 *state = astman_get_header(m, "State");
|
||||
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;
|
||||
RAII_VAR(struct stasis_message *, stasis_message, NULL, ao2_cleanup);
|
||||
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;
|
||||
}
|
||||
|
||||
if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
|
||||
ast_channel_unref(c);
|
||||
astman_send_error(s, m, "Cannot set mute flag");
|
||||
return AMI_SUCCESS;
|
||||
if (ast_strlen_zero(mixmonitor_id)) {
|
||||
mutedcount = ast_audiohook_set_mute_all(c, mixmonitor_spy_type, flag, clearmute);
|
||||
if (mutedcount < 0) {
|
||||
ast_channel_unref(c);
|
||||
astman_send_error(s, m, "Cannot set mute flag");
|
||||
return AMI_SUCCESS;
|
||||
}
|
||||
} else {
|
||||
if (mute_mixmonitor_instance(c, mixmonitor_id, flag, clearmute)) {
|
||||
ast_channel_unref(c);
|
||||
astman_send_error(s, m, "Cannot set mute flag");
|
||||
return AMI_SUCCESS;
|
||||
}
|
||||
mutedcount = 1;
|
||||
}
|
||||
|
||||
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,
|
||||
"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),
|
||||
ast_channel_mixmonitor_mute_type(), stasis_message_blob);
|
||||
|
|
|
@ -101,7 +101,7 @@ static int mp3play(const char *filename, unsigned int sampling_rate, int fd)
|
|||
/* Execute mpg123, but buffer if it's a net connection */
|
||||
if (!strncasecmp(filename, "http://", 7) && strstr(filename, ".m3u")) {
|
||||
char buffer_size_str[8];
|
||||
snprintf(buffer_size_str, 8, "%u", (int) 0.5*2*sampling_rate/1000); // 0.5 seconds for a live stream
|
||||
snprintf(buffer_size_str, 8, "%u", (int) 0.5*2*sampling_rate/1000); /* 0.5 seconds for a live stream */
|
||||
/* Most commonly installed in /usr/local/bin */
|
||||
execl(LOCAL_MPG_123, "mpg123", "-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 */
|
||||
|
@ -111,7 +111,7 @@ static int mp3play(const char *filename, unsigned int sampling_rate, int fd)
|
|||
}
|
||||
else if (!strncasecmp(filename, "http://", 7)) {
|
||||
char buffer_size_str[8];
|
||||
snprintf(buffer_size_str, 8, "%u", 6*2*sampling_rate/1000); // 6 seconds for a remote MP3 file
|
||||
snprintf(buffer_size_str, 8, "%u", 6*2*sampling_rate/1000); /* 6 seconds for a remote MP3 file */
|
||||
/* Most commonly installed in /usr/local/bin */
|
||||
execl(LOCAL_MPG_123, "mpg123", "-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 */
|
||||
|
|
|
@ -48,6 +48,13 @@
|
|||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="filenames" required="true" argsep="&">
|
||||
<para>Ampersand separated list of filenames. If the filename
|
||||
is a relative filename (it does not begin with a slash), it
|
||||
will be searched for in the Asterisk sounds directory. If the
|
||||
filename is able to be parsed as a URL, Asterisk will
|
||||
download the file and then begin playback on it. To include a
|
||||
literal <literal>&</literal> in the URL you can enclose
|
||||
the URL in single quotes.</para>
|
||||
<argument name="filename" required="true" />
|
||||
<argument name="filename2" multiple="true" />
|
||||
</parameter>
|
||||
|
@ -492,7 +499,7 @@ static int playback_exec(struct ast_channel *chan, const char *data)
|
|||
char *front;
|
||||
|
||||
ast_stopstream(chan);
|
||||
while (!res && (front = strsep(&back, "&"))) {
|
||||
while (!res && (front = ast_strsep(&back, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||
if (option_say)
|
||||
res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1);
|
||||
else if (option_mix){
|
||||
|
@ -507,8 +514,7 @@ static int playback_exec(struct ast_channel *chan, const char *data)
|
|||
if (!res) {
|
||||
res = ast_waitstream(chan, "");
|
||||
ast_stopstream(chan);
|
||||
}
|
||||
if (res) {
|
||||
} else {
|
||||
if (!ast_check_hangup(chan)) {
|
||||
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>
|
||||
</option>
|
||||
<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 name="F" argsep="^">
|
||||
<argument name="context" required="false" />
|
||||
|
@ -171,12 +174,6 @@
|
|||
to the specified destination and <emphasis>start</emphasis> execution at that location.</para>
|
||||
<para>NOTE: Any channel variables you want the called channel to inherit from the caller channel must be
|
||||
prefixed with one or two underbars ('_').</para>
|
||||
</option>
|
||||
<option name="F">
|
||||
<para>When the caller hangs up, transfer the <emphasis>called member</emphasis> to the next priority of
|
||||
the current extension and <emphasis>start</emphasis> execution at that location.</para>
|
||||
<para>NOTE: Any channel variables you want the called channel to inherit from the caller channel must be
|
||||
prefixed with one or two underbars ('_').</para>
|
||||
<para>NOTE: Using this option from a Macro() or GoSub() might not make sense as there would be no return points.</para>
|
||||
</option>
|
||||
<option name="h">
|
||||
|
@ -244,11 +241,18 @@
|
|||
<para><replaceable>URL</replaceable> will be sent to the called party if the channel supports it.</para>
|
||||
</parameter>
|
||||
<parameter name="announceoverride" argsep="&">
|
||||
<argument name="filename" required="true">
|
||||
<para>Announcement file(s) to play to agent before bridging call, overriding the announcement(s)
|
||||
configured in <filename>queues.conf</filename>, if any.</para>
|
||||
</argument>
|
||||
<argument name="filename2" multiple="true" />
|
||||
<para>Announcement file(s) to play to agent before bridging
|
||||
call, overriding the announcement(s) configured in
|
||||
<filename>queues.conf</filename>, if any.</para>
|
||||
<para>Ampersand separated list of filenames. If the filename
|
||||
is a relative filename (it does not begin with a slash), it
|
||||
will be searched for in the Asterisk sounds directory. If the
|
||||
filename is able to be parsed as a URL, Asterisk will
|
||||
download the file and then begin playback on it. To include a
|
||||
literal <literal>&</literal> in the URL you can enclose
|
||||
the URL in single quotes.</para>
|
||||
<argument name="announceoverride" required="true" />
|
||||
<argument name="announceoverride2" multiple="true" />
|
||||
</parameter>
|
||||
<parameter name="timeout">
|
||||
<para>Will cause the queue to fail out after a specified number of
|
||||
|
@ -1062,6 +1066,9 @@
|
|||
<parameter name="Priority" required="true">
|
||||
<para>Priority value for change for caller on queue.</para>
|
||||
</parameter>
|
||||
<parameter name="Immediate">
|
||||
<para>When set to yes will cause the priority change to be reflected immediately, causing the channel to change position within the queue.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
</description>
|
||||
|
@ -1277,7 +1284,7 @@
|
|||
</syntax>
|
||||
<see-also>
|
||||
<ref type="application">PauseQueueMember</ref>
|
||||
<ref type="application">UnPauseQueueMember</ref>
|
||||
<ref type="application">UnpauseQueueMember</ref>
|
||||
</see-also>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
|
@ -1621,9 +1628,15 @@ static int negative_penalty_invalid;
|
|||
/*! \brief queues.conf [general] option */
|
||||
static int log_membername_as_agent;
|
||||
|
||||
/*! \brief queues.conf [general] option */
|
||||
static int force_longest_waiting_caller;
|
||||
|
||||
/*! \brief name of the ringinuse field in the realtime database */
|
||||
static char *realtime_ringinuse_field;
|
||||
|
||||
/*! \brief does realtime backend support reason_paused */
|
||||
static int realtime_reason_paused;
|
||||
|
||||
enum queue_result {
|
||||
QUEUE_UNKNOWN = 0,
|
||||
QUEUE_TIMEOUT = 1,
|
||||
|
@ -1856,6 +1869,7 @@ struct call_queue {
|
|||
int announcepositionlimit; /*!< How many positions we announce? */
|
||||
int announcefrequency; /*!< How often to announce their position */
|
||||
int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
|
||||
int periodicannouncestartdelay; /*!< How long into the queue should the periodic accouncement start */
|
||||
int periodicannouncefrequency; /*!< How often to play periodic announcement */
|
||||
int numperiodicannounce; /*!< The number of periodic announcements configured */
|
||||
int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
|
||||
|
@ -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
|
||||
* 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. */
|
||||
queue_ref(q);
|
||||
new->parent = q;
|
||||
if (!new->parent) {
|
||||
queue_ref(q);
|
||||
new->parent = q;
|
||||
}
|
||||
new->pos = ++(*pos);
|
||||
new->opos = *pos;
|
||||
}
|
||||
|
@ -2956,7 +2972,11 @@ static void init_queue(struct call_queue *q)
|
|||
q->timeout = DEFAULT_TIMEOUT;
|
||||
q->maxlen = 0;
|
||||
|
||||
ast_string_field_set(q, announce, "");
|
||||
ast_string_field_set(q, context, "");
|
||||
ast_string_field_set(q, membermacro, "");
|
||||
ast_string_field_set(q, membergosub, "");
|
||||
ast_string_field_set(q, defaultrule, "");
|
||||
|
||||
q->announcefrequency = 0;
|
||||
q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
|
||||
|
@ -2983,9 +3003,13 @@ static void init_queue(struct call_queue *q)
|
|||
q->weight = 0;
|
||||
q->timeoutrestart = 0;
|
||||
q->periodicannouncefrequency = 0;
|
||||
q->periodicannouncestartdelay = -1;
|
||||
q->randomperiodicannounce = 0;
|
||||
q->numperiodicannounce = 0;
|
||||
q->relativeperiodicannounce = 0;
|
||||
q->autopause = QUEUE_AUTOPAUSE_OFF;
|
||||
q->autopausebusy = 0;
|
||||
q->autopauseunavail = 0;
|
||||
q->timeoutpriority = TIMEOUT_PRIORITY_APP;
|
||||
q->autopausedelay = 0;
|
||||
if (!q->members) {
|
||||
|
@ -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_seconds, "queue-seconds");
|
||||
ast_string_field_set(q, sound_thanks, "queue-thankyou");
|
||||
ast_string_field_set(q, sound_callerannounce, "");
|
||||
ast_string_field_set(q, sound_reporthold, "queue-reporthold");
|
||||
|
||||
if (!q->sound_periodicannounce[0]) {
|
||||
|
@ -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);
|
||||
q->numperiodicannounce = 1;
|
||||
}
|
||||
} else if (!strcasecmp(param, "periodic-announce-startdelay")) {
|
||||
q->periodicannouncestartdelay = atoi(val);
|
||||
} else if (!strcasecmp(param, "periodic-announce-frequency")) {
|
||||
q->periodicannouncefrequency = atoi(val);
|
||||
} else if (!strcasecmp(param, "relative-periodic-announce")) {
|
||||
|
@ -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 *paused_str = ast_variable_retrieve(member_config, category, "paused");
|
||||
const char *wrapuptime_str = ast_variable_retrieve(member_config, category, "wrapuptime");
|
||||
const char *reason_paused = ast_variable_retrieve(member_config, category, "reason_paused");
|
||||
|
||||
if (ast_strlen_zero(rt_uniqueid)) {
|
||||
ast_log(LOG_WARNING, "Realtime field 'uniqueid' is empty for member %s\n",
|
||||
|
@ -3653,6 +3681,9 @@ static void rt_handle_member_record(struct call_queue *q, char *category, struct
|
|||
m->penalty = penalty;
|
||||
m->ringinuse = ringinuse;
|
||||
m->wrapuptime = wrapuptime;
|
||||
if (realtime_reason_paused) {
|
||||
ast_copy_string(m->reason_paused, S_OR(reason_paused, ""), sizeof(m->reason_paused));
|
||||
}
|
||||
found = 1;
|
||||
ao2_ref(m, -1);
|
||||
break;
|
||||
|
@ -3667,6 +3698,9 @@ static void rt_handle_member_record(struct call_queue *q, char *category, struct
|
|||
m->dead = 0;
|
||||
m->realtime = 1;
|
||||
ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
|
||||
if (!ast_strlen_zero(reason_paused)) {
|
||||
ast_copy_string(m->reason_paused, reason_paused, sizeof(m->reason_paused));
|
||||
}
|
||||
if (!log_membername_as_agent) {
|
||||
ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
|
||||
} else {
|
||||
|
@ -4560,6 +4594,56 @@ static int compare_weight(struct call_queue *rq, struct member *member)
|
|||
return found;
|
||||
}
|
||||
|
||||
static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member)
|
||||
{
|
||||
struct call_queue *q;
|
||||
struct member *mem;
|
||||
int is_longest_waiting = 1;
|
||||
struct ao2_iterator queue_iter;
|
||||
struct queue_ent *ch;
|
||||
|
||||
queue_iter = ao2_iterator_init(queues, 0);
|
||||
while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
|
||||
if (q == caller->parent) { /* don't check myself, could deadlock */
|
||||
queue_t_unref(q, "Done with iterator");
|
||||
continue;
|
||||
}
|
||||
ao2_lock(q);
|
||||
/*
|
||||
* If the other queue has equal weight, see if we should let that handle
|
||||
* their call first. If weights are not equal, compare_weights will step in.
|
||||
*/
|
||||
if (q->weight == caller->parent->weight && q->count && q->members) {
|
||||
if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
|
||||
ast_debug(2, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
|
||||
|
||||
/* Does this queue have a caller that's been waiting longer? */
|
||||
ch = q->head;
|
||||
while (ch) {
|
||||
/* If ch->pending, the other call (which may be waiting for a longer period of time),
|
||||
* is already ringing at another agent. Ignore such callers; otherwise, all agents
|
||||
* will be unused until the first caller is picked up.
|
||||
*/
|
||||
if (ch->start < caller->start && !ch->pending) {
|
||||
ast_debug(1, "Queue %s has a call at position %i that's been waiting longer (%li vs %li)\n",
|
||||
q->name, ch->pos, ch->start, caller->start);
|
||||
is_longest_waiting = 0;
|
||||
break;
|
||||
}
|
||||
ch = ch->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
ao2_unlock(q);
|
||||
queue_t_unref(q, "Done with iterator");
|
||||
if (!is_longest_waiting) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ao2_iterator_destroy(&queue_iter);
|
||||
return is_longest_waiting;
|
||||
}
|
||||
|
||||
/*! \brief common hangup actions */
|
||||
static void do_hang(struct callattempt *o)
|
||||
{
|
||||
|
@ -4624,6 +4708,12 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (force_longest_waiting_caller && !is_longest_waiting_caller(qe, memberp)) {
|
||||
ast_debug(1, "Another caller was waiting longer; delaying call to %s:%s\n",
|
||||
qe->parent->name, call->interface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!memberp->ringinuse) {
|
||||
struct member *mem;
|
||||
|
||||
|
@ -7161,7 +7251,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
|
|||
if (!res2 && announce) {
|
||||
char *front;
|
||||
char *announcefiles = ast_strdupa(announce);
|
||||
while ((front = strsep(&announcefiles, "&"))) {
|
||||
while ((front = ast_strsep(&announcefiles, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||
if (play_file(peer, front) < 0) {
|
||||
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_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 queue_ent *qe;
|
||||
struct queue_ent *current, *prev = NULL, *caller_qe = NULL;
|
||||
int res = RES_NOSUCHQUEUE;
|
||||
|
||||
/*! \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);
|
||||
res = RES_NOT_CALLER;
|
||||
for (qe = q->head; qe; qe = qe->next) {
|
||||
if (strcmp(ast_channel_name(qe->chan), caller) == 0) {
|
||||
for (current = q->head; current; current = current->next) {
|
||||
if (strcmp(ast_channel_name(current->chan), caller) == 0) {
|
||||
ast_debug(1, "%s Caller new priority %d in queue %s\n",
|
||||
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;
|
||||
break;
|
||||
} else if (immediate) {
|
||||
prev = current;
|
||||
}
|
||||
}
|
||||
|
||||
if (caller_qe) {
|
||||
int inserted = 0, pos = 0;
|
||||
|
||||
/* If a caller queue entry exists, we are applying their priority immediately
|
||||
* and have to reinsert them at the correct position.
|
||||
*/
|
||||
prev = NULL;
|
||||
current = q->head;
|
||||
while (current) {
|
||||
if (!inserted && (caller_qe->prio > current->prio)) {
|
||||
insert_entry(q, prev, caller_qe, &pos);
|
||||
inserted = 1;
|
||||
}
|
||||
|
||||
/* We always update the position as it may have changed */
|
||||
current->pos = ++pos;
|
||||
|
||||
/* Move to the next caller in the queue */
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if (!inserted) {
|
||||
insert_entry(q, prev, caller_qe, &pos);
|
||||
}
|
||||
}
|
||||
|
||||
ao2_unlock(q);
|
||||
return res;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
if (mem->realtime) {
|
||||
if (update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0")) {
|
||||
ast_log(LOG_WARNING, "Failed %spause update of realtime queue member %s:%s\n",
|
||||
(paused ? "" : "un"), q->name, mem->interface);
|
||||
if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid)) {
|
||||
if (realtime_reason_paused) {
|
||||
if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "reason_paused", S_OR(reason, ""), "paused", paused ? "1" : "0", SENTINEL) < 0) {
|
||||
ast_log(LOG_WARNING, "Failed update of realtime queue member %s:%s %spause and reason '%s'\n",
|
||||
q->name, mem->interface, (paused ? "" : "un"), S_OR(reason, ""));
|
||||
}
|
||||
} else {
|
||||
if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "paused", paused ? "1" : "0", SENTINEL) < 0) {
|
||||
ast_log(LOG_WARNING, "Failed %spause update of realtime queue member %s:%s\n",
|
||||
(paused ? "" : "un"), q->name, mem->interface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7754,6 +7894,9 @@ static void set_queue_member_pause(struct call_queue *q, struct member *mem, con
|
|||
if (paused && !ast_strlen_zero(reason)) {
|
||||
ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
|
||||
} else {
|
||||
/* We end up filling this in again later (temporarily) but we need it
|
||||
* empty for now so that the intervening code - specifically
|
||||
* dump_queue_members() - has the correct view of things. */
|
||||
mem->reason_paused[0] = '\0';
|
||||
}
|
||||
|
||||
|
@ -7772,10 +7915,22 @@ static void set_queue_member_pause(struct call_queue *q, struct member *mem, con
|
|||
"Queue:%s_avail", q->name);
|
||||
}
|
||||
|
||||
ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"),
|
||||
"%s", S_OR(reason, ""));
|
||||
if (!paused && !ast_strlen_zero(reason)) {
|
||||
/* Because we've been unpaused with a 'reason' we need to ensure that
|
||||
* that reason is emitted when the subsequent PauseQueueMember event
|
||||
* is raised. So temporarily set it on the member and clear it out
|
||||
* again right after. */
|
||||
ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
|
||||
}
|
||||
|
||||
ast_queue_log(q->name, "NONE", mem->membername, paused ? "PAUSE" : "UNPAUSE",
|
||||
"%s", mem->reason_paused);
|
||||
|
||||
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)
|
||||
|
@ -8172,7 +8327,7 @@ static int pqm_exec(struct ast_channel *chan, const char *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief UnPauseQueueMember application */
|
||||
/*! \brief UnpauseQueueMember application */
|
||||
static int upqm_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *parse;
|
||||
|
@ -8193,7 +8348,7 @@ static int upqm_exec(struct ast_channel *chan, const char *data)
|
|||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (ast_strlen_zero(args.interface)) {
|
||||
ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
|
||||
ast_log(LOG_WARNING, "Missing interface argument to UnpauseQueueMember ([queuename],interface[,options[,reason]])\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -8614,6 +8769,11 @@ static int queue_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
ast_assert(qe.parent != NULL);
|
||||
|
||||
if (qe.parent->periodicannouncestartdelay >= 0) {
|
||||
qe.last_periodic_announce_time += qe.parent->periodicannouncestartdelay;
|
||||
qe.last_periodic_announce_time -= qe.parent->periodicannouncefrequency;
|
||||
}
|
||||
|
||||
ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d",
|
||||
S_OR(args.url, ""),
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
|
||||
|
@ -9498,6 +9658,7 @@ static void queue_reset_global_params(void)
|
|||
shared_lastcall = 0;
|
||||
negative_penalty_invalid = 0;
|
||||
log_membername_as_agent = 0;
|
||||
force_longest_waiting_caller = 0;
|
||||
}
|
||||
|
||||
/*! Set the global queue parameters as defined in the "general" section of queues.conf */
|
||||
|
@ -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"))) {
|
||||
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
|
||||
|
@ -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)
|
||||
{
|
||||
const char *queuename, *caller, *priority_s;
|
||||
int priority = 0;
|
||||
const char *queuename, *caller, *priority_s, *immediate_s;
|
||||
int priority = 0, immediate = 0;
|
||||
|
||||
queuename = astman_get_header(m, "Queue");
|
||||
caller = astman_get_header(m, "Caller");
|
||||
priority_s = astman_get_header(m, "Priority");
|
||||
immediate_s = astman_get_header(m, "Immediate");
|
||||
|
||||
if (ast_strlen_zero(queuename)) {
|
||||
astman_send_error(s, m, "'Queue' not specified.");
|
||||
|
@ -10849,7 +11014,11 @@ static int manager_change_priority_caller_on_queue(struct mansession *s, const s
|
|||
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:
|
||||
astman_send_ack(s, m, "Priority change for caller on queue");
|
||||
break;
|
||||
|
@ -10910,7 +11079,7 @@ static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct as
|
|||
case CLI_INIT:
|
||||
e->command = "queue add member";
|
||||
e->usage =
|
||||
"Usage: queue add member <dial string> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
|
||||
"Usage: queue add member <dial string> to <queue> [penalty <penalty> [as <membername> [state_interface <interface>]]]\n"
|
||||
" Add a dial string (Such as a channel,e.g. SIP/6001) to a queue with optionally: a penalty, membername and a state_interface\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
|
@ -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)
|
||||
{
|
||||
const char *queuename, *caller;
|
||||
int priority;
|
||||
int priority, immediate = 0;
|
||||
char *res = CLI_FAILURE;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "queue priority caller";
|
||||
e->usage =
|
||||
"Usage: queue priority caller <channel> on <queue> to <priority>\n"
|
||||
" Change the priority of a channel on a queue.\n";
|
||||
"Usage: queue priority caller <channel> on <queue> to <priority> [immediate]\n"
|
||||
" Change the priority of a channel on a queue, optionally applying the change in relation to existing callers.\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (a->argc != 8) {
|
||||
if (a->argc < 8) {
|
||||
return CLI_SHOWUSAGE;
|
||||
} else if (strcmp(a->argv[4], "on")) {
|
||||
return CLI_SHOWUSAGE;
|
||||
|
@ -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) {
|
||||
ast_log (LOG_ERROR, "<priority> parameter must be an integer.\n");
|
||||
return CLI_SHOWUSAGE;
|
||||
} else if (a->argc == 9) {
|
||||
if (strcmp(a->argv[8], "immediate")) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
immediate = 1;
|
||||
}
|
||||
|
||||
caller = a->argv[3];
|
||||
queuename = a->argv[5];
|
||||
|
||||
switch (change_priority_caller_on_queue(queuename, caller, priority)) {
|
||||
switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
|
||||
case RES_OKAY:
|
||||
res = CLI_SUCCESS;
|
||||
break;
|
||||
|
@ -11693,11 +11867,13 @@ static int load_module(void)
|
|||
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
|
||||
* Necessary for supporting older setups.
|
||||
*
|
||||
* It also checks if 'reason_paused' exists in the realtime backend
|
||||
*/
|
||||
member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name LIKE", "%", SENTINEL);
|
||||
if (!member_config) {
|
||||
|
@ -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");
|
||||
realtime_ringinuse_field = "ringinuse";
|
||||
}
|
||||
|
||||
if (ast_variable_retrieve(member_config, NULL, "reason_paused")) {
|
||||
realtime_reason_paused = 1;
|
||||
}
|
||||
}
|
||||
ast_config_destroy(member_config);
|
||||
|
||||
|
|
|
@ -49,9 +49,15 @@
|
|||
name.</para>
|
||||
</parameter>
|
||||
<parameter name="filenames" argsep="&">
|
||||
<argument name="filename" required="true">
|
||||
<para>file(s) to play before reading digits or tone with option i</para>
|
||||
</argument>
|
||||
<para>Ampersand separated list of filenames to play before
|
||||
reading digits or tone with option <literal>i</literal>. If
|
||||
the filename is a relative filename (it does not begin with a
|
||||
slash), it will be searched for in the Asterisk sounds
|
||||
directory. If the filename is able to be parsed as a URL,
|
||||
Asterisk will download the file and then begin playback on
|
||||
it. To include a literal <literal>&</literal> in the URL
|
||||
you can enclose the URL in single quotes.</para>
|
||||
<argument name="filename" required="true" />
|
||||
<argument name="filename2" multiple="true" />
|
||||
</parameter>
|
||||
<parameter name="maxdigits">
|
||||
|
@ -85,6 +91,13 @@
|
|||
and you will need to rely on duration and max digits
|
||||
for ending input.</para>
|
||||
</option>
|
||||
<option name="e">
|
||||
<para>to read the terminator as the digit string if the
|
||||
only digit read is the terminator. This is for cases
|
||||
where the terminator is a valid digit, but only by itself.
|
||||
ie; <literal>1234</literal> and <literal>#</literal> are
|
||||
valid, but <literal>1234#</literal> is not.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
<parameter name="attempts">
|
||||
|
@ -125,6 +138,7 @@ enum read_option_flags {
|
|||
OPT_INDICATION = (1 << 1),
|
||||
OPT_NOANSWER = (1 << 2),
|
||||
OPT_TERMINATOR = (1 << 3),
|
||||
OPT_KEEP_TERMINATOR = (1 << 4),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -138,6 +152,7 @@ AST_APP_OPTIONS(read_app_options, {
|
|||
AST_APP_OPTION('i', OPT_INDICATION),
|
||||
AST_APP_OPTION('n', OPT_NOANSWER),
|
||||
AST_APP_OPTION_ARG('t', OPT_TERMINATOR, OPT_ARG_TERMINATOR),
|
||||
AST_APP_OPTION('e', OPT_KEEP_TERMINATOR),
|
||||
});
|
||||
|
||||
static char *app = "Read";
|
||||
|
@ -261,12 +276,20 @@ static int read_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
} else {
|
||||
res = ast_app_getdata_terminator(chan, arglist.filename, tmp, maxdigits, to, terminator);
|
||||
if (res == AST_GETDATA_COMPLETE || res == AST_GETDATA_EMPTY_END_TERMINATED)
|
||||
if (res == AST_GETDATA_COMPLETE) {
|
||||
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";
|
||||
else if (res == AST_GETDATA_INTERRUPTED)
|
||||
} else if (res == AST_GETDATA_INTERRUPTED) {
|
||||
status = "INTERRUPTED";
|
||||
}
|
||||
}
|
||||
if (res > -1) {
|
||||
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
|
||||
|
|
|
@ -57,6 +57,15 @@
|
|||
<parameter name="channel" required="false">
|
||||
<para>Channel where digits will be played</para>
|
||||
</parameter>
|
||||
<parameter name="options">
|
||||
<optionlist>
|
||||
<option name="a">
|
||||
<para>Answer the channel specified by the <literal>channel</literal>
|
||||
parameter if it is not already up. If no <literal>channel</literal>
|
||||
parameter is provided, the current channel will be answered.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>It will send all digits or terminate if it encounters an error.</para>
|
||||
|
@ -88,8 +97,38 @@
|
|||
<para>Plays a dtmf digit on the specified channel.</para>
|
||||
</description>
|
||||
</manager>
|
||||
<manager name="SendFlash" language="en_US">
|
||||
<synopsis>
|
||||
Send a hook flash on a specific channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<parameter name="Channel" required="true">
|
||||
<para>Channel name to send hook flash to.</para>
|
||||
</parameter>
|
||||
<parameter name="Receive" required="false">
|
||||
<para>Emulate receiving a hook flash on this channel instead of sending it out.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Sends a hook flash on the specified channel.</para>
|
||||
</description>
|
||||
</manager>
|
||||
***/
|
||||
|
||||
enum read_option_flags {
|
||||
OPT_ANSWER = (1 << 0),
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(senddtmf_app_options, {
|
||||
AST_APP_OPTION('a', OPT_ANSWER),
|
||||
});
|
||||
|
||||
enum {
|
||||
/* note: this entry _MUST_ be the last one in the enum */
|
||||
OPT_ARG_ARRAY_SIZE,
|
||||
};
|
||||
|
||||
static const char senddtmf_name[] = "SendDTMF";
|
||||
|
||||
static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
||||
|
@ -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_dest = chan;
|
||||
struct ast_channel *chan_autoservice = NULL;
|
||||
char *opt_args[OPT_ARG_ARRAY_SIZE];
|
||||
struct ast_flags flags = {0};
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(digits);
|
||||
AST_APP_ARG(dinterval);
|
||||
AST_APP_ARG(duration);
|
||||
AST_APP_ARG(channel);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(vdata)) {
|
||||
|
@ -136,6 +178,12 @@ static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
|||
chan_autoservice = chan;
|
||||
}
|
||||
}
|
||||
if (!ast_strlen_zero(args.options)) {
|
||||
ast_app_parse_options(senddtmf_app_options, &flags, opt_args, args.options);
|
||||
}
|
||||
if (ast_test_flag(&flags, OPT_ANSWER)) {
|
||||
ast_auto_answer(chan_dest);
|
||||
}
|
||||
res = ast_dtmf_stream(chan_dest, chan_autoservice, args.digits,
|
||||
dinterval <= 0 ? 250 : dinterval, duration);
|
||||
if (chan_found) {
|
||||
|
@ -187,12 +235,41 @@ static int manager_play_dtmf(struct mansession *s, const struct message *m)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int manager_send_flash(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *channel = astman_get_header(m, "Channel");
|
||||
const char *receive_s = astman_get_header(m, "Receive");
|
||||
struct ast_channel *chan;
|
||||
|
||||
if (!(chan = ast_channel_get_by_name(channel))) {
|
||||
astman_send_error(s, m, "Channel not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ast_true(receive_s)) {
|
||||
struct ast_frame f = { AST_FRAME_CONTROL, };
|
||||
f.subclass.integer = AST_CONTROL_FLASH;
|
||||
ast_queue_frame(chan, &f);
|
||||
} else {
|
||||
struct ast_frame f = { AST_FRAME_CONTROL, };
|
||||
f.subclass.integer = AST_CONTROL_FLASH;
|
||||
ast_channel_lock(chan);
|
||||
ast_write(chan, &f);
|
||||
ast_channel_unlock(chan);
|
||||
}
|
||||
|
||||
chan = ast_channel_unref(chan);
|
||||
astman_send_ack(s, m, "Flash successfully queued");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(senddtmf_name);
|
||||
res |= ast_manager_unregister("PlayDTMF");
|
||||
res |= ast_manager_unregister("SendFlash");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -202,6 +279,7 @@ static int load_module(void)
|
|||
int res;
|
||||
|
||||
res = ast_manager_register_xml("PlayDTMF", EVENT_FLAG_CALL, manager_play_dtmf);
|
||||
res |= ast_manager_register_xml("SendFlash", EVENT_FLAG_CALL, manager_send_flash);
|
||||
res |= ast_register_application_xml(senddtmf_name, senddtmf_exec);
|
||||
|
||||
return res;
|
||||
|
|
|
@ -139,8 +139,6 @@
|
|||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">SendImage</ref>
|
||||
<ref type="application">SendURL</ref>
|
||||
<ref type="application">ReceiveText</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
|
@ -182,8 +180,6 @@
|
|||
</description>
|
||||
<see-also>
|
||||
<ref type="application">SendText</ref>
|
||||
<ref type="application">SendImage</ref>
|
||||
<ref type="application">SendURL</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
|
|
@ -0,0 +1,471 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2022, Naveen Albert
|
||||
*
|
||||
* Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Channel signaling applications
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/module.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="Signal" language="en_US">
|
||||
<synopsis>
|
||||
Sends a signal to any waiting channels.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="signalname" required="true">
|
||||
<para>Name of signal to send.</para>
|
||||
</parameter>
|
||||
<parameter name="payload" required="false">
|
||||
<para>Payload data to deliver.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Sends a named signal to any channels that may be
|
||||
waiting for one. Acts as a producer in a simple
|
||||
message queue.</para>
|
||||
<variablelist>
|
||||
<variable name="SIGNALSTATUS">
|
||||
<value name="SUCCESS">
|
||||
Signal was successfully sent to at least
|
||||
one listener for processing.
|
||||
</value>
|
||||
<value name="FAILURE">
|
||||
Signal could not be sent or nobody
|
||||
was listening for this signal.
|
||||
</value>
|
||||
</variable>
|
||||
</variablelist>
|
||||
<example title="Send a signal named workdone">
|
||||
same => n,Signal(workdone,Work has completed)
|
||||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">WaitForSignal</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="WaitForSignal" language="en_US">
|
||||
<synopsis>
|
||||
Waits for a named signal on a channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="signalname" required="true">
|
||||
<para>Name of signal to send.</para>
|
||||
</parameter>
|
||||
<parameter name="signaltimeout" required="false">
|
||||
<para>Maximum time, in seconds, to wait for signal.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Waits for <replaceable>signaltimeout</replaceable> seconds on the current
|
||||
channel to receive a signal with name <replaceable>signalname</replaceable>.
|
||||
Acts as a consumer in a simple message queue.</para>
|
||||
<para>Result of signal wait will be stored in the following variables:</para>
|
||||
<variablelist>
|
||||
<variable name="WAITFORSIGNALSTATUS">
|
||||
<value name="SIGNALED">
|
||||
Signal was received.
|
||||
</value>
|
||||
<value name="TIMEOUT">
|
||||
Timed out waiting for signal.
|
||||
</value>
|
||||
<value name="HANGUP">
|
||||
Channel hung up before signal was received.
|
||||
</value>
|
||||
</variable>
|
||||
<variable name="WAITFORSIGNALPAYLOAD">
|
||||
<para>Data payload attached to signal, if it exists</para>
|
||||
</variable>
|
||||
</variablelist>
|
||||
<example title="Wait for the workdone signal, indefinitely, and print out payload">
|
||||
same => n,WaitForSignal(workdone)
|
||||
same => n,NoOp(Received: ${WAITFORSIGNALPAYLOAD})
|
||||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">Signal</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static const char * const app = "Signal";
|
||||
static const char * const app2 = "WaitForSignal";
|
||||
|
||||
struct signalitem {
|
||||
ast_mutex_t lock;
|
||||
char name[AST_MAX_CONTEXT];
|
||||
int sig_alert_pipe[2];
|
||||
int watchers;
|
||||
unsigned int signaled:1;
|
||||
char *payload;
|
||||
AST_LIST_ENTRY(signalitem) entry; /*!< Next Signal item */
|
||||
};
|
||||
|
||||
static AST_RWLIST_HEAD_STATIC(signals, signalitem);
|
||||
|
||||
static struct signalitem *alloc_signal(const char *sname)
|
||||
{
|
||||
struct signalitem *s;
|
||||
|
||||
if (!(s = ast_calloc(1, sizeof(*s)))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_mutex_init(&s->lock);
|
||||
ast_copy_string(s->name, sname, sizeof(s->name));
|
||||
|
||||
s->sig_alert_pipe[0] = -1;
|
||||
s->sig_alert_pipe[1] = -1;
|
||||
s->watchers = 0;
|
||||
s->payload = NULL;
|
||||
ast_alertpipe_init(s->sig_alert_pipe);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int dealloc_signal(struct signalitem *s)
|
||||
{
|
||||
if (s->watchers) { /* somebody is still using us... refuse to go away */
|
||||
ast_debug(1, "Signal '%s' is still being used by %d listener(s)\n", s->name, s->watchers);
|
||||
return -1;
|
||||
}
|
||||
ast_alertpipe_close(s->sig_alert_pipe);
|
||||
ast_mutex_destroy(&s->lock);
|
||||
if (s->payload) {
|
||||
ast_free(s->payload);
|
||||
s->payload = NULL;
|
||||
}
|
||||
ast_free(s);
|
||||
s = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_signal(char *sname)
|
||||
{
|
||||
int res = -1;
|
||||
struct signalitem *s;
|
||||
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&signals, s, entry) {
|
||||
if (!strcmp(s->name, sname)) {
|
||||
AST_LIST_REMOVE_CURRENT(entry);
|
||||
res = dealloc_signal(s);
|
||||
ast_debug(1, "Removed signal '%s'\n", sname);
|
||||
}
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct signalitem *get_signal(char *sname, int addnew)
|
||||
{
|
||||
struct signalitem *s = NULL;
|
||||
AST_RWLIST_WRLOCK(&signals);
|
||||
AST_LIST_TRAVERSE(&signals, s, entry) {
|
||||
if (!strcasecmp(s->name, sname)) {
|
||||
ast_debug(1, "Using existing signal item '%s'\n", sname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!s) {
|
||||
if (addnew) { /* signal doesn't exist, so create it */
|
||||
s = alloc_signal(sname);
|
||||
/* Totally fail if we fail to find/create an entry */
|
||||
if (s) {
|
||||
ast_debug(1, "Created new signal item '%s'\n", sname);
|
||||
AST_RWLIST_INSERT_HEAD(&signals, s, entry);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Failed to create signal item for '%s'\n", sname);
|
||||
}
|
||||
} else {
|
||||
ast_debug(1, "Signal '%s' doesn't exist, and not creating it\n", sname);
|
||||
}
|
||||
}
|
||||
AST_RWLIST_UNLOCK(&signals);
|
||||
return s;
|
||||
}
|
||||
|
||||
static int wait_for_signal_or_hangup(struct ast_channel *chan, char *signame, int timeout)
|
||||
{
|
||||
struct signalitem *s = NULL;
|
||||
int ms, remaining_time, res = 1, goaway = 0;
|
||||
struct timeval start;
|
||||
struct ast_frame *frame = NULL;
|
||||
|
||||
remaining_time = timeout;
|
||||
start = ast_tvnow();
|
||||
|
||||
s = get_signal(signame, 1);
|
||||
|
||||
ast_mutex_lock(&s->lock);
|
||||
s->watchers = s->watchers + 1; /* we unlock, because a) other people need to use this and */
|
||||
ast_mutex_unlock(&s->lock); /* b) the signal will be available to us as long as watchers > 0 */
|
||||
|
||||
while (timeout == 0 || remaining_time > 0) {
|
||||
int ofd, exception;
|
||||
|
||||
ms = 1000;
|
||||
errno = 0;
|
||||
if (ast_waitfor_nandfds(&chan, 1, &s->sig_alert_pipe[0], 1, &exception, &ofd, &ms)) { /* channel won */
|
||||
if (!(frame = ast_read(chan))) { /* channel hung up */
|
||||
ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
|
||||
res = -1;
|
||||
break;
|
||||
} else {
|
||||
ast_frfree(frame); /* handle frames */
|
||||
}
|
||||
} else if (ofd == s->sig_alert_pipe[0]) { /* fd won */
|
||||
if (ast_alertpipe_read(s->sig_alert_pipe) == AST_ALERT_READ_SUCCESS) {
|
||||
ast_debug(1, "Alert pipe has data for us\n");
|
||||
res = 0;
|
||||
break;
|
||||
} else {
|
||||
ast_debug(1, "Alert pipe does not have data for us\n");
|
||||
}
|
||||
} else { /* nobody won */
|
||||
if (ms && (ofd < 0)) {
|
||||
if (!((errno == 0) || (errno == EINTR))) {
|
||||
ast_log(LOG_WARNING, "Something bad happened while channel '%s' was polling.\n", ast_channel_name(chan));
|
||||
break;
|
||||
}
|
||||
} /* else, nothing happened */
|
||||
}
|
||||
if (timeout) {
|
||||
remaining_time = ast_remaining_ms(start, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/* WRLOCK the list so that if we're going to destroy the signal now, nobody else can grab it before that happens. */
|
||||
AST_RWLIST_WRLOCK(&signals);
|
||||
ast_mutex_lock(&s->lock);
|
||||
if (s->payload) {
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALPAYLOAD", s->payload);
|
||||
}
|
||||
s->watchers = s->watchers - 1;
|
||||
if (s->watchers) { /* folks are still waiting for this, pass it on... */
|
||||
int save_errno = errno;
|
||||
if (ast_alertpipe_write(s->sig_alert_pipe)) {
|
||||
ast_log(LOG_WARNING, "%s: write() failed: %s\n", __FUNCTION__, strerror(errno));
|
||||
}
|
||||
errno = save_errno;
|
||||
} else { /* nobody else is waiting for this */
|
||||
goaway = 1; /* we were the last guy using this, so mark signal item for destruction */
|
||||
}
|
||||
ast_mutex_unlock(&s->lock);
|
||||
|
||||
if (goaway) {
|
||||
/* remove_signal calls ast_mutex_destroy, so don't call it with the mutex itself locked. */
|
||||
remove_signal(signame);
|
||||
}
|
||||
AST_RWLIST_UNLOCK(&signals);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int send_signal(char *signame, char *payload)
|
||||
{
|
||||
struct signalitem *s;
|
||||
int save_errno = errno;
|
||||
int res = 0;
|
||||
|
||||
s = get_signal(signame, 0); /* if signal doesn't exist already, no point in creating it, because nobody could be waiting for it! */
|
||||
|
||||
if (!s) {
|
||||
return -1; /* this signal didn't exist, so we can't send a signal for it */
|
||||
}
|
||||
|
||||
/* at this point, we know someone is listening, since signals are destroyed when watchers gets down to 0 */
|
||||
ast_mutex_lock(&s->lock);
|
||||
s->signaled = 1;
|
||||
if (payload && *payload) {
|
||||
int len = strlen(payload);
|
||||
if (s->payload) {
|
||||
ast_free(s->payload); /* if there was already a payload, replace it */
|
||||
s->payload = NULL;
|
||||
}
|
||||
s->payload = ast_malloc(len + 1);
|
||||
if (!s->payload) {
|
||||
ast_log(LOG_WARNING, "Failed to allocate signal payload '%s'\n", payload);
|
||||
} else {
|
||||
ast_copy_string(s->payload, payload, len + 1);
|
||||
}
|
||||
}
|
||||
if (ast_alertpipe_write(s->sig_alert_pipe)) {
|
||||
ast_log(LOG_WARNING, "%s: write() failed: %s\n", __FUNCTION__, strerror(errno));
|
||||
s->signaled = 0; /* okay, so we didn't send a signal after all... */
|
||||
res = -1;
|
||||
}
|
||||
errno = save_errno;
|
||||
ast_debug(1, "Sent '%s' signal to %d listeners\n", signame, s->watchers);
|
||||
ast_mutex_unlock(&s->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int waitsignal_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *argcopy;
|
||||
int r = 0, timeoutms = 0;
|
||||
double timeout = 0;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(signame);
|
||||
AST_APP_ARG(sigtimeout);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Signal() requires arguments\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argcopy = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, argcopy);
|
||||
|
||||
if (ast_strlen_zero(args.signame)) {
|
||||
ast_log(LOG_WARNING, "Missing signal name\n");
|
||||
return -1;
|
||||
}
|
||||
if (strlen(args.signame) >= AST_MAX_CONTEXT) {
|
||||
ast_log(LOG_WARNING, "Signal name '%s' is too long\n", args.signame);
|
||||
return -1;
|
||||
}
|
||||
if (!ast_strlen_zero(args.sigtimeout)) {
|
||||
if (sscanf(args.sigtimeout, "%30lg", &timeout) != 1 || timeout < 0) {
|
||||
ast_log(LOG_WARNING, "Invalid timeout provided: %s. Defaulting to no timeout.\n", args.sigtimeout);
|
||||
} else {
|
||||
timeoutms = timeout * 1000; /* sec to msec */
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
ast_debug(1, "Waiting for signal '%s' for %d ms\n", args.signame, timeoutms);
|
||||
} else {
|
||||
ast_debug(1, "Waiting for signal '%s', indefinitely\n", args.signame);
|
||||
}
|
||||
|
||||
r = wait_for_signal_or_hangup(chan, args.signame, timeoutms);
|
||||
|
||||
if (r == 1) {
|
||||
ast_verb(3, "Channel '%s' timed out, waiting for signal '%s'\n", ast_channel_name(chan), args.signame);
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "TIMEOUT");
|
||||
} else if (!r) {
|
||||
ast_verb(3, "Received signal '%s' on channel '%s'\n", args.signame, ast_channel_name(chan));
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "SIGNALED");
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "HANGUP");
|
||||
ast_verb(3, "Channel '%s' hung up\n", ast_channel_name(chan));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int signal_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *argcopy;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(signame);
|
||||
AST_APP_ARG(payload);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Signal() requires arguments\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argcopy = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, argcopy);
|
||||
|
||||
if (ast_strlen_zero(args.signame)) {
|
||||
ast_log(LOG_WARNING, "Missing signal name\n");
|
||||
return -1;
|
||||
}
|
||||
if (strlen(args.signame) >= AST_MAX_CONTEXT) {
|
||||
ast_log(LOG_WARNING, "Signal name '%s' is too long\n", args.signame);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (send_signal(args.signame, args.payload)) {
|
||||
pbx_builtin_setvar_helper(chan, "SIGNALSTATUS", "FAILURE");
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, "SIGNALSTATUS", "SUCCESS");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
struct signalitem *s;
|
||||
int res = 0;
|
||||
|
||||
/* To avoid a locking nightmare, and for logistical reasons, this module
|
||||
* will refuse to unload if watchers > 0. That way we know a signal's
|
||||
* pipe won't disappear while it's being used. */
|
||||
|
||||
AST_RWLIST_WRLOCK(&signals);
|
||||
/* Don't just use AST_RWLIST_REMOVE_HEAD, because if dealloc_signal fails, it should stay in the list. */
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&signals, s, entry) {
|
||||
int mres = dealloc_signal(s);
|
||||
res |= mres;
|
||||
if (!mres) {
|
||||
AST_LIST_REMOVE_CURRENT(entry);
|
||||
}
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&signals);
|
||||
|
||||
/* One or more signals still has watchers. */
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "One or more signals is currently in use. Unload failed.\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
res |= ast_unregister_application(app);
|
||||
res |= ast_unregister_application(app2);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application_xml(app, signal_exec);
|
||||
res |= ast_register_application_xml(app2, waitsignal_exec);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel Signaling Applications");
|
|
@ -16,7 +16,7 @@
|
|||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* https://wiki.asterisk.org/wiki/display/AST/Coding+Guidelines
|
||||
* https://docs.asterisk.org/Development/Policies-and-Procedures/Coding-Guidelines/
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
@ -371,7 +371,8 @@ static void play_files_helper(struct ast_channel *chan, const char *prompts)
|
|||
char *prompt, *rest = ast_strdupa(prompts);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,17 @@
|
|||
Play a sound file and wait for speech to be recognized.
|
||||
</synopsis>
|
||||
<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">
|
||||
<para>Timeout integer in seconds. Note the timeout will only start
|
||||
once the sound file has stopped playing.</para>
|
||||
|
@ -95,6 +105,9 @@
|
|||
<option name="n">
|
||||
<para>Don't answer the channel if it has not already been answered.</para>
|
||||
</option>
|
||||
<option name="p">
|
||||
<para>Return partial results when backend is terminated by timeout.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
|
@ -690,10 +703,12 @@ static int speech_streamfile(struct ast_channel *chan, const char *filename, con
|
|||
|
||||
enum {
|
||||
SB_OPT_NOANSWER = (1 << 0),
|
||||
SB_OPT_PARTIALRESULTS = (1 << 1),
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(speech_background_options, BEGIN_OPTIONS
|
||||
AST_APP_OPTION('n', SB_OPT_NOANSWER),
|
||||
AST_APP_OPTION('p', SB_OPT_PARTIALRESULTS),
|
||||
END_OPTIONS );
|
||||
|
||||
/*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
|
||||
|
@ -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! */
|
||||
while (done == 0) {
|
||||
/* If the filename is null and stream is not running, start up a new sound file */
|
||||
if (!quieted && (ast_channel_streamid(chan) == -1 && ast_channel_timingfunc(chan) == NULL) && (filename = 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 */
|
||||
ast_stopstream(chan);
|
||||
/* 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 */
|
||||
speech->results = ast_calloc(1, sizeof(*speech->results));
|
||||
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,
|
||||
S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n",
|
||||
ast_log(LOG_WARNING, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n",
|
||||
ast_channel_name(chan), app_gosub, sub_args);
|
||||
balance_stack(chan);
|
||||
pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -120,6 +120,9 @@
|
|||
<configOption name="end_marked">
|
||||
<synopsis>Kick the user from the conference when the last marked user leaves</synopsis>
|
||||
</configOption>
|
||||
<configOption name="end_marked_any">
|
||||
<synopsis>Kick the user from the conference when any marked user leaves</synopsis>
|
||||
</configOption>
|
||||
<configOption name="talk_detection_events">
|
||||
<synopsis>Set whether or not notifications of when a user begins and ends talking should be sent out as events over AMI</synopsis>
|
||||
</configOption>
|
||||
|
@ -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 (res) {
|
||||
struct conf_menu_action *menu_action;
|
||||
while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
|
||||
ast_free(menu_action);
|
||||
}
|
||||
conf_menu_entry_destroy(menu_entry);
|
||||
ast_free(menu_entry);
|
||||
return -1;
|
||||
}
|
||||
|
@ -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) {
|
||||
if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
|
||||
AST_LIST_REMOVE_CURRENT(entry);
|
||||
conf_menu_entry_destroy(cur);
|
||||
ast_free(cur);
|
||||
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",
|
||||
u_profile.flags & USER_OPT_WAITMARKED ?
|
||||
"enabled" : "disabled");
|
||||
ast_cli(a->fd,"END Marked: %s\n",
|
||||
ast_cli(a->fd,"END Marked (All): %s\n",
|
||||
u_profile.flags & USER_OPT_ENDMARKED ?
|
||||
"enabled" : "disabled");
|
||||
ast_cli(a->fd,"END Marked (Any): %s\n",
|
||||
u_profile.flags & USER_OPT_ENDMARKEDANY ?
|
||||
"enabled" : "disabled");
|
||||
ast_cli(a->fd,"Drop_silence: %s\n",
|
||||
u_profile.flags & USER_OPT_DROP_SILENCE ?
|
||||
"enabled" : "disabled");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
static int sample_rate_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct bridge_profile *b_profile = obj;
|
||||
unsigned int *slot;
|
||||
|
||||
if (!strcasecmp(var->name, "internal_sample_rate")) {
|
||||
slot = &b_profile->internal_sample_rate;
|
||||
if (!strcasecmp(var->value, "auto")) {
|
||||
*slot = 0;
|
||||
return 0;
|
||||
}
|
||||
} else if (!strcasecmp(var->name, "maximum_sample_rate")) {
|
||||
slot = &b_profile->maximum_sample_rate;
|
||||
if (!strcasecmp(var->value, "none")) {
|
||||
*slot = 0;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ast_parse_arg(var->value, PARSE_UINT32 | PARSE_IN_RANGE, slot, 8000, 192000);
|
||||
}
|
||||
|
||||
static int bridge_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct bridge_profile *b_profile = obj;
|
||||
|
@ -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, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED);
|
||||
aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED);
|
||||
aco_option_register(&cfg_info, "end_marked_any", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKEDANY);
|
||||
aco_option_register(&cfg_info, "talk_detection_events", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_TALKER_DETECT);
|
||||
aco_option_register(&cfg_info, "dtmf_passthrough", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DTMF_PASS);
|
||||
aco_option_register(&cfg_info, "announce_join_leave", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANNOUNCE_JOIN_LEAVE);
|
||||
|
@ -2432,10 +2461,9 @@ int conf_load_config(void)
|
|||
/* Bridge options */
|
||||
aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
|
||||
aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);
|
||||
/* "auto" will fail to parse as a uint, but we use PARSE_DEFAULT to set the value to 0 in that case, which is the value that auto resolves to */
|
||||
aco_option_register(&cfg_info, "internal_sample_rate", ACO_EXACT, bridge_types, "0", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct bridge_profile, internal_sample_rate), 0);
|
||||
aco_option_register_custom(&cfg_info, "internal_sample_rate", ACO_EXACT, bridge_types, "auto", sample_rate_handler, 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, "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(&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);
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
@ -85,37 +82,39 @@ static void leave_marked(struct confbridge_user *user)
|
|||
|
||||
conf_remove_user_marked(user->conference, user);
|
||||
|
||||
if (user->conference->markedusers == 0) {
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&user->conference->active_list, user_iter, list) {
|
||||
/* Kick ENDMARKED cbu_iters */
|
||||
if (ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKED) && !user_iter->kicked) {
|
||||
if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
AST_LIST_REMOVE_CURRENT(list);
|
||||
user_iter->conference->activeusers--;
|
||||
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
||||
user_iter->conference->waitingusers++;
|
||||
}
|
||||
user_iter->kicked = 1;
|
||||
pbx_builtin_setvar_helper(user_iter->chan, "CONFBRIDGE_RESULT", "ENDMARKED");
|
||||
ast_bridge_remove(user_iter->conference->bridge, user_iter->chan);
|
||||
} else if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
need_prompt = 1;
|
||||
|
||||
/* If all marked users have left, or we're set to kick if any marked user leaves, then boot everyone */
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&user->conference->active_list, user_iter, list) {
|
||||
if (user->conference->markedusers > 0 && !ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY)) {
|
||||
continue;
|
||||
}
|
||||
/* Kick ENDMARKED cbu_iters */
|
||||
if ((ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKED) || ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY)) && !user_iter->kicked) {
|
||||
if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& (!ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER) || ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY))) {
|
||||
AST_LIST_REMOVE_CURRENT(list);
|
||||
user_iter->conference->activeusers--;
|
||||
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
||||
user_iter->conference->waitingusers++;
|
||||
} else {
|
||||
/* User is neither wait_marked nor end_marked; however, they
|
||||
* should still hear the prompt.
|
||||
*/
|
||||
need_prompt = 1;
|
||||
}
|
||||
user_iter->kicked = 1;
|
||||
pbx_builtin_setvar_helper(user_iter->chan, "CONFBRIDGE_RESULT", "ENDMARKED");
|
||||
ast_bridge_remove(user_iter->conference->bridge, user_iter->chan);
|
||||
} else if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
need_prompt = 1;
|
||||
|
||||
AST_LIST_REMOVE_CURRENT(list);
|
||||
user_iter->conference->activeusers--;
|
||||
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
||||
user_iter->conference->waitingusers++;
|
||||
} else {
|
||||
/* User is neither wait_marked nor end_marked nor end_marked_any; however, they
|
||||
* should still hear the prompt.
|
||||
*/
|
||||
need_prompt = 1;
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
|
||||
switch (user->conference->activeusers) {
|
||||
case 0:
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
@ -25,7 +22,7 @@
|
|||
*
|
||||
* \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.
|
||||
*/
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ enum user_profile_flags {
|
|||
USER_OPT_TEXT_MESSAGING = (1 << 19), /*!< Send text messages to the user */
|
||||
USER_OPT_ANSWER_CHANNEL = (1 << 20), /*!< Sets if the channel should be answered if currently unanswered */
|
||||
USER_OPT_HEAR_OWN_JOIN_SOUND = (1 << 21), /*!< Set if the caller should hear the join sound */
|
||||
USER_OPT_ENDMARKEDANY = (1 << 22), /*!< Set if the user should be kicked after any marked user exits */
|
||||
};
|
||||
|
||||
enum bridge_profile_flags {
|
||||
|
|
|
@ -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
|
||||
|
@ -14,20 +14,24 @@
|
|||
# flags that are needed. (The user can also force certain compiler
|
||||
# flags/libs to be tested by setting these environment variables.)
|
||||
#
|
||||
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||
# multi-threaded programs (defaults to the value of CC otherwise). (This
|
||||
# is necessary on AIX to use the special cc_r compiler alias.)
|
||||
# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is
|
||||
# needed for multi-threaded programs (defaults to the value of CC
|
||||
# respectively CXX otherwise). (This is necessary on e.g. AIX to use the
|
||||
# special cc_r/CC_r compiler alias.)
|
||||
#
|
||||
# NOTE: You are assumed to not only compile your program with these flags,
|
||||
# but also to link with them as well. For example, you might link with
|
||||
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
#
|
||||
# If you are only building threaded programs, you may wish to use these
|
||||
# variables in your default LIBS, CFLAGS, and CC:
|
||||
#
|
||||
# LIBS="$PTHREAD_LIBS $LIBS"
|
||||
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
|
||||
# CC="$PTHREAD_CC"
|
||||
# CXX="$PTHREAD_CXX"
|
||||
#
|
||||
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
||||
# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
|
||||
|
@ -55,6 +59,7 @@
|
|||
#
|
||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
|
||||
# Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
|
@ -67,7 +72,7 @@
|
|||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <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
|
||||
# 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
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 23
|
||||
#serial 31
|
||||
|
||||
AU_ALIAS([ACX_PTHREAD], [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_LIBS="$LIBS"
|
||||
AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
|
||||
AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"])
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
|
||||
|
@ -123,10 +129,12 @@ fi
|
|||
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
||||
# libraries is broken (non-POSIX).
|
||||
|
||||
# Create a list of thread flags to try. Items starting with a "-" are
|
||||
# C compiler flags, and other items are library names, except for "none"
|
||||
# which indicates that we try without any flags at all, and "pthread-config"
|
||||
# which is a program returning the flags for the Pth emulation library.
|
||||
# Create a list of thread flags to try. Items with a "," contain both
|
||||
# C compiler flags (before ",") and linker flags (after ","). Other items
|
||||
# starting with a "-" are C compiler flags, and remaining items are
|
||||
# library names, except for "none" which indicates that we try without
|
||||
# any flags at all, and "pthread-config" which is a program returning
|
||||
# the flags for the Pth emulation library.
|
||||
|
||||
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
|
||||
# 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
|
||||
|
||||
# Are we compiling with Clang?
|
||||
|
||||
AC_CACHE_CHECK([whether $CC is Clang],
|
||||
[ax_cv_PTHREAD_CLANG],
|
||||
[ax_cv_PTHREAD_CLANG=no
|
||||
# Note that Autoconf sets GCC=yes for Clang as well as GCC
|
||||
if test "x$GCC" = "xyes"; then
|
||||
AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
|
||||
[/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
|
||||
# if defined(__clang__) && defined(__llvm__)
|
||||
AX_PTHREAD_CC_IS_CLANG
|
||||
# endif
|
||||
],
|
||||
[ax_cv_PTHREAD_CLANG=yes])
|
||||
fi
|
||||
])
|
||||
ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
|
||||
|
||||
|
||||
# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
|
||||
|
||||
# Note that for GCC and Clang -pthread generally implies -lpthread,
|
||||
# except when -nostdlib is passed.
|
||||
# This is problematic using libtool to build C++ shared libraries with pthread:
|
||||
# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460
|
||||
# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333
|
||||
# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555
|
||||
# To solve this, first try -pthread together with -lpthread for GCC
|
||||
|
||||
AS_IF([test "x$GCC" = "xyes"],
|
||||
[ax_pthread_flags="-pthread -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
|
||||
# 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="!defined($ax_pthread_check_macro)"])
|
||||
|
||||
# Are we compiling with Clang?
|
||||
|
||||
AC_CACHE_CHECK([whether $CC is Clang],
|
||||
[ax_cv_PTHREAD_CLANG],
|
||||
[ax_cv_PTHREAD_CLANG=no
|
||||
# Note that Autoconf sets GCC=yes for Clang as well as GCC
|
||||
if test "x$GCC" = "xyes"; then
|
||||
AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
|
||||
[/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
|
||||
# if defined(__clang__) && defined(__llvm__)
|
||||
AX_PTHREAD_CC_IS_CLANG
|
||||
# endif
|
||||
],
|
||||
[ax_cv_PTHREAD_CLANG=yes])
|
||||
fi
|
||||
])
|
||||
ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
|
||||
|
||||
ax_pthread_clang_warning=no
|
||||
|
||||
# Clang needs special handling, because older versions handle the -pthread
|
||||
# option in a rather... idiosyncratic way
|
||||
|
||||
if test "x$ax_pthread_clang" = "xyes"; then
|
||||
|
||||
# Clang takes -pthread; it has never supported any other flag
|
||||
|
||||
# (Note 1: This will need to be revisited if a system that Clang
|
||||
# supports has POSIX threads in a separate library. This tends not
|
||||
# to be the way of modern systems, but it's conceivable.)
|
||||
|
||||
# (Note 2: On some systems, notably Darwin, -pthread is not needed
|
||||
# to get POSIX threads support; the API is always present and
|
||||
# active. We could reasonably leave PTHREAD_CFLAGS empty. But
|
||||
# -pthread does define _REENTRANT, and while the Darwin headers
|
||||
# ignore this macro, third-party headers might not.)
|
||||
|
||||
PTHREAD_CFLAGS="-pthread"
|
||||
PTHREAD_LIBS=
|
||||
|
||||
ax_pthread_ok=yes
|
||||
|
||||
# However, older versions of Clang make a point of warning the user
|
||||
# that, in an invocation where only linking and no compilation is
|
||||
# taking place, the -pthread option has no effect ("argument unused
|
||||
# during compilation"). They expect -pthread to be passed in only
|
||||
# when source code is being compiled.
|
||||
#
|
||||
# Problem is, this is at odds with the way Automake and most other
|
||||
# C build frameworks function, which is that the same flags used in
|
||||
# compilation (CFLAGS) are also used in linking. Many systems
|
||||
# supported by AX_PTHREAD require exactly this for POSIX threads
|
||||
# support, and in fact it is often not straightforward to specify a
|
||||
# flag that is used only in the compilation phase and not in
|
||||
# linking. Such a scenario is extremely rare in practice.
|
||||
#
|
||||
# Even though use of the -pthread flag in linking would only print
|
||||
# a warning, this can be a nuisance for well-run software projects
|
||||
# that build with -Werror. So if the active version of Clang has
|
||||
# this misfeature, we search for an option to squash it.
|
||||
|
||||
AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
|
||||
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
|
||||
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
|
||||
# Create an alternate version of $ac_link that compiles and
|
||||
# links in two steps (.c -> .o, .o -> exe) instead of one
|
||||
# (.c -> exe), because the warning occurs only in the second
|
||||
# step
|
||||
ax_pthread_save_ac_link="$ac_link"
|
||||
ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
|
||||
ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"`
|
||||
ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
|
||||
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||
for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
|
||||
AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
|
||||
CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
|
||||
ac_link="$ax_pthread_save_ac_link"
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
|
||||
[ac_link="$ax_pthread_2step_ac_link"
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
|
||||
[break])
|
||||
])
|
||||
done
|
||||
ac_link="$ax_pthread_save_ac_link"
|
||||
CFLAGS="$ax_pthread_save_CFLAGS"
|
||||
AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
|
||||
ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
|
||||
])
|
||||
|
||||
case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
|
||||
no | unknown) ;;
|
||||
*) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
|
||||
esac
|
||||
|
||||
fi # $ax_pthread_clang = yes
|
||||
|
||||
if test "x$ax_pthread_ok" = "xno"; then
|
||||
for ax_pthread_try_flag in $ax_pthread_flags; do
|
||||
|
@ -328,10 +274,10 @@ for ax_pthread_try_flag in $ax_pthread_flags; do
|
|||
AC_MSG_CHECKING([whether pthreads work without any flags])
|
||||
;;
|
||||
|
||||
-mt,pthread)
|
||||
AC_MSG_CHECKING([whether pthreads work with -mt -lpthread])
|
||||
PTHREAD_CFLAGS="-mt"
|
||||
PTHREAD_LIBS="-lpthread"
|
||||
*,*)
|
||||
PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"`
|
||||
PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"`
|
||||
AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"])
|
||||
;;
|
||||
|
||||
-*)
|
||||
|
@ -371,7 +317,13 @@ for ax_pthread_try_flag in $ax_pthread_flags; do
|
|||
# if $ax_pthread_check_cond
|
||||
# error "$ax_pthread_check_macro must be defined"
|
||||
# endif
|
||||
static void 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; }],
|
||||
[pthread_t th; pthread_attr_t attr;
|
||||
pthread_create(&th, 0, start_routine, 0);
|
||||
|
@ -393,6 +345,80 @@ for ax_pthread_try_flag in $ax_pthread_flags; do
|
|||
done
|
||||
fi
|
||||
|
||||
|
||||
# Clang needs special handling, because older versions handle the -pthread
|
||||
# option in a rather... idiosyncratic way
|
||||
|
||||
if test "x$ax_pthread_clang" = "xyes"; then
|
||||
|
||||
# Clang takes -pthread; it has never supported any other flag
|
||||
|
||||
# (Note 1: This will need to be revisited if a system that Clang
|
||||
# supports has POSIX threads in a separate library. This tends not
|
||||
# to be the way of modern systems, but it's conceivable.)
|
||||
|
||||
# (Note 2: On some systems, notably Darwin, -pthread is not needed
|
||||
# to get POSIX threads support; the API is always present and
|
||||
# active. We could reasonably leave PTHREAD_CFLAGS empty. But
|
||||
# -pthread does define _REENTRANT, and while the Darwin headers
|
||||
# ignore this macro, third-party headers might not.)
|
||||
|
||||
# However, older versions of Clang make a point of warning the user
|
||||
# that, in an invocation where only linking and no compilation is
|
||||
# taking place, the -pthread option has no effect ("argument unused
|
||||
# during compilation"). They expect -pthread to be passed in only
|
||||
# when source code is being compiled.
|
||||
#
|
||||
# Problem is, this is at odds with the way Automake and most other
|
||||
# C build frameworks function, which is that the same flags used in
|
||||
# compilation (CFLAGS) are also used in linking. Many systems
|
||||
# supported by AX_PTHREAD require exactly this for POSIX threads
|
||||
# support, and in fact it is often not straightforward to specify a
|
||||
# flag that is used only in the compilation phase and not in
|
||||
# linking. Such a scenario is extremely rare in practice.
|
||||
#
|
||||
# Even though use of the -pthread flag in linking would only print
|
||||
# a warning, this can be a nuisance for well-run software projects
|
||||
# that build with -Werror. So if the active version of Clang has
|
||||
# this misfeature, we search for an option to squash it.
|
||||
|
||||
AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
|
||||
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
|
||||
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
|
||||
# Create an alternate version of $ac_link that compiles and
|
||||
# links in two steps (.c -> .o, .o -> exe) instead of one
|
||||
# (.c -> exe), because the warning occurs only in the second
|
||||
# step
|
||||
ax_pthread_save_ac_link="$ac_link"
|
||||
ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
|
||||
ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"`
|
||||
ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
|
||||
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||
for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
|
||||
AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
|
||||
CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
|
||||
ac_link="$ax_pthread_save_ac_link"
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
|
||||
[ac_link="$ax_pthread_2step_ac_link"
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
|
||||
[break])
|
||||
])
|
||||
done
|
||||
ac_link="$ax_pthread_save_ac_link"
|
||||
CFLAGS="$ax_pthread_save_CFLAGS"
|
||||
AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
|
||||
ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
|
||||
])
|
||||
|
||||
case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
|
||||
no | unknown) ;;
|
||||
*) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
|
||||
esac
|
||||
|
||||
fi # $ax_pthread_clang = yes
|
||||
|
||||
|
||||
|
||||
# Various other checks:
|
||||
if test "x$ax_pthread_ok" = "xyes"; then
|
||||
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||
|
@ -438,7 +464,8 @@ if test "x$ax_pthread_ok" = "xyes"; then
|
|||
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
|
||||
[ax_cv_PTHREAD_PRIO_INHERIT],
|
||||
[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
|
||||
[[int i = PTHREAD_PRIO_INHERIT;]])],
|
||||
[[int i = PTHREAD_PRIO_INHERIT;
|
||||
return i;]])],
|
||||
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
|
||||
[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
|
||||
AS_CASE(["x$CC"],
|
||||
[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
|
||||
fi
|
||||
fi
|
||||
|
||||
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
|
||||
test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX"
|
||||
|
||||
AC_SUBST([PTHREAD_LIBS])
|
||||
AC_SUBST([PTHREAD_CFLAGS])
|
||||
AC_SUBST([PTHREAD_CC])
|
||||
AC_SUBST([PTHREAD_CXX])
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test "x$ax_pthread_ok" = "xyes"; then
|
||||
|
|
|
@ -9,9 +9,9 @@ check_for_app() {
|
|||
fi
|
||||
}
|
||||
|
||||
# OpenBSD: pkg_add autoconf%2.63 automake%1.9 metaauto
|
||||
test -n "$AUTOCONF_VERSION" || export AUTOCONF_VERSION=2.63
|
||||
test -n "$AUTOMAKE_VERSION" || export AUTOMAKE_VERSION=1.9
|
||||
# OpenBSD: pkg_add autoconf%2.69 automake%1.16 metaauto
|
||||
test -n "$AUTOCONF_VERSION" || export AUTOCONF_VERSION=2.69
|
||||
test -n "$AUTOMAKE_VERSION" || export AUTOMAKE_VERSION=1.16
|
||||
|
||||
check_for_app autoconf
|
||||
check_for_app autoheader
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
#include "asterisk/mixmonitor.h"
|
||||
#include "asterisk/audiohook.h"
|
||||
#include "asterisk/causes.h"
|
||||
#include "asterisk/beep.h"
|
||||
|
||||
enum set_touch_variables_res {
|
||||
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;
|
||||
const char *var_format;
|
||||
const char *var_monitor;
|
||||
const char *var_prefix;
|
||||
const char *var_beep;
|
||||
|
||||
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_monitor = "TOUCH_MIXMONITOR";
|
||||
var_prefix = "TOUCH_MIXMONITOR_PREFIX";
|
||||
var_beep = "TOUCH_MIXMONITOR_BEEP";
|
||||
} else {
|
||||
var_format = "TOUCH_MONITOR_FORMAT";
|
||||
var_monitor = "TOUCH_MONITOR";
|
||||
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_monitor, touch_monitor);
|
||||
set_touch_variable(&res, chan, var_prefix, touch_monitor_prefix);
|
||||
set_touch_variable(&res, chan, var_beep, touch_monitor_beep);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -141,20 +146,22 @@ static void start_automonitor(struct ast_bridge_channel *bridge_channel, struct
|
|||
char *touch_filename;
|
||||
size_t len;
|
||||
int x;
|
||||
char beep_id[64] = "";
|
||||
enum set_touch_variables_res set_touch_res;
|
||||
|
||||
RAII_VAR(char *, touch_format, NULL, ast_free);
|
||||
RAII_VAR(char *, touch_monitor, NULL, ast_free);
|
||||
RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
|
||||
RAII_VAR(char *, touch_monitor_beep, NULL, ast_free);
|
||||
|
||||
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) {
|
||||
case SET_TOUCH_SUCCESS:
|
||||
break;
|
||||
case SET_TOUCH_UNSET:
|
||||
set_touch_res = set_touch_variables(peer_chan, 0, &touch_format, &touch_monitor,
|
||||
&touch_monitor_prefix);
|
||||
&touch_monitor_prefix, &touch_monitor_beep);
|
||||
if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
|
||||
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);
|
||||
|
||||
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_channel_name(bridge_channel->chan));
|
||||
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)
|
||||
{
|
||||
char *touch_filename;
|
||||
char *touch_filename, mix_options[32] = "b";
|
||||
size_t len;
|
||||
int x;
|
||||
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_monitor, NULL, ast_free);
|
||||
RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
|
||||
RAII_VAR(char *, touch_monitor_beep, NULL, ast_free);
|
||||
|
||||
set_touch_res = set_touch_variables(bridge_channel->chan, 1, &touch_format,
|
||||
&touch_monitor, &touch_monitor_prefix);
|
||||
&touch_monitor, &touch_monitor_prefix, &touch_monitor_beep);
|
||||
switch (set_touch_res) {
|
||||
case SET_TOUCH_SUCCESS:
|
||||
break;
|
||||
case SET_TOUCH_UNSET:
|
||||
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) {
|
||||
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);
|
||||
|
||||
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_channel_name(bridge_channel->chan));
|
||||
|
||||
|
|
|
@ -181,7 +181,14 @@ static int simple_bridge_join(struct ast_bridge *bridge, struct ast_bridge_chann
|
|||
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);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
<support_level>extended</support_level>
|
||||
</member>
|
||||
<member name="DETECT_DEADLOCKS" displayname="Detect Deadlocks">
|
||||
<depend>DEBUG_THREADS</depend>
|
||||
<support_level>extended</support_level>
|
||||
</member>
|
||||
<member name="DUMP_SCHEDULER" displayname="Dump Scheduler Contents for Debugging">
|
||||
|
|
|
@ -128,4 +128,8 @@
|
|||
<defaultenabled>yes</defaultenabled>
|
||||
<depend>native_arch</depend>
|
||||
</member>
|
||||
<member name="ADD_CFLAGS_TO_BUILDOPTS_H" displayname="Add ALL of the flags on this page to buildopts.h. Useful for IDEs but may cause slightly longer compile times after flags are changed.">
|
||||
<support_level>core</support_level>
|
||||
<defaultenabled>no</defaultenabled>
|
||||
</member>
|
||||
</category>
|
||||
|
|
|
@ -58,7 +58,7 @@ if [[ -z ${cache_dir} ]] ; then
|
|||
fi
|
||||
|
||||
version=$(${ASTTOPDIR}/build_tools/make_version ${ASTTOPDIR})
|
||||
if [[ ! ${version} =~ ^(GIT-)?(certified/)?([^.-]+)[.-].* ]] ; then
|
||||
if [[ ! ${version} =~ ^(GIT-)?(certified[/-])?([^.-]+)[.-].* ]] ; then
|
||||
echo "${module_name}: Couldn't parse version ${version}"
|
||||
exit 1
|
||||
fi
|
||||
|
@ -172,7 +172,7 @@ if [[ -f ${DESTDIR}${ASTMODDIR}/${module_name}.manifest.xml ]] ; then
|
|||
|
||||
cs=$(${MD5} ${f} | cut -b1-32)
|
||||
if [[ "${cs}" != "${sum}" ]] ; then
|
||||
echo Checksum mismatch: ${f}
|
||||
echo "Checksum mismatch: ${f}"
|
||||
need_install=1
|
||||
break
|
||||
fi
|
||||
|
@ -194,8 +194,8 @@ else
|
|||
fi
|
||||
|
||||
need_download=1
|
||||
if [[ -f ${cache_dir}/${full_name}.manifest.xml ]] ; then
|
||||
cpv=$(${XMLSTARLET} sel -t -v "/package/@version" ${cache_dir}/${full_name}.manifest.xml)
|
||||
if [[ -f ${cache_dir}/${full_name}-${major_version}.manifest.xml ]] ; then
|
||||
cpv=$(${XMLSTARLET} sel -t -v "/package/@version" ${cache_dir}/${full_name}-${major_version}.manifest.xml)
|
||||
cpvi=$(version_convert ${cpv})
|
||||
echo "${full_name}: Cached package version ${cpv} (${cpvi})"
|
||||
if [[ ${cpvi} == ${rpvi} && ( -f ${cache_dir}/${tarball} ) ]] ; then
|
||||
|
@ -210,7 +210,7 @@ if [[ ${need_download} = 1 ]] ; then
|
|||
echo "${full_name}: Unable to fetch ${remote_url}/${tarball}"
|
||||
exit 1
|
||||
}
|
||||
cp ${tmpdir}/${variant_manifest} ${cache_dir}/${full_name}.manifest.xml
|
||||
cp ${tmpdir}/${variant_manifest} ${cache_dir}/${full_name}-${major_version}.manifest.xml
|
||||
fi
|
||||
|
||||
tar -xzf ${cache_dir}/${tarball} -C ${cache_dir}
|
||||
|
|
|
@ -18,42 +18,79 @@ then
|
|||
# gets added to BUILDOPTS.
|
||||
fi
|
||||
|
||||
TMP=`${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts | sed 's/MENUSELECT_CFLAGS\=//g' | sed 's/-D//g'`
|
||||
for x in ${TMP}; do
|
||||
if test "${x}" = "AO2_DEBUG" \
|
||||
-o "${x}" = "BETTER_BACKTRACES" \
|
||||
-o "${x}" = "BUILD_NATIVE" \
|
||||
-o "${x}" = "COMPILE_DOUBLE" \
|
||||
-o "${x}" = "DEBUG_CHAOS" \
|
||||
-o "${x}" = "DEBUG_SCHEDULER" \
|
||||
-o "${x}" = "DETECT_DEADLOCKS" \
|
||||
-o "${x}" = "DONT_OPTIMIZE" \
|
||||
-o "${x}" = "DUMP_SCHEDULER" \
|
||||
-o "${x}" = "LOTS_OF_SPANS" \
|
||||
-o "${x}" = "MALLOC_DEBUG" \
|
||||
-o "${x}" = "RADIO_RELAX" \
|
||||
-o "${x}" = "REBUILD_PARSERS" \
|
||||
-o "${x}" = "REF_DEBUG" \
|
||||
-o "${x}" = "USE_HOARD_ALLOCATOR" ; then
|
||||
# These options are only for specific sources and have no effect on public ABI.
|
||||
# Keep them out of buildopts.h so ccache does not invalidate all sources.
|
||||
continue
|
||||
fi
|
||||
ADD_CFLAGS_TO_BUILDOPTS=false
|
||||
MENUSELECT_CFLAGS=$(${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts)
|
||||
echo "$MENUSELECT_CFLAGS" | grep -q -e "ADD_CFLAGS_TO_BUILDOPTS_H" && ADD_CFLAGS_TO_BUILDOPTS=true
|
||||
|
||||
# Clean up MENUSELECT_CFLAGS by removing the "MENUSELECT_CFLAGS="
|
||||
# at the front, the "ADD_CFLAGS_TO_BUILDOPTS_H" flag, and any "-D"
|
||||
# entries.
|
||||
MENUSELECT_CFLAGS=$( echo "$MENUSELECT_CFLAGS" | \
|
||||
sed -r -e "s/(MENUSELECT_CFLAGS=|ADD_CFLAGS_TO_BUILDOPTS_H|-D)//g")
|
||||
|
||||
# This is a list of flags that don't affect the ABI.
|
||||
# "ADD_CFLAGS_TO_BUILDOPTS_H" is NOT set, we'll filter these
|
||||
# out of the buildopts.h file.
|
||||
#
|
||||
# These used to always be filtered out but if they're not in
|
||||
# buildopts.h, many IDEs will show them as undefined and mark
|
||||
# any code blocks enabled by them as disabled.
|
||||
#
|
||||
# The original reasoning for removing them was that trivial
|
||||
# changes to the buildopts.h file will cause ccache to
|
||||
# invalidate any source files that use it and increase the
|
||||
# compile time. It's not such a huge deal these days but
|
||||
# to preserve backwards behavior the default is still to
|
||||
# remove them.
|
||||
#
|
||||
# The ABI-breaking flags are always included in buildopts.h.
|
||||
|
||||
# This variable is used by sed so it needs to be a valid
|
||||
# regex which will be surrounded by parens.]
|
||||
FILTER_OUT="\
|
||||
AO2_DEBUG|BETTER_BACKTRACES|BUILD_NATIVE|\
|
||||
COMPILE_DOUBLE|DEBUG_CHAOS|DEBUG_SCHEDULER|\
|
||||
DETECT_DEADLOCKS|DONT_OPTIMIZE|DUMP_SCHEDULER|\
|
||||
LOTS_OF_SPANS|MALLOC_DEBUG|RADIO_RELAX|\
|
||||
REBUILD_PARSERS|REF_DEBUG|USE_HOARD_ALLOCATOR"
|
||||
|
||||
# Create buildopts.h
|
||||
|
||||
INCLUDE_CFLAGS="$MENUSELECT_CFLAGS"
|
||||
# Do the filter-out if needed.
|
||||
if ! $ADD_CFLAGS_TO_BUILDOPTS ; then
|
||||
INCLUDE_CFLAGS=$( echo "$MENUSELECT_CFLAGS" | \
|
||||
sed -r -e "s/(${FILTER_OUT})//g")
|
||||
fi
|
||||
|
||||
# Output the defines.
|
||||
for x in ${INCLUDE_CFLAGS}; do
|
||||
echo "#define ${x} 1"
|
||||
if test "${x}" = "LOW_MEMORY" ; then
|
||||
# LOW_MEMORY isn't an ABI affecting option but it is used in many sources
|
||||
# so it gets defined globally but is not included in AST_BUILTOPTS.
|
||||
continue
|
||||
fi
|
||||
if test "x${BUILDOPTS}" != "x" ; then
|
||||
BUILDOPTS="${BUILDOPTS}, ${x}"
|
||||
else
|
||||
BUILDOPTS="${x}"
|
||||
fi
|
||||
done
|
||||
|
||||
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_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
|
||||
|
||||
AWK=${AWK:-awk}
|
||||
GIT=${GIT:-git}
|
||||
GREP=${GREP:-grep}
|
||||
SED=${SED:-sed}
|
||||
|
||||
AWK=${AWK:-awk}
|
||||
|
||||
if [ -f ${1}/.version ]; then
|
||||
cat ${1}/.version
|
||||
elif [ -d ${1}/.svn ]; then
|
||||
PARTS=`LANG=C svn info ${1} | ${GREP} URL | ${AWK} '{print $2;}' | ${SED} -e 's:^.*/svn/asterisk/::' | ${SED} -e 's:/: :g'`
|
||||
BRANCH=0
|
||||
TEAM=0
|
||||
TAG=0
|
||||
FEATURE=0
|
||||
|
||||
REV=`svnversion -c ${1} | cut -d: -f2`
|
||||
|
||||
INTEGRATED=`LANG=C svn pg automerge-propname ${1}`
|
||||
if [ -z "${INTEGRATED}" ] ; then
|
||||
INTEGRATED=svnmerge-integrated
|
||||
fi
|
||||
|
||||
BASE=`LANG=C svn pg ${INTEGRATED} ${1} | cut -d: -f1`
|
||||
|
||||
if [ "${PARTS}" = "trunk" ] ; then
|
||||
echo SVN-trunk-r${REV}
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for PART in $PARTS ; do
|
||||
if [ ${TAG} != 0 ] ; then
|
||||
if [ "${PART}" = "autotag_for_be" ] ; then
|
||||
continue
|
||||
fi
|
||||
if [ "${PART}" = "autotag_for_sx00i" ] ; then
|
||||
continue
|
||||
fi
|
||||
RESULT="${PART}"
|
||||
break
|
||||
fi
|
||||
|
||||
if [ ${BRANCH} != 0 ] ; then
|
||||
RESULT="${RESULT}-${PART}"
|
||||
if [ ${FEATURE} != 0 ] ; then
|
||||
RESULT="${RESULT}-${FEATURE_NAME}"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
|
||||
if [ ${TEAM} != 0 ] ; then
|
||||
if [ -z "${RESULT}" ] ; then
|
||||
RESULT="${PART}"
|
||||
else
|
||||
RESULT="${RESULT}-${PART}"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "certified" ] ; then
|
||||
FEATURE=1
|
||||
FEATURE_NAME="cert"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "branches" ] ; then
|
||||
BRANCH=1
|
||||
RESULT="branch"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "tags" ] ; then
|
||||
TAG=1
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "team" ] ; then
|
||||
TEAM=1
|
||||
continue
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${TAG} != 0 ] ; then
|
||||
echo ${RESULT}
|
||||
else
|
||||
echo SVN-${RESULT}-r${REV}${BASE:+-${BASE}}
|
||||
fi
|
||||
elif [ -d ${1}/.git ]; then
|
||||
if [ -z ${GIT} ]; then
|
||||
GIT="git"
|
||||
fi
|
||||
|
||||
if ! command -v ${GIT} >/dev/null 2>&1; then
|
||||
echo "UNKNOWN__and_probably_unsupported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GITCHECK=$(${GIT} describe --always 2>/dev/null || echo gitfail 2>/dev/null)
|
||||
if [ "x${GITCHECK}" = "xgitfail" ]; then
|
||||
echo "UNKNOWN__git_check_fail"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd ${1}
|
||||
|
||||
# If the first log commit messages indicates that this is checked into
|
||||
# subversion, we'll just use the SVN- form of the revision.
|
||||
MODIFIED=""
|
||||
SVN_REV=`${GIT} log --pretty=full -1 | ${SED} -n '/git-svn-id:/ s/.*\@\([^ ]*\) .*/\1/p'`
|
||||
if [ -z "$SVN_REV" ]; then
|
||||
# If MAINLINE_BRANCH is already set in the environment, use it.
|
||||
if [ -z "${MAINLINE_BRANCH}" ] ; then
|
||||
# Try to retrieve MAINLINE_BRANCH from a local .develvars file first.
|
||||
# .develvars is keyed by the branch name so we need to get that first.
|
||||
BRANCH=$(${GIT} symbolic-ref --short HEAD)
|
||||
if [ -f .develvars ] ; then
|
||||
MAINLINE_BRANCH=$(${GIT} config -f .develvars --get branch.${BRANCH}.mainline-branch)
|
||||
fi
|
||||
|
||||
# If we didn't find it, see if this is a well-known development branch.
|
||||
# development/<mainline_branch>/<branchname> or
|
||||
# devel/<mainline_branch>/<branchname>
|
||||
if [ "x${MAINLINE_BRANCH}" = "x" ] ; then
|
||||
MAINLINE_BRANCH=$(echo "${BRANCH}" | ${SED} -n -r -e "s@devel(opment)?/([0-9]+)/.+@\2@p")
|
||||
fi
|
||||
|
||||
# If we didn't find it, get it from .gitreview defaultbranch.
|
||||
if [ "x${MAINLINE_BRANCH}" = "x" ] ; then
|
||||
MAINLINE_BRANCH=$(${GIT} config -f .gitreview --get gerrit.defaultbranch)
|
||||
fi
|
||||
fi
|
||||
|
||||
VERSION=`${GIT} describe --long --always --tags --dirty=M 2> /dev/null`
|
||||
if [ $? -ne 0 ]; then
|
||||
if [ "`${GIT} ls-files -m | wc -l`" != "0" ]; then
|
||||
MODIFIED="M"
|
||||
fi
|
||||
# Some older versions of git do not support all the above
|
||||
# options.
|
||||
VERSION=`${GIT} rev-parse --short --verify HEAD`${MODIFIED}
|
||||
fi
|
||||
echo GIT-${MAINLINE_BRANCH}-${VERSION}
|
||||
else
|
||||
PARTS=`LANG=C ${GIT} log --pretty=full | ${GREP} -F "git-svn-id:" | head -1 | ${AWK} '{print $2;}' | ${SED} -e s:^.*/svn/$2/:: | ${SED} -e 's:/: :g' | ${SED} -e 's/@.*$//g'`
|
||||
BRANCH=0
|
||||
TEAM=0
|
||||
TAG=0
|
||||
FEATURE=0
|
||||
|
||||
if [ "`${GIT} ls-files -m | wc -l`" != "0" ]; then
|
||||
MODIFIED="M"
|
||||
fi
|
||||
|
||||
for PART in $PARTS ; do
|
||||
if [ ${TAG} != 0 ] ; then
|
||||
if [ "${PART}" = "autotag_for_be" ] ; then
|
||||
continue
|
||||
fi
|
||||
if [ "${PART}" = "autotag_for_sx00i" ] ; then
|
||||
continue
|
||||
fi
|
||||
RESULT="${PART}"
|
||||
break
|
||||
fi
|
||||
|
||||
if [ ${BRANCH} != 0 ] ; then
|
||||
RESULT="${RESULT}-${PART}"
|
||||
if [ ${FEATURE} != 0 ] ; then
|
||||
RESULT="${RESULT}-${FEATURE_NAME}"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
|
||||
if [ ${TEAM} != 0 ] ; then
|
||||
if [ -z "${RESULT}" ] ; then
|
||||
RESULT="${PART}"
|
||||
else
|
||||
RESULT="${RESULT}-${PART}"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "certified" ] ; then
|
||||
FEATURE=1
|
||||
FEATURE_NAME="cert"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "branches" ] ; then
|
||||
BRANCH=1
|
||||
RESULT="branch"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "tags" ] ; then
|
||||
TAG=1
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "team" ] ; then
|
||||
TEAM=1
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "trunk" ]; then
|
||||
echo SVN-trunk-r${SVN_REV}${MODIFIED}
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${TAG} != 0 ] ; then
|
||||
echo ${RESULT}
|
||||
else
|
||||
echo SVN-${RESULT##-}-r${SVN_REV}${MODIFIED}
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "UNKNOWN__and_probably_unsupported"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -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}
|
||||
|
||||
if test ! -f include/asterisk/buildopts.h ; then
|
||||
echo "include/asterisk/buildopts.h is missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test ! -f .flavor ; then
|
||||
EXTRA=""
|
||||
elif test ! -f .version ; then
|
||||
|
@ -18,14 +23,11 @@ then
|
|||
BUILDOPTS="AST_DEVMODE"
|
||||
fi
|
||||
|
||||
TMP=`${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts | sed 's/MENUSELECT_CFLAGS\=//g' | sed 's/-D//g'`
|
||||
for x in ${TMP}; do
|
||||
if test "x${BUILDOPTS}" != "x" ; then
|
||||
BUILDOPTS="${BUILDOPTS}, ${x}"
|
||||
else
|
||||
BUILDOPTS="${x}"
|
||||
fi
|
||||
done
|
||||
BUILDOPTS=$(sed -n -r -e 's/#define\s+AST_BUILDOPTS\s+"([^"]+)"/\1/gp' \
|
||||
include/asterisk/buildopts.h )
|
||||
|
||||
BUILDOPTS_ALL=$(sed -n -r -e 's/#define\s+AST_BUILDOPTS_ALL\s+"([^"]+)"/\1/gp' \
|
||||
include/asterisk/buildopts.h )
|
||||
|
||||
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_all[] = "${BUILDOPTS_ALL}";
|
||||
|
||||
const char *ast_get_version(void)
|
||||
{
|
||||
return asterisk_version;
|
||||
|
@ -58,4 +62,9 @@ const char *ast_get_build_opts(void)
|
|||
return asterisk_build_opts;
|
||||
}
|
||||
|
||||
const char *ast_get_build_opts_all(void)
|
||||
{
|
||||
return asterisk_build_opts_all;
|
||||
}
|
||||
|
||||
END
|
||||
|
|
|
@ -135,12 +135,18 @@ if [ "${for_wiki}" -eq "1" ] || [ "${validate}" -eq "1" ]; then
|
|||
fi
|
||||
fi
|
||||
|
||||
make_absolute() {
|
||||
case "$1" in
|
||||
/*) echo "$1" ;;
|
||||
*) echo "$source_tree/$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [ "${command}" = "print_dependencies" ] ; then
|
||||
for subdir in ${mod_subdirs} ; do
|
||||
subpath="${source_tree}/${subdir}"
|
||||
# We WANT word splitting in the following line.
|
||||
# shellcheck disable=SC2046
|
||||
${GREP} -l -E '(language="en_US"|appdocsxml.dtd)' $(${FIND} "${subpath}" -name '*.c' -or -name '*.cc' -or -name '*.xml') || :
|
||||
subpath=$(make_absolute "$subdir")
|
||||
${FIND} "${subpath}" \( -name '*.c' -o -name '*.cc' -o -name '*.xml' \) \
|
||||
-exec ${GREP} -l -E '(language="en_US"|appdocsxml.dtd)' '{}' \;
|
||||
done
|
||||
exit
|
||||
fi
|
||||
|
@ -186,7 +192,7 @@ printf "Building Documentation For: "
|
|||
|
||||
for subdir in ${mod_subdirs} ; do
|
||||
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
|
||||
if [ "${with_moduleinfo}" -eq "1" ] ; then
|
||||
MODULEINFO=$(${AWK} -f "${source_tree}/build_tools/get_moduleinfo" "${i}")
|
||||
|
|
|
@ -29,6 +29,7 @@ URIPARSER=@PBX_URIPARSER@
|
|||
KQUEUE=@PBX_KQUEUE@
|
||||
LDAP=@PBX_LDAP@
|
||||
LIBEDIT=@PBX_LIBEDIT@
|
||||
LIBJWT=@PBX_LIBJWT@
|
||||
LIBXML2=@PBX_LIBXML2@
|
||||
LIBXSLT=@PBX_LIBXSLT@
|
||||
XMLSTARLET=@PBX_XMLSTARLET@
|
||||
|
|
|
@ -154,6 +154,8 @@ static struct console_pvt {
|
|||
struct ast_frame fr;
|
||||
/*! Running = 1, Not running = 0 */
|
||||
unsigned int streamstate:1;
|
||||
/*! Abort stream processing? */
|
||||
unsigned int abort:1;
|
||||
/*! On-hook = 0, Off-hook = 1 */
|
||||
unsigned int hookstate:1;
|
||||
/*! Unmuted = 0, Muted = 1 */
|
||||
|
@ -277,18 +279,19 @@ static void *stream_monitor(void *data)
|
|||
};
|
||||
|
||||
for (;;) {
|
||||
pthread_testcancel();
|
||||
console_pvt_lock(pvt);
|
||||
res = Pa_ReadStream(pvt->stream, buf, sizeof(buf) / sizeof(int16_t));
|
||||
console_pvt_unlock(pvt);
|
||||
pthread_testcancel();
|
||||
|
||||
if (!pvt->owner) {
|
||||
if (!pvt->owner || pvt->abort) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (res == paNoError)
|
||||
if (res == paNoError) {
|
||||
ast_queue_frame(pvt->owner, &f);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Console ReadStream failed: %s\n", Pa_GetErrorText(res));
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -403,8 +406,9 @@ static int stop_stream(struct console_pvt *pvt)
|
|||
if (!pvt->streamstate || pvt->thread == AST_PTHREADT_NULL)
|
||||
return 0;
|
||||
|
||||
pthread_cancel(pvt->thread);
|
||||
pthread_kill(pvt->thread, SIGURG);
|
||||
pvt->abort = 1;
|
||||
/* Wait for pvt->thread to exit cleanly, to avoid killing it while it's holding a lock. */
|
||||
pthread_kill(pvt->thread, SIGURG); /* Wake it up if needed, but don't cancel it */
|
||||
pthread_join(pvt->thread, NULL);
|
||||
|
||||
console_pvt_lock(pvt);
|
||||
|
|
|
@ -261,6 +261,36 @@
|
|||
completely disabled)</para>
|
||||
<para> <literal>voice</literal> Voice mode (returns from FAX mode, reverting the changes that were made)</para>
|
||||
</enum>
|
||||
<enum name="dialmode">
|
||||
<para>R/W Pulse and tone dialing mode of the channel.</para>
|
||||
<para>Disabling tone dialing using this option will not disable the DSP used for DTMF detection.
|
||||
To do that, also set the <literal>digitdetect</literal> option. If digit detection is disabled,
|
||||
DTMF will not be detected, regardless of the <literal>dialmode</literal> setting.
|
||||
The <literal>digitdetect</literal> setting has no impact on pulse dialing detection.</para>
|
||||
<para>If set, overrides the setting in <literal>chan_dahdi.conf</literal> for that channel.</para>
|
||||
<enumlist>
|
||||
<enum name="both" />
|
||||
<enum name="pulse" />
|
||||
<enum name="dtmf" />
|
||||
<enum name="tone" />
|
||||
<enum name="none" />
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="waitfordialtone">
|
||||
<para>W/O Duration in ms for which to wait for dial tone on the current call.</para>
|
||||
<para>This setting is will temporarily override the <literal>waitfordialtone</literal>
|
||||
setting in <literal>chan_dahdi.conf</literal> (typically if that setting is disabled).
|
||||
You must call this in a pre-dial handler when making a call on an analog trunk
|
||||
(e.g. FXS-signalled interface).</para>
|
||||
<para>This allows, for example, being able to barge in on an in-use trunk,
|
||||
if dialed specifically, but allows skipping the trunk when routing calls
|
||||
if dial tone is not present on a channel.</para>
|
||||
<para>This setting will only apply to the current (next) call made on the
|
||||
DAHDI channel, and will not persist for future calls.</para>
|
||||
<para>Please keep in mind that due to the way that chan_dahdi implements dial tone detection,
|
||||
DTMF digits on an in-use channel will temporarily relay to any other channels attempting to use the channel for a call.
|
||||
However, voice transmission will not leak.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</info>
|
||||
<info name="Dial_Resource" language="en_US" tech="DAHDI">
|
||||
|
@ -1027,6 +1057,7 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void)
|
|||
#endif
|
||||
.chan = {
|
||||
.context = "default",
|
||||
.immediatering = 1,
|
||||
.cid_num = "",
|
||||
.cid_name = "",
|
||||
.cid_tag = "",
|
||||
|
@ -1034,6 +1065,7 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void)
|
|||
.mohsuggest = "",
|
||||
.parkinglot = "",
|
||||
.transfertobusy = 1,
|
||||
.dialmode = 0,
|
||||
|
||||
.ani_info_digits = 2,
|
||||
.ani_wink_time = 1000,
|
||||
|
@ -1447,6 +1479,18 @@ static int my_get_callerid(void *pvt, char *namebuf, char *numbuf, enum analog_e
|
|||
if (num)
|
||||
ast_copy_string(numbuf, num, ANALOG_MAX_CID);
|
||||
|
||||
if (flags & (CID_PRIVATE_NUMBER | CID_UNKNOWN_NUMBER)) {
|
||||
/* If we got a presentation, we must set it on the channel */
|
||||
struct ast_channel *chan = analog_p->ss_astchan;
|
||||
struct ast_party_caller caller;
|
||||
|
||||
ast_party_caller_set_init(&caller, ast_channel_caller(chan));
|
||||
caller.id.name.presentation = caller.id.number.presentation = (flags & CID_PRIVATE_NUMBER) ?
|
||||
AST_PRES_RESTRICTED | AST_PRES_USER_NUMBER_UNSCREENED : AST_PRES_UNAVAILABLE | AST_PRES_USER_NUMBER_UNSCREENED;
|
||||
ast_party_caller_set(ast_channel_caller(chan), &caller, NULL);
|
||||
ast_party_caller_free(&caller);
|
||||
}
|
||||
|
||||
ast_debug(1, "CallerID number: %s, name: %s, flags=%d\n", num, name, flags);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1633,6 +1677,7 @@ static int my_callwait(void *pvt)
|
|||
static int my_send_callerid(void *pvt, int cwcid, struct ast_party_caller *caller)
|
||||
{
|
||||
struct dahdi_pvt *p = pvt;
|
||||
struct analog_pvt *analog_p = p->sig_pvt;
|
||||
|
||||
ast_debug(2, "Starting cid spill\n");
|
||||
|
||||
|
@ -1644,13 +1689,20 @@ static int my_send_callerid(void *pvt, int cwcid, struct ast_party_caller *calle
|
|||
if ((p->cidspill = ast_malloc(MAX_CALLERID_SIZE))) {
|
||||
int pres = ast_party_id_presentation(&caller->id);
|
||||
if (cwcid == 0) {
|
||||
/* Some CPE support additional parameters for on-hook Caller*ID,
|
||||
* such as redirecting reason and call qualifier, so send those
|
||||
* if available.
|
||||
* I don't know of any CPE that supports this for Call Waiting (unfortunately),
|
||||
* so don't send those for call waiting as that will just lengthen the CID spill
|
||||
* for no good reason.
|
||||
*/
|
||||
p->cidlen = ast_callerid_full_generate(p->cidspill,
|
||||
caller->id.name.str,
|
||||
caller->id.number.str,
|
||||
NULL,
|
||||
-1,
|
||||
analog_p->redirecting_reason,
|
||||
pres,
|
||||
0,
|
||||
analog_p->call_qualifier,
|
||||
CID_TYPE_MDMF,
|
||||
AST_LAW(p));
|
||||
} else {
|
||||
|
@ -1991,6 +2043,9 @@ static void my_set_cadence(void *pvt, int *cid_rings, struct ast_channel *ast)
|
|||
ast_log(LOG_WARNING, "Unable to set distinctive ring cadence %d on '%s': %s\n", p->distinctivering, ast_channel_name(ast), strerror(errno));
|
||||
*cid_rings = cidrings[p->distinctivering - 1];
|
||||
} else {
|
||||
if (p->distinctivering > 0) {
|
||||
ast_log(LOG_WARNING, "Cadence %d is not defined, falling back to default ring cadence\n", p->distinctivering);
|
||||
}
|
||||
if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETCADENCE, NULL))
|
||||
ast_log(LOG_WARNING, "Unable to reset default ring on '%s': %s\n", ast_channel_name(ast), strerror(errno));
|
||||
*cid_rings = p->sendcalleridafter;
|
||||
|
@ -2064,11 +2119,40 @@ static void my_set_waitingfordt(void *pvt, struct ast_channel *ast)
|
|||
{
|
||||
struct dahdi_pvt *p = pvt;
|
||||
|
||||
if (p->waitfordialtone && CANPROGRESSDETECT(p) && p->dsp) {
|
||||
ast_debug(1, "Defer dialing for %dms or dialtone\n", p->waitfordialtone);
|
||||
gettimeofday(&p->waitingfordt, NULL);
|
||||
ast_setstate(ast, AST_STATE_OFFHOOK);
|
||||
/* We reset p->waitfordialtonetemp here, to prevent leaking to future calls,
|
||||
* but we also need to check against this value until we get dialtone
|
||||
* or the timer expires, since waitingfordt is when the timer started,
|
||||
* not when it should expire.
|
||||
*
|
||||
* Critically, we only set p->waitingfordt here if waitfordialtone or waitfordialtonetemp
|
||||
* has already been set, as waitingfordt is what is checked at runtime to determine
|
||||
* if we should be waiting for dial tone. This ensures that if a second call
|
||||
* is initiated concurrently, the first one "consumes" waitfordialtonetemp and resets it,
|
||||
* preventing leaking to other calls while remaining available to check on the first one while dialing.
|
||||
*/
|
||||
p->waitfordialtoneduration = p->waitfordialtonetemp ? p->waitfordialtonetemp : p->waitfordialtone;
|
||||
p->waitfordialtonetemp = 0;
|
||||
|
||||
if (!(p->waitfordialtoneduration && CANPROGRESSDETECT(p))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Because the DSP is allocated when the channel is created,
|
||||
* if we requested waitfordialtone later (in a predial handler),
|
||||
* we need to create it now */
|
||||
if (!p->dsp) {
|
||||
p->dsp = ast_dsp_new();
|
||||
if (!p->dsp) {
|
||||
ast_log(LOG_ERROR, "Unable to allocate DSP\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
p->dsp_features |= DSP_FEATURE_WAITDIALTONE;
|
||||
ast_dsp_set_features(p->dsp, p->dsp_features);
|
||||
|
||||
ast_debug(1, "Defer dialing for %dms or dialtone\n", p->waitfordialtoneduration);
|
||||
gettimeofday(&p->waitingfordt, NULL);
|
||||
ast_setstate(ast, AST_STATE_OFFHOOK);
|
||||
}
|
||||
|
||||
static int my_check_waitingfordt(void *pvt)
|
||||
|
@ -3447,7 +3531,7 @@ struct sig_ss7_callback sig_ss7_callbacks =
|
|||
*/
|
||||
static void notify_message(char *mailbox, int thereornot)
|
||||
{
|
||||
char s[sizeof(mwimonitornotify) + 80];
|
||||
char s[sizeof(mwimonitornotify) + 164];
|
||||
|
||||
if (ast_strlen_zero(mailbox)) {
|
||||
return;
|
||||
|
@ -4115,7 +4199,7 @@ static void dahdi_r2_on_context_log(openr2_context_t *r2context, openr2_log_leve
|
|||
{
|
||||
#define CONTEXT_TAG "Context - "
|
||||
char logmsg[256];
|
||||
char completemsg[sizeof(logmsg) + sizeof(CONTEXT_TAG) - 1];
|
||||
char completemsg[sizeof(logmsg) * 2];
|
||||
vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
|
||||
snprintf(completemsg, sizeof(completemsg), CONTEXT_TAG "%s", logmsg);
|
||||
dahdi_r2_write_log(level, completemsg);
|
||||
|
@ -4128,10 +4212,11 @@ static void dahdi_r2_on_chan_log(openr2_chan_t *r2chan, openr2_log_level_t level
|
|||
{
|
||||
#define CHAN_TAG "Chan "
|
||||
char logmsg[256];
|
||||
char completemsg[sizeof(logmsg) + sizeof(CHAN_TAG) - 1];
|
||||
char completemsg[sizeof(logmsg) * 2];
|
||||
vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
|
||||
snprintf(completemsg, sizeof(completemsg), CHAN_TAG "%d - %s", openr2_chan_get_number(r2chan), logmsg);
|
||||
dahdi_r2_write_log(level, completemsg);
|
||||
#undef CHAN_TAG
|
||||
}
|
||||
|
||||
static int dahdi_r2_on_dnis_digit_received(openr2_chan_t *r2chan, char digit)
|
||||
|
@ -5189,6 +5274,18 @@ static int has_voicemail(struct dahdi_pvt *p)
|
|||
int new_msgs;
|
||||
RAII_VAR(struct stasis_message *, mwi_message, NULL, ao2_cleanup);
|
||||
|
||||
/* A manual MWI disposition has been requested, use that instead
|
||||
* if this is for sending the new MWI indication. */
|
||||
if (p->mwioverride_active) {
|
||||
/* We don't clear p->mwioverride_active automatically,
|
||||
* because otherwise do_monitor would just change it back to the way it was.
|
||||
* We need to keep the override active until explicitly disabled by the user,
|
||||
* so that we can keep returning the correct answer in subsequent calls to do_monitor. */
|
||||
ast_debug(6, "MWI manual override active on channel %d: pretending that it should be %s\n",
|
||||
p->channel, p->mwioverride_disposition ? "active" : "inactive");
|
||||
return p->mwioverride_disposition;
|
||||
}
|
||||
|
||||
mwi_message = stasis_cache_get(ast_mwi_state_cache(), ast_mwi_state_type(), p->mailbox);
|
||||
if (mwi_message) {
|
||||
struct ast_mwi_state *mwi_state = stasis_message_data(mwi_message);
|
||||
|
@ -6531,6 +6628,36 @@ hangup_out:
|
|||
ast_free(p->cidspill);
|
||||
p->cidspill = NULL;
|
||||
|
||||
if (p->reoriginate && p->sig == SIG_FXOKS && dahdi_analog_lib_handles(p->sig, p->radio, 0)) {
|
||||
/* Automatic reorigination: if all calls towards a user have hung up,
|
||||
* give dial tone again, so user doesn't need to cycle the hook state manually. */
|
||||
if (my_is_off_hook(p) && !p->owner) {
|
||||
/* 2 important criteria: channel must be off-hook, with no calls remaining (no owner) */
|
||||
ast_debug(1, "Queuing reorigination for channel %d\n", p->channel);
|
||||
my_play_tone(p, SUB_REAL, -1); /* Stop any congestion tone that may be present. */
|
||||
/* Must wait for the loop disconnect to end.
|
||||
* Sadly, these definitions are in dahdi/kernel.h, not dahdi/user.h
|
||||
* Calling usleep on an active DAHDI channel is a no-no, but this is okay.
|
||||
*/
|
||||
usleep(800000); /* DAHDI_KEWLTIME + DAHDI_AFTERKEWLTIME */
|
||||
/* If the line is still off-hook and ownerless, actually queue the reorigination.
|
||||
* do_monitor will actually go ahead and do it. */
|
||||
if (!p->owner && my_is_off_hook(p)) {
|
||||
p->doreoriginate = 1; /* Tell do_monitor to reoriginate this channel */
|
||||
/* Note, my_off_hook will fail if called before the loop disconnect has finished
|
||||
* (important for FXOKS signaled channels). This is because DAHDI will reject
|
||||
* DAHDI_OFFHOOK while the channel is in TXSTATE_KEWL or TXSTATE_AFTERKEWL,
|
||||
* so we have to wait for that to finish (see comment above).
|
||||
* do_monitor itself cannot block, so make the blocking usleep call
|
||||
* here in the channel thread instead.
|
||||
*/
|
||||
my_off_hook(p); /* Now, go ahead and take the channel back off hook (sig_analog put it on hook) */
|
||||
} else {
|
||||
ast_debug(1, "Channel %d is no longer eligible for reorigination (went back on hook or became in use)\n", p->channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ast_mutex_unlock(&p->lock);
|
||||
ast_verb(3, "Hungup '%s'\n", ast_channel_name(ast));
|
||||
|
||||
|
@ -6655,6 +6782,14 @@ static int dahdi_queryoption(struct ast_channel *chan, int option, void *data, i
|
|||
}
|
||||
|
||||
switch (option) {
|
||||
case AST_OPTION_TDD:
|
||||
cp = (char *) data;
|
||||
if (p->mate) {
|
||||
*cp = 2;
|
||||
} else {
|
||||
*cp = p->tdd ? 1 : 0;
|
||||
}
|
||||
break;
|
||||
case AST_OPTION_DIGIT_DETECT:
|
||||
cp = (char *) data;
|
||||
*cp = p->ignoredtmf ? 0 : 1;
|
||||
|
@ -7008,6 +7143,32 @@ static int dahdi_func_read(struct ast_channel *chan, const char *function, char
|
|||
}
|
||||
ast_mutex_unlock(&p->lock);
|
||||
#endif /* defined(HAVE_PRI) */
|
||||
} else if (!strcasecmp(data, "dialmode")) {
|
||||
struct analog_pvt *analog_p;
|
||||
ast_mutex_lock(&p->lock);
|
||||
analog_p = p->sig_pvt;
|
||||
/* Hardcode p->radio and p->oprmode as 0 since we're using this to check for analogness, not the handler */
|
||||
if (dahdi_analog_lib_handles(p->sig, 0, 0) && analog_p) {
|
||||
switch (analog_p->dialmode) {
|
||||
case ANALOG_DIALMODE_BOTH:
|
||||
ast_copy_string(buf, "both", len);
|
||||
break;
|
||||
case ANALOG_DIALMODE_PULSE:
|
||||
ast_copy_string(buf, "pulse", len);
|
||||
break;
|
||||
case ANALOG_DIALMODE_DTMF:
|
||||
ast_copy_string(buf, "dtmf", len);
|
||||
break;
|
||||
case ANALOG_DIALMODE_NONE:
|
||||
ast_copy_string(buf, "none", len);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "%s only supported on analog channels\n", data);
|
||||
*buf = '\0';
|
||||
res = -1;
|
||||
}
|
||||
ast_mutex_unlock(&p->lock);
|
||||
} else {
|
||||
*buf = '\0';
|
||||
res = -1;
|
||||
|
@ -7113,6 +7274,45 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char
|
|||
ast_log(LOG_WARNING, "Unsupported value '%s' provided for '%s' item.\n", value, data);
|
||||
res = -1;
|
||||
}
|
||||
} else if (!strcasecmp(data, "dialmode")) {
|
||||
struct analog_pvt *analog_p;
|
||||
|
||||
ast_mutex_lock(&p->lock);
|
||||
analog_p = p->sig_pvt;
|
||||
if (!dahdi_analog_lib_handles(p->sig, 0, 0) || !analog_p) {
|
||||
ast_log(LOG_WARNING, "%s only supported on analog channels\n", data);
|
||||
ast_mutex_unlock(&p->lock);
|
||||
return -1;
|
||||
}
|
||||
/* analog pvt is used for pulse dialing, so update both */
|
||||
if (!strcasecmp(value, "pulse")) {
|
||||
p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_PULSE;
|
||||
} else if (!strcasecmp(value, "dtmf") || !strcasecmp(value, "tone")) {
|
||||
p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_DTMF;
|
||||
} else if (!strcasecmp(value, "none")) {
|
||||
p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_NONE;
|
||||
} else if (!strcasecmp(value, "both")) {
|
||||
p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_BOTH;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "'%s' is an invalid setting for %s\n", value, data);
|
||||
res = -1;
|
||||
}
|
||||
ast_mutex_unlock(&p->lock);
|
||||
} else if (!strcasecmp(data, "waitfordialtone")) {
|
||||
if (ast_strlen_zero(value)) {
|
||||
ast_log(LOG_WARNING, "waitfordialtone requires a duration in ms\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_mutex_lock(&p->lock);
|
||||
if (!CANPROGRESSDETECT(p)) {
|
||||
ast_log(LOG_WARNING, "%s only supported on analog trunks\n", data);
|
||||
ast_mutex_unlock(&p->lock);
|
||||
return -1;
|
||||
}
|
||||
/* Only set the temp waitfordialtone setting, not the permanent one. */
|
||||
p->waitfordialtonetemp = atoi(value);
|
||||
ast_mutex_unlock(&p->lock);
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
|
@ -8952,9 +9152,9 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
|
|||
/* DSP clears us of being pulse */
|
||||
p->pulsedial = 0;
|
||||
} else if (p->waitingfordt.tv_sec) {
|
||||
if (ast_tvdiff_ms(ast_tvnow(), p->waitingfordt) >= p->waitfordialtone ) {
|
||||
if (ast_tvdiff_ms(ast_tvnow(), p->waitingfordt) >= p->waitfordialtoneduration) {
|
||||
p->waitingfordt.tv_sec = 0;
|
||||
ast_log(LOG_WARNING, "Never saw dialtone on channel %d\n", p->channel);
|
||||
ast_log(LOG_NOTICE, "Never saw dialtone on channel %d\n", p->channel);
|
||||
ast_frfree(f);
|
||||
f = NULL;
|
||||
} else if (f->frametype == AST_FRAME_VOICE) {
|
||||
|
@ -8996,6 +9196,13 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
|
|||
} else {
|
||||
dahdi_handle_dtmf(ast, idx, &f);
|
||||
}
|
||||
if (!(p->dialmode == ANALOG_DIALMODE_BOTH || p->dialmode == ANALOG_DIALMODE_DTMF)) {
|
||||
if (f->frametype == AST_FRAME_DTMF_END) { /* only show this message when the key is let go of */
|
||||
ast_debug(1, "Dropping DTMF digit '%c' because tone dialing is disabled\n", f->subclass.integer);
|
||||
}
|
||||
f->frametype = AST_FRAME_NULL;
|
||||
f->subclass.integer = 0;
|
||||
}
|
||||
break;
|
||||
case AST_FRAME_VOICE:
|
||||
if (p->cidspill || p->cid_suppress_expire) {
|
||||
|
@ -11753,7 +11960,7 @@ static void *do_monitor(void *data)
|
|||
&& (last->sig & __DAHDI_SIG_FXO)
|
||||
&& !analog_p->fxsoffhookstate
|
||||
&& !last->owner
|
||||
&& !ast_strlen_zero(last->mailbox)
|
||||
&& (!ast_strlen_zero(last->mailbox) || last->mwioverride_active)
|
||||
&& !analog_p->subs[SUB_REAL].owner /* could be a recall ring from a flash hook hold */
|
||||
&& (thispass - analog_p->onhooktime > 3)) {
|
||||
res = has_voicemail(last);
|
||||
|
@ -11901,6 +12108,26 @@ static void *do_monitor(void *data)
|
|||
else
|
||||
doomed = handle_init_event(i, res);
|
||||
}
|
||||
if (i->doreoriginate && res == DAHDI_EVENT_HOOKCOMPLETE) {
|
||||
/* Actually automatically reoriginate this FXS line, if directed to.
|
||||
* We should get a DAHDI_EVENT_HOOKCOMPLETE from the loop disconnect
|
||||
* doing its thing (one reason why this is for FXOKS only: FXOLS
|
||||
* hangups don't give us any DAHDI events to piggyback off of)*/
|
||||
i->doreoriginate = 0;
|
||||
/* Double check the channel is still off-hook. There's only about a millisecond
|
||||
* between when doreoriginate is set high and we see that here, but just to be safe. */
|
||||
if (!my_is_off_hook(i)) {
|
||||
ast_debug(1, "Woah! Went back on hook before reoriginate could happen on channel %d\n", i->channel);
|
||||
} else {
|
||||
ast_verb(3, "Automatic reorigination on channel %d\n", i->channel);
|
||||
res = DAHDI_EVENT_RINGOFFHOOK; /* Pretend that the physical channel just went off hook */
|
||||
if (dahdi_analog_lib_handles(i->sig, i->radio, i->oprmode)) {
|
||||
doomed = (struct dahdi_pvt *) analog_handle_init_event(i->sig_pvt, dahdievent_to_analogevent(res));
|
||||
} else {
|
||||
doomed = handle_init_event(i, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_mutex_lock(&iflock);
|
||||
}
|
||||
}
|
||||
|
@ -12778,7 +13005,9 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
|
|||
}
|
||||
#endif
|
||||
tmp->immediate = conf->chan.immediate;
|
||||
tmp->immediatering = conf->chan.immediatering;
|
||||
tmp->transfertobusy = conf->chan.transfertobusy;
|
||||
tmp->dialmode = conf->chan.dialmode;
|
||||
if (chan_sig & __DAHDI_SIG_FXS) {
|
||||
tmp->mwimonitor_fsk = conf->chan.mwimonitor_fsk;
|
||||
tmp->mwimonitor_neon = conf->chan.mwimonitor_neon;
|
||||
|
@ -12805,6 +13034,8 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
|
|||
tmp->usedistinctiveringdetection = usedistinctiveringdetection;
|
||||
tmp->callwaitingcallerid = conf->chan.callwaitingcallerid;
|
||||
tmp->threewaycalling = conf->chan.threewaycalling;
|
||||
tmp->threewaysilenthold = conf->chan.threewaysilenthold;
|
||||
tmp->calledsubscriberheld = conf->chan.calledsubscriberheld; /* Not used in chan_dahdi.c, just analog pvt, but must exist on the DAHDI pvt anyways */
|
||||
tmp->adsi = conf->chan.adsi;
|
||||
tmp->use_smdi = conf->chan.use_smdi;
|
||||
tmp->permhidecallerid = conf->chan.hidecallerid;
|
||||
|
@ -12993,6 +13224,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
|
|||
tmp->ani_wink_time = conf->chan.ani_wink_time;
|
||||
tmp->ani_timeout = conf->chan.ani_timeout;
|
||||
tmp->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch;
|
||||
tmp->reoriginate = conf->chan.reoriginate;
|
||||
tmp->sendcalleridafter = conf->chan.sendcalleridafter;
|
||||
ast_cc_copy_config_params(tmp->cc_params, conf->chan.cc_params);
|
||||
|
||||
|
@ -13102,16 +13334,20 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
|
|||
analog_p->ani_wink_time = conf->chan.ani_wink_time;
|
||||
analog_p->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch;
|
||||
analog_p->permcallwaiting = conf->chan.callwaiting; /* permcallwaiting possibly modified in analog_config_complete */
|
||||
analog_p->calledsubscriberheld = conf->chan.calledsubscriberheld; /* Only actually used in analog pvt, not DAHDI pvt */
|
||||
analog_p->callreturn = conf->chan.callreturn;
|
||||
analog_p->cancallforward = conf->chan.cancallforward;
|
||||
analog_p->canpark = conf->chan.canpark;
|
||||
analog_p->dahditrcallerid = conf->chan.dahditrcallerid;
|
||||
analog_p->immediate = conf->chan.immediate;
|
||||
analog_p->permhidecallerid = conf->chan.permhidecallerid;
|
||||
analog_p->immediatering = conf->chan.immediatering;
|
||||
analog_p->permhidecallerid = conf->chan.hidecallerid; /* hidecallerid is the config setting, not permhidecallerid (~permcallwaiting above) */
|
||||
/* It's not necessary to set analog_p->hidecallerid here, sig_analog will set hidecallerid=permhidecaller before each call */
|
||||
analog_p->pulse = conf->chan.pulse;
|
||||
analog_p->threewaycalling = conf->chan.threewaycalling;
|
||||
analog_p->transfer = conf->chan.transfer;
|
||||
analog_p->transfertobusy = conf->chan.transfertobusy;
|
||||
analog_p->dialmode = conf->chan.dialmode;
|
||||
analog_p->use_callerid = tmp->use_callerid;
|
||||
analog_p->usedistinctiveringdetection = tmp->usedistinctiveringdetection;
|
||||
analog_p->use_smdi = tmp->use_smdi;
|
||||
|
@ -13651,6 +13887,7 @@ static struct ast_channel *dahdi_request(const char *type, struct ast_format_cap
|
|||
struct ast_channel *tmp = NULL;
|
||||
struct dahdi_pvt *exitpvt;
|
||||
int channelmatched = 0;
|
||||
int foundowner = 0;
|
||||
int groupmatched = 0;
|
||||
#if defined(HAVE_PRI) || defined(HAVE_SS7)
|
||||
int transcapdigital = 0;
|
||||
|
@ -13674,6 +13911,10 @@ static struct ast_channel *dahdi_request(const char *type, struct ast_format_cap
|
|||
if (start.roundrobin)
|
||||
round_robin[start.rr_starting_point] = p;
|
||||
|
||||
if (p->owner) {
|
||||
foundowner++;
|
||||
}
|
||||
|
||||
if (is_group_or_channel_match(p, start.span, start.groupmatch, &groupmatched, start.channelmatch, &channelmatched)
|
||||
&& available(&p, channelmatched)) {
|
||||
ast_debug(1, "Using channel %d\n", p->channel);
|
||||
|
@ -13792,7 +14033,7 @@ next:
|
|||
ast_mutex_unlock(&iflock);
|
||||
restart_monitor();
|
||||
if (cause && !tmp) {
|
||||
if (callwait || channelmatched) {
|
||||
if (callwait || (channelmatched && foundowner)) {
|
||||
*cause = AST_CAUSE_BUSY;
|
||||
} else if (groupmatched) {
|
||||
*cause = AST_CAUSE_CONGESTION;
|
||||
|
@ -16371,6 +16612,75 @@ static char *dahdi_set_dnd(struct ast_cli_entry *e, int cmd, struct ast_cli_args
|
|||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static char *dahdi_set_mwi(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
int channel;
|
||||
int on;
|
||||
int override = 1;
|
||||
struct dahdi_pvt *dahdi_chan = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "dahdi set mwi";
|
||||
e->usage =
|
||||
"Usage: dahdi set mwi <chan#> <on|off|reset>\n"
|
||||
" Sets/unsets MWI (Message Waiting Indicator) manually on a channel.\n"
|
||||
" This may be used regardless of whether the channel is assigned any mailboxes.\n"
|
||||
" When active, this setting will override the voicemail status to set MWI.\n"
|
||||
" Once cleared, the voicemail status will resume control of MWI.\n"
|
||||
" Changes are queued for when the channel is idle and persist until cleared.\n"
|
||||
" <chan num> is the channel number\n"
|
||||
" <on|off|reset> Enable, disable, or reset Message Waiting Indicator override?\n"
|
||||
;
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (a->argc != 5)
|
||||
return CLI_SHOWUSAGE;
|
||||
|
||||
if ((channel = atoi(a->argv[3])) <= 0) {
|
||||
ast_cli(a->fd, "Expected channel number, got '%s'\n", a->argv[3]);
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
if (ast_true(a->argv[4])) {
|
||||
on = 1;
|
||||
} else if (ast_false(a->argv[4])) {
|
||||
on = 0;
|
||||
} else if (!strcmp(a->argv[4], "reset")) {
|
||||
override = 0;
|
||||
} else {
|
||||
ast_cli(a->fd, "Expected 'on' or 'off' or 'reset', got '%s'\n", a->argv[4]);
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
ast_mutex_lock(&iflock);
|
||||
for (dahdi_chan = iflist; dahdi_chan; dahdi_chan = dahdi_chan->next) {
|
||||
if (dahdi_chan->channel != channel)
|
||||
continue;
|
||||
|
||||
/* Found the channel. Actually set it */
|
||||
if (override) {
|
||||
dahdi_chan->mwioverride_disposition = on;
|
||||
ast_cli(a->fd, "MWI '%s' queued for channel %d\n", on ? "enable" : "disable", channel);
|
||||
}
|
||||
dahdi_chan->mwioverride_active = override;
|
||||
/* The do_monitor thread will take care of actually sending the MWI
|
||||
* at an appropriate time for the channel. */
|
||||
break;
|
||||
}
|
||||
ast_mutex_unlock(&iflock);
|
||||
|
||||
if (!dahdi_chan) {
|
||||
ast_cli(a->fd, "Unable to find given channel %d\n", channel);
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static struct ast_cli_entry dahdi_cli[] = {
|
||||
AST_CLI_DEFINE(handle_dahdi_show_cadences, "List cadences"),
|
||||
AST_CLI_DEFINE(dahdi_show_channels, "Show active DAHDI channels"),
|
||||
|
@ -16383,6 +16693,7 @@ static struct ast_cli_entry dahdi_cli[] = {
|
|||
AST_CLI_DEFINE(dahdi_set_hwgain, "Set hardware gain on a channel"),
|
||||
AST_CLI_DEFINE(dahdi_set_swgain, "Set software gain on a channel"),
|
||||
AST_CLI_DEFINE(dahdi_set_dnd, "Sets/resets DND (Do Not Disturb) mode on a channel"),
|
||||
AST_CLI_DEFINE(dahdi_set_mwi, "Sets/unsets MWI (Message Waiting Indicator) manually on a channel"),
|
||||
};
|
||||
|
||||
#define TRANSFER 0
|
||||
|
@ -18144,6 +18455,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
confp->chan.cid_start = CID_START_RING;
|
||||
} else if (!strcasecmp(v->name, "threewaycalling")) {
|
||||
confp->chan.threewaycalling = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "threewaysilenthold")) {
|
||||
confp->chan.threewaysilenthold = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "cancallforward")) {
|
||||
confp->chan.cancallforward = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "relaxdtmf")) {
|
||||
|
@ -18186,6 +18499,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
confp->chan.busycount = atoi(v->value);
|
||||
} else if (!strcasecmp(v->name, "busypattern")) {
|
||||
parse_busy_pattern(v, &confp->chan.busy_cadence);
|
||||
} else if (!strcasecmp(v->name, "calledsubscriberheld")) {
|
||||
confp->chan.calledsubscriberheld = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "callprogress")) {
|
||||
confp->chan.callprogress &= ~CALLPROGRESS_PROGRESS;
|
||||
if (ast_true(v->value))
|
||||
|
@ -18275,18 +18590,30 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
} else if (!strcasecmp(v->name, "group")) {
|
||||
confp->chan.group = ast_get_group(v->value);
|
||||
} else if (!strcasecmp(v->name, "callgroup")) {
|
||||
if (!((confp->chan.sig == SIG_FXOKS) || (confp->chan.sig == SIG_FXOGS) || (confp->chan.sig == SIG_FXOLS))) {
|
||||
ast_log(LOG_WARNING, "Only FXO signalled channels may belong to a call group\n");
|
||||
}
|
||||
if (!strcasecmp(v->value, "none"))
|
||||
confp->chan.callgroup = 0;
|
||||
else
|
||||
confp->chan.callgroup = ast_get_group(v->value);
|
||||
} else if (!strcasecmp(v->name, "pickupgroup")) {
|
||||
if (!((confp->chan.sig == SIG_FXOKS) || (confp->chan.sig == SIG_FXOGS) || (confp->chan.sig == SIG_FXOLS))) {
|
||||
ast_log(LOG_WARNING, "Only FXO signalled channels may belong to a pickup group\n");
|
||||
}
|
||||
if (!strcasecmp(v->value, "none"))
|
||||
confp->chan.pickupgroup = 0;
|
||||
else
|
||||
confp->chan.pickupgroup = ast_get_group(v->value);
|
||||
} else if (!strcasecmp(v->name, "namedcallgroup")) {
|
||||
if (!((confp->chan.sig == SIG_FXOKS) || (confp->chan.sig == SIG_FXOGS) || (confp->chan.sig == SIG_FXOLS))) {
|
||||
ast_log(LOG_WARNING, "Only FXO signalled channels may belong to a named call group\n");
|
||||
}
|
||||
confp->chan.named_callgroups = ast_get_namedgroups(v->value);
|
||||
} else if (!strcasecmp(v->name, "namedpickupgroup")) {
|
||||
if (!((confp->chan.sig == SIG_FXOKS) || (confp->chan.sig == SIG_FXOGS) || (confp->chan.sig == SIG_FXOLS))) {
|
||||
ast_log(LOG_WARNING, "Only FXO signalled channels may belong to a named pickup group\n");
|
||||
}
|
||||
confp->chan.named_pickupgroups = ast_get_namedgroups(v->value);
|
||||
} else if (!strcasecmp(v->name, "setvar")) {
|
||||
if (v->value) {
|
||||
|
@ -18306,8 +18633,20 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
}
|
||||
} else if (!strcasecmp(v->name, "immediate")) {
|
||||
confp->chan.immediate = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "immediatering")) {
|
||||
confp->chan.immediatering = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "transfertobusy")) {
|
||||
confp->chan.transfertobusy = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "dialmode")) {
|
||||
if (!strcasecmp(v->value, "pulse")) {
|
||||
confp->chan.dialmode = ANALOG_DIALMODE_PULSE;
|
||||
} else if (!strcasecmp(v->value, "dtmf") || !strcasecmp(v->value, "tone")) {
|
||||
confp->chan.dialmode = ANALOG_DIALMODE_DTMF;
|
||||
} else if (!strcasecmp(v->value, "none")) {
|
||||
confp->chan.dialmode = ANALOG_DIALMODE_NONE;
|
||||
} else {
|
||||
confp->chan.dialmode = ANALOG_DIALMODE_BOTH;
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "mwimonitor")) {
|
||||
confp->chan.mwimonitor_neon = 0;
|
||||
confp->chan.mwimonitor_fsk = 0;
|
||||
|
@ -18406,6 +18745,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
confp->chan.ani_timeout = atoi(v->value);
|
||||
} else if (!strcasecmp(v->name, "hanguponpolarityswitch")) {
|
||||
confp->chan.hanguponpolarityswitch = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "autoreoriginate")) {
|
||||
confp->chan.reoriginate = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "sendcalleridafter")) {
|
||||
confp->chan.sendcalleridafter = atoi(v->value);
|
||||
} else if (!strcasecmp(v->name, "mwimonitornotify")) {
|
||||
|
@ -19411,7 +19752,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
report_alarms = REPORT_SPAN_ALARMS;
|
||||
}
|
||||
} else if (!(options & PROC_DAHDI_OPT_NOWARN) )
|
||||
ast_log(LOG_WARNING, "Ignoring any changes to '%s' (on reload) at line %d.\n", v->name, v->lineno);
|
||||
ast_log(LOG_NOTICE, "Ignoring any changes to '%s' (on reload) at line %d.\n", v->name, v->lineno);
|
||||
}
|
||||
|
||||
if (dahdichan) {
|
||||
|
|
|
@ -146,6 +146,7 @@ struct dahdi_pvt {
|
|||
* \note Set to a couple of nonzero values but it is only tested like a boolean.
|
||||
*/
|
||||
int radio;
|
||||
int dialmode; /*!< Dialing Modes Allowed (Pulse/Tone) */
|
||||
int outsigmod; /*!< Outbound Signalling style (modifier) */
|
||||
int oprmode; /*!< "Operator Services" mode */
|
||||
struct dahdi_pvt *oprpeer; /*!< "Operator Services" peer tech_pvt ptr */
|
||||
|
@ -203,6 +204,13 @@ struct dahdi_pvt {
|
|||
* \note Set from the "busydetect" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int busydetect:1;
|
||||
/*!
|
||||
* \brief TRUE if Called Subscriber held is enabled.
|
||||
* This allows a single incoming call to hold a DAHDI channel up,
|
||||
* allowing a recipient to hang up an extension and pick up another
|
||||
* phone on the same line without disconnecting the call.
|
||||
*/
|
||||
unsigned int calledsubscriberheld:1;
|
||||
/*!
|
||||
* \brief TRUE if call return is enabled.
|
||||
* (*69, if your dialplan doesn't catch this first)
|
||||
|
@ -275,6 +283,14 @@ struct dahdi_pvt {
|
|||
* \note Set from the "hanguponpolarityswitch" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int hanguponpolarityswitch:1;
|
||||
/*!
|
||||
* \brief TRUE if FXS (FXO-signalled) channel should reoriginate for user to make a new call.
|
||||
*/
|
||||
unsigned int reoriginate:1;
|
||||
/*!
|
||||
* \brief Internal flag for if we should actually process a reorigination.
|
||||
*/
|
||||
unsigned int doreoriginate:1;
|
||||
/*! \brief TRUE if DTMF detection needs to be done by hardware. */
|
||||
unsigned int hardwaredtmf:1;
|
||||
/*!
|
||||
|
@ -298,6 +314,12 @@ struct dahdi_pvt {
|
|||
* \note Set from the "immediate" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int immediate:1;
|
||||
/*!
|
||||
* \brief TRUE if audible ringback should be provided
|
||||
* when immediate = yes.
|
||||
* \note Set from the "immediatering" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int immediatering:1;
|
||||
/*! \brief TRUE if in an alarm condition. */
|
||||
unsigned int inalarm:1;
|
||||
/*! \brief TRUE if TDD in MATE mode */
|
||||
|
@ -344,6 +366,11 @@ struct dahdi_pvt {
|
|||
* \note Set from the "threewaycalling" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int threewaycalling:1;
|
||||
/*!
|
||||
* \brief TRUE if a three way dial tone should time out to silence
|
||||
* \note Set from the "threewaysilenthold" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int threewaysilenthold:1;
|
||||
/*!
|
||||
* \brief TRUE if call transfer is enabled
|
||||
* \note For FXS ports (either direct analog or over T1/E1):
|
||||
|
@ -404,6 +431,10 @@ struct dahdi_pvt {
|
|||
unsigned int mwimonitoractive:1;
|
||||
/*! \brief TRUE if a MWI message sending thread is active */
|
||||
unsigned int mwisendactive:1;
|
||||
/*! \brief TRUE if a manual MWI override is active for a channel */
|
||||
unsigned int mwioverride_active:1;
|
||||
/*! \brief Manual MWI disposition (on/off) */
|
||||
unsigned int mwioverride_disposition:1;
|
||||
/*!
|
||||
* \brief TRUE if channel is out of reset and ready
|
||||
* \note Used by SS7. Otherwise set but not used.
|
||||
|
@ -623,6 +654,14 @@ struct dahdi_pvt {
|
|||
* \note Set from the "waitfordialtone" value read in from chan_dahdi.conf
|
||||
*/
|
||||
int waitfordialtone;
|
||||
/*!
|
||||
* \brief Transient variable. Same as waitfordialtone, but temporarily set for a specific call, rather than permanently for the channel.
|
||||
*/
|
||||
int waitfordialtonetemp;
|
||||
/*!
|
||||
* \brief Transient variable. Stored off waitfordialtone duration at runtime.
|
||||
*/
|
||||
int waitfordialtoneduration;
|
||||
/*!
|
||||
* \brief Number of frames to watch for dialtone in incoming calls
|
||||
* \note Set from the "dialtone_detect" value read in from chan_dahdi.conf
|
||||
|
|
|
@ -226,6 +226,16 @@
|
|||
</enum>
|
||||
</enumlist>
|
||||
</info>
|
||||
<info name="Dial_Resource" language="en_US" tech="IAX2">
|
||||
<para>The general syntax is:</para>
|
||||
<para><literal>Dial(IAX2/[username[:password]@]peer[:port][/exten[@context]][/options]</literal></para>
|
||||
<para>IAX2 optionally allows modifiers to be specified after the extension.</para>
|
||||
<enumlist>
|
||||
<enum name="a">
|
||||
<para>Request auto answer (supporting equipment/configuration required)</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</info>
|
||||
<manager name="IAXpeers" language="en_US">
|
||||
<synopsis>
|
||||
List IAX peers.
|
||||
|
@ -388,6 +398,47 @@ static int (*iax2_regfunk)(const char *username, int onoff) = NULL;
|
|||
#define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */
|
||||
#define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */
|
||||
|
||||
/*! \brief Name of effective auth method */
|
||||
static const char *auth_method_labels[] = {
|
||||
[0] = "none",
|
||||
[IAX_AUTH_PLAINTEXT] = "plaintext",
|
||||
[IAX_AUTH_MD5] = "MD5",
|
||||
[IAX_AUTH_RSA] = "RSA",
|
||||
};
|
||||
|
||||
/* Max length is length of |RSA|MD5|plaintext (18 + 1 for NUL = 19) */
|
||||
#define AUTH_METHOD_NAMES_BUFSIZE 19
|
||||
|
||||
/*!
|
||||
* \brief Get names of all auth methods
|
||||
* \param Bit field of auth methods
|
||||
* \param[out] buf Buffer into which to write the names. Must be of size AUTH_METHOD_NAMES_BUFSIZE.
|
||||
* \return Auth methods name
|
||||
*/
|
||||
static char *auth_method_names(int authmethods, char *restrict buf)
|
||||
{
|
||||
char *pos = buf;
|
||||
|
||||
*pos = '\0';
|
||||
|
||||
if (authmethods & IAX_AUTH_RSA) {
|
||||
pos += sprintf(pos, "|RSA");
|
||||
}
|
||||
if (authmethods & IAX_AUTH_MD5) {
|
||||
pos += sprintf(pos, "|MD5");
|
||||
}
|
||||
if (authmethods & IAX_AUTH_PLAINTEXT) {
|
||||
pos += sprintf(pos, "|plaintext");
|
||||
}
|
||||
|
||||
if (pos == buf) { /* No auth methods */
|
||||
strcpy(buf, "none");
|
||||
return buf;
|
||||
}
|
||||
|
||||
return buf + 1; /* Skip leading | */
|
||||
}
|
||||
|
||||
/* if a pvt has encryption setup done and is running on the call */
|
||||
#define IAX_CALLENCRYPTED(pvt) \
|
||||
(ast_test_flag64(pvt, IAX_ENCRYPTED) && ast_test_flag64(pvt, IAX_KEYPOPULATED))
|
||||
|
@ -815,6 +866,8 @@ struct chan_iax2_pvt {
|
|||
int authrej;
|
||||
/*! permitted authentication methods */
|
||||
int authmethods;
|
||||
/*! effective authentication method */
|
||||
int eff_auth_method;
|
||||
/*! permitted encryption methods */
|
||||
int encmethods;
|
||||
/*! Encryption AES-128 Key */
|
||||
|
@ -3407,7 +3460,7 @@ static int send_packet(struct iax_frame *f)
|
|||
|
||||
/* Called with iaxsl held */
|
||||
if (iaxdebug) {
|
||||
ast_debug(3, "Sending %u on %d/%d to %s\n", f->ts, callno, iaxs[callno]->peercallno, ast_sockaddr_stringify(&iaxs[callno]->addr));
|
||||
ast_debug(8, "Sending %u on %d/%d to %s\n", f->ts, callno, iaxs[callno]->peercallno, ast_sockaddr_stringify(&iaxs[callno]->addr));
|
||||
}
|
||||
if (f->transfer) {
|
||||
iax_outputframe(f, NULL, 0, &iaxs[callno]->transfer, f->datalen - sizeof(struct ast_iax2_full_hdr));
|
||||
|
@ -4148,9 +4201,19 @@ static void __get_from_jb(const void *p)
|
|||
now.tv_usec += 1000;
|
||||
|
||||
ms = ast_tvdiff_ms(now, pvt->rxcore);
|
||||
|
||||
voicefmt = ast_format_compatibility_bitfield2format(pvt->voiceformat);
|
||||
if (voicefmt && ms >= (next = jb_next(pvt->jb))) {
|
||||
if (ms >= (next = jb_next(pvt->jb))) {
|
||||
voicefmt = ast_format_compatibility_bitfield2format(pvt->voiceformat);
|
||||
if (!voicefmt) {
|
||||
/* pvt->voiceformat won't be set if we haven't received any voice frames yet.
|
||||
* In this case, fall back to using the format negotiated during call setup,
|
||||
* so we don't stall the jitterbuffer completely. */
|
||||
voicefmt = ast_format_compatibility_bitfield2format(pvt->peerformat);
|
||||
}
|
||||
if (!voicefmt) {
|
||||
/* Really shouldn't happen, but if it does, should be looked into */
|
||||
ast_log(LOG_WARNING, "No voice format and no peer format available on %s, backlogging frame\n", ast_channel_name(pvt->owner));
|
||||
goto cleanup; /* Don't crash if there's no voice format */
|
||||
}
|
||||
ret = jb_get(pvt->jb, &frame, ms, ast_format_get_default_ms(voicefmt));
|
||||
switch(ret) {
|
||||
case JB_OK:
|
||||
|
@ -4192,6 +4255,7 @@ static void __get_from_jb(const void *p)
|
|||
break;
|
||||
}
|
||||
}
|
||||
cleanup:
|
||||
if (pvt)
|
||||
update_jbsched(pvt);
|
||||
ast_mutex_unlock(&iaxsl[callno]);
|
||||
|
@ -6384,7 +6448,7 @@ static int invalid_key(ast_aes_decrypt_key *ecx)
|
|||
#ifdef HAVE_OPENSSL
|
||||
int i;
|
||||
for (i = 0; i < 60; i++) {
|
||||
if (ecx->rd_key[i]) {
|
||||
if (ecx->raw[i]) {
|
||||
return 0; /* stop if we encounter anything non-zero */
|
||||
}
|
||||
}
|
||||
|
@ -8168,7 +8232,7 @@ static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
|
|||
user = user_unref(user);
|
||||
}
|
||||
if (ast_test_flag64(p, IAX_FORCE_ENCRYPT) && !p->encmethods) {
|
||||
ast_log(LOG_NOTICE, "Call Terminated, Incoming call is unencrypted while force encrypt is enabled.\n");
|
||||
ast_log(LOG_WARNING, "Call Terminated, incoming call is unencrypted while force encrypt is enabled.\n");
|
||||
return res;
|
||||
}
|
||||
if (!ast_test_flag(&p->state, IAX_STATE_AUTHENTICATED))
|
||||
|
@ -8194,12 +8258,17 @@ static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
|
|||
key = ast_key_get(keyn, AST_KEY_PUBLIC);
|
||||
if (key && !ast_check_signature(key, p->challenge, rsasecret)) {
|
||||
res = 0;
|
||||
p->eff_auth_method = IAX_AUTH_RSA;
|
||||
break;
|
||||
} else if (!key)
|
||||
ast_log(LOG_WARNING, "requested inkey '%s' for RSA authentication does not exist\n", keyn);
|
||||
} else if (!key) {
|
||||
ast_log(LOG_WARNING, "Requested inkey '%s' for RSA authentication does not exist\n", keyn);
|
||||
}
|
||||
keyn = strsep(&stringp, ":");
|
||||
}
|
||||
ast_free(tmpkey);
|
||||
if (res && authdebug) {
|
||||
ast_log(LOG_WARNING, "No RSA public keys on file matched incoming call\n");
|
||||
}
|
||||
} else if (p->authmethods & IAX_AUTH_MD5) {
|
||||
struct MD5Context md5;
|
||||
unsigned char digest[16];
|
||||
|
@ -8216,12 +8285,19 @@ static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
|
|||
sprintf(requeststr + (x << 1), "%02hhx", digest[x]); /* safe */
|
||||
if (!strcasecmp(requeststr, md5secret)) {
|
||||
res = 0;
|
||||
p->eff_auth_method = IAX_AUTH_MD5;
|
||||
break;
|
||||
} else if (authdebug) {
|
||||
ast_log(LOG_WARNING, "MD5 secret mismatch\n");
|
||||
}
|
||||
}
|
||||
} else if (p->authmethods & IAX_AUTH_PLAINTEXT) {
|
||||
if (!strcmp(secret, p->secret))
|
||||
if (!strcmp(secret, p->secret)) {
|
||||
res = 0;
|
||||
p->eff_auth_method = IAX_AUTH_PLAINTEXT;
|
||||
} else if (authdebug) {
|
||||
ast_log(LOG_WARNING, "Plaintext secret mismatch\n");
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -8396,22 +8472,25 @@ static int authenticate(const char *challenge, const char *secret, const char *k
|
|||
if (!ast_strlen_zero(keyn)) {
|
||||
if (!(authmethods & IAX_AUTH_RSA)) {
|
||||
if (ast_strlen_zero(secret)) {
|
||||
ast_log(LOG_NOTICE, "Asked to authenticate to %s with an RSA key, but they don't allow RSA authentication\n", ast_sockaddr_stringify_addr(addr));
|
||||
ast_log(LOG_WARNING, "Asked to authenticate to %s with an RSA key, but they don't allow RSA authentication\n", ast_sockaddr_stringify_addr(addr));
|
||||
}
|
||||
} else if (ast_strlen_zero(challenge)) {
|
||||
ast_log(LOG_NOTICE, "No challenge provided for RSA authentication to %s\n", ast_sockaddr_stringify_addr(addr));
|
||||
ast_log(LOG_WARNING, "No challenge provided for RSA authentication to %s\n", ast_sockaddr_stringify_addr(addr));
|
||||
} else {
|
||||
char sig[256];
|
||||
struct ast_key *key;
|
||||
key = ast_key_get(keyn, AST_KEY_PRIVATE);
|
||||
if (!key) {
|
||||
ast_log(LOG_NOTICE, "Unable to find private key '%s'\n", keyn);
|
||||
ast_log(LOG_WARNING, "Unable to find private key '%s'\n", keyn);
|
||||
} else {
|
||||
if (ast_sign(key, (char*)challenge, sig)) {
|
||||
ast_log(LOG_NOTICE, "Unable to sign challenge with key\n");
|
||||
ast_log(LOG_WARNING, "Unable to sign challenge with key\n");
|
||||
res = -1;
|
||||
} else {
|
||||
iax_ie_append_str(ied, IAX_IE_RSA_RESULT, sig);
|
||||
if (pvt) {
|
||||
pvt->eff_auth_method = IAX_AUTH_RSA;
|
||||
}
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
|
@ -8444,11 +8523,15 @@ static int authenticate(const char *challenge, const char *secret, const char *k
|
|||
sprintf(digres + (x << 1), "%02hhx", digest[x]); /* safe */
|
||||
if (pvt) {
|
||||
build_encryption_keys(digest, pvt);
|
||||
pvt->eff_auth_method = IAX_AUTH_MD5;
|
||||
}
|
||||
iax_ie_append_str(ied, IAX_IE_MD5_RESULT, digres);
|
||||
res = 0;
|
||||
} else if (authmethods & IAX_AUTH_PLAINTEXT) {
|
||||
iax_ie_append_str(ied, IAX_IE_PASSWORD, secret);
|
||||
if (pvt) {
|
||||
pvt->eff_auth_method = IAX_AUTH_PLAINTEXT;
|
||||
}
|
||||
res = 0;
|
||||
} else
|
||||
ast_log(LOG_WARNING, "No way to send secret to peer '%s' (their methods: %d)\n", ast_sockaddr_stringify_addr(addr), authmethods);
|
||||
|
@ -10363,7 +10446,7 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
}
|
||||
if (ast_test_flag64(iaxs[fr->callno], IAX_ENCRYPTED) && !decrypted) {
|
||||
if (decrypt_frame(fr->callno, fh, &f, &res)) {
|
||||
ast_log(LOG_NOTICE, "Packet Decrypt Failed!\n");
|
||||
ast_log(LOG_WARNING, "Packet Decrypt Failed!\n");
|
||||
ast_variables_destroy(ies.vars);
|
||||
ast_mutex_unlock(&iaxsl[fr->callno]);
|
||||
return 1;
|
||||
|
@ -11290,7 +11373,7 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
}
|
||||
if (authenticate_verify(iaxs[fr->callno], &ies)) {
|
||||
if (authdebug)
|
||||
ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", ast_sockaddr_stringify(&addr),
|
||||
ast_log(LOG_WARNING, "Host %s failed to authenticate as %s\n", ast_sockaddr_stringify(&addr),
|
||||
iaxs[fr->callno]->username);
|
||||
memset(&ied0, 0, sizeof(ied0));
|
||||
auth_fail(fr->callno, IAX_COMMAND_REJECT);
|
||||
|
@ -11303,7 +11386,7 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
exists = 0;
|
||||
if (strcmp(iaxs[fr->callno]->exten, "TBD") && !exists) {
|
||||
if (authdebug)
|
||||
ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n",
|
||||
ast_log(LOG_WARNING, "Rejected connect attempt from %s, request '%s@%s' does not exist\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iaxs[fr->callno]->exten,
|
||||
iaxs[fr->callno]->context);
|
||||
|
@ -11358,12 +11441,12 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
if (!format) {
|
||||
if (authdebug) {
|
||||
if (ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
|
||||
ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested '%s' incompatible with our capability '%s'.\n",
|
||||
ast_log(LOG_WARNING, "Rejected connect attempt from %s, requested '%s' incompatible with our capability '%s'.\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peerformat, &peer_form_buf),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->capability, &cap_buf));
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability '%s'/'%s' incompatible with our capability '%s'.\n",
|
||||
ast_log(LOG_WARNING, "Rejected connect attempt from %s, requested/capability '%s'/'%s' incompatible with our capability '%s'.\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peerformat, &peer_form_buf),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peercapability, &peer_buf),
|
||||
|
@ -11416,12 +11499,12 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
iax2_getformatname_multiple(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability, &cap_buf));
|
||||
if (authdebug) {
|
||||
if (ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
|
||||
ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested '%s' incompatible with our capability '%s'.\n",
|
||||
ast_log(LOG_WARNING, "Rejected connect attempt from %s, requested '%s' incompatible with our capability '%s'.\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peerformat, &peer_form_buf),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->capability, &cap_buf));
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability '%s'/'%s' incompatible with our capability '%s'.\n",
|
||||
ast_log(LOG_WARNING, "Rejected connect attempt from %s, requested/capability '%s'/'%s' incompatible with our capability '%s'.\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peerformat, &peer_form_buf),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peercapability, &peer_buf),
|
||||
|
@ -11445,8 +11528,12 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
iax_ie_append_versioned_uint64(&ied1, IAX_IE_FORMAT2, 0, format);
|
||||
send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
|
||||
if (strcmp(iaxs[fr->callno]->exten, "TBD")) {
|
||||
char authmethodnames[AUTH_METHOD_NAMES_BUFSIZE];
|
||||
ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
|
||||
ast_verb(3, "Accepting AUTHENTICATED call from %s:\n"
|
||||
"%srequested auth methods = (%s),\n"
|
||||
"%sactual auth method = %s,\n"
|
||||
"%sencrypted = %s,\n"
|
||||
"%srequested format = %s,\n"
|
||||
"%srequested prefs = %s,\n"
|
||||
"%sactual format = %s,\n"
|
||||
|
@ -11454,6 +11541,12 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
"%spriority = %s\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
VERBOSE_PREFIX_4,
|
||||
auth_method_names(iaxs[fr->callno]->authmethods, authmethodnames),
|
||||
VERBOSE_PREFIX_4,
|
||||
auth_method_labels[iaxs[fr->callno]->eff_auth_method],
|
||||
VERBOSE_PREFIX_4,
|
||||
IAX_CALLENCRYPTED(iaxs[fr->callno]) ? "yes" : "no",
|
||||
VERBOSE_PREFIX_4,
|
||||
iax2_getformatname(iaxs[fr->callno]->peerformat),
|
||||
VERBOSE_PREFIX_4,
|
||||
caller_pref_buf,
|
||||
|
@ -11522,7 +11615,7 @@ immediatedial:
|
|||
ast_string_field_set(iaxs[fr->callno], exten, ies.called_number ? ies.called_number : "s");
|
||||
if (!ast_exists_extension(NULL, iaxs[fr->callno]->context, iaxs[fr->callno]->exten, 1, iaxs[fr->callno]->cid_num)) {
|
||||
if (authdebug)
|
||||
ast_log(LOG_NOTICE, "Rejected dial attempt from %s, request '%s@%s' does not exist\n",
|
||||
ast_log(LOG_WARNING, "Rejected dial attempt from %s, request '%s@%s' does not exist\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iaxs[fr->callno]->exten,
|
||||
iaxs[fr->callno]->context);
|
||||
|
@ -12033,7 +12126,7 @@ immediatedial:
|
|||
iaxs[fr->callno]->last = fr->ts;
|
||||
#if 1
|
||||
if (iaxdebug)
|
||||
ast_debug(3, "For call=%d, set last=%u\n", fr->callno, fr->ts);
|
||||
ast_debug(8, "For call=%d, set last=%u\n", fr->callno, fr->ts);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -14302,7 +14395,7 @@ static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *dat
|
|||
ast_log(LOG_WARNING, "Timeout waiting for %s exten %s\n", data, exten);
|
||||
}
|
||||
|
||||
if (ast_check_hangup(chan)) {
|
||||
if (chan && ast_check_hangup(chan)) {
|
||||
doabort = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -167,6 +167,14 @@ static struct ast_sip_session_supplement chan_pjsip_ack_supplement = {
|
|||
.incoming_request = chan_pjsip_incoming_ack,
|
||||
};
|
||||
|
||||
static int chan_pjsip_incoming_prack(struct ast_sip_session *session, struct pjsip_rx_data *rdata);
|
||||
|
||||
static struct ast_sip_session_supplement chan_pjsip_prack_supplement = {
|
||||
.method = "PRACK",
|
||||
.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL,
|
||||
.incoming_request = chan_pjsip_incoming_prack,
|
||||
};
|
||||
|
||||
/*! \brief Function called by RTP engine to get local audio RTP peer */
|
||||
static enum ast_rtp_glue_result chan_pjsip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
|
||||
{
|
||||
|
@ -1564,13 +1572,22 @@ static int send_topology_change_refresh(void *data)
|
|||
{
|
||||
struct topology_change_refresh_data *refresh_data = data;
|
||||
struct ast_sip_session *session = refresh_data->session;
|
||||
enum ast_channel_state state = ast_channel_state(session->channel);
|
||||
enum ast_sip_session_refresh_method method = AST_SIP_SESSION_REFRESH_METHOD_INVITE;
|
||||
int ret;
|
||||
SCOPE_ENTER(3, "%s: %s\n", ast_sip_session_get_name(session),
|
||||
ast_str_tmp(256, ast_stream_topology_to_str(refresh_data->media_state->topology, &STR_TMP)));
|
||||
|
||||
/* See RFC 6337, especially section 3.2: If the early media SDP was sent reliably, we are allowed
|
||||
* to send UPDATEs. Only relevant for AST_STATE_RINGING and AST_STATE_RING - if the channel is UP,
|
||||
* re-INVITES can be sent.
|
||||
*/
|
||||
if (session->early_confirmed && (state == AST_STATE_RINGING || state == AST_STATE_RING)) {
|
||||
method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
|
||||
}
|
||||
|
||||
ret = ast_sip_session_refresh(session, NULL, NULL, on_topology_change_response,
|
||||
AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1, refresh_data->media_state);
|
||||
method, 1, refresh_data->media_state);
|
||||
refresh_data->media_state = NULL;
|
||||
topology_change_refresh_data_free(refresh_data);
|
||||
|
||||
|
@ -2513,6 +2530,15 @@ static int hangup(void *data)
|
|||
if (session) {
|
||||
int cause = h_data->cause;
|
||||
|
||||
if (channel->session->active_media_state &&
|
||||
channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO]) {
|
||||
struct ast_sip_session_media *media =
|
||||
channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
|
||||
if (media->rtp) {
|
||||
ast_rtp_instance_set_stats_vars(ast, media->rtp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* It's possible that session_terminate might cause the session to be destroyed
|
||||
* immediately so we need to keep a reference to it so we can NULL session->channel
|
||||
|
@ -2872,97 +2898,6 @@ static int chan_pjsip_sendtext(struct ast_channel *ast, const char *text)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*! \brief Convert SIP hangup causes to Asterisk hangup causes */
|
||||
static int hangup_sip2cause(int cause)
|
||||
{
|
||||
/* Possible values taken from causes.h */
|
||||
|
||||
switch(cause) {
|
||||
case 401: /* Unauthorized */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 403: /* Not found */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 404: /* Not found */
|
||||
return AST_CAUSE_UNALLOCATED;
|
||||
case 405: /* Method not allowed */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 407: /* Proxy authentication required */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 408: /* No reaction */
|
||||
return AST_CAUSE_NO_USER_RESPONSE;
|
||||
case 409: /* Conflict */
|
||||
return AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
|
||||
case 410: /* Gone */
|
||||
return AST_CAUSE_NUMBER_CHANGED;
|
||||
case 411: /* Length required */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 413: /* Request entity too large */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 414: /* Request URI too large */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 415: /* Unsupported media type */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 420: /* Bad extension */
|
||||
return AST_CAUSE_NO_ROUTE_DESTINATION;
|
||||
case 480: /* No answer */
|
||||
return AST_CAUSE_NO_ANSWER;
|
||||
case 481: /* No answer */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 482: /* Loop detected */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 483: /* Too many hops */
|
||||
return AST_CAUSE_NO_ANSWER;
|
||||
case 484: /* Address incomplete */
|
||||
return AST_CAUSE_INVALID_NUMBER_FORMAT;
|
||||
case 485: /* Ambiguous */
|
||||
return AST_CAUSE_UNALLOCATED;
|
||||
case 486: /* Busy everywhere */
|
||||
return AST_CAUSE_BUSY;
|
||||
case 487: /* Request terminated */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 488: /* No codecs approved */
|
||||
return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
|
||||
case 491: /* Request pending */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 493: /* Undecipherable */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 500: /* Server internal failure */
|
||||
return AST_CAUSE_FAILURE;
|
||||
case 501: /* Call rejected */
|
||||
return AST_CAUSE_FACILITY_REJECTED;
|
||||
case 502:
|
||||
return AST_CAUSE_DESTINATION_OUT_OF_ORDER;
|
||||
case 503: /* Service unavailable */
|
||||
return AST_CAUSE_CONGESTION;
|
||||
case 504: /* Gateway timeout */
|
||||
return AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
|
||||
case 505: /* SIP version not supported */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 600: /* Busy everywhere */
|
||||
return AST_CAUSE_USER_BUSY;
|
||||
case 603: /* Decline */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 604: /* Does not exist anywhere */
|
||||
return AST_CAUSE_UNALLOCATED;
|
||||
case 606: /* Not acceptable */
|
||||
return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
|
||||
default:
|
||||
if (cause < 500 && cause >= 400) {
|
||||
/* 4xx class error that is unknown - someting wrong with our request */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
} else if (cause < 600 && cause >= 500) {
|
||||
/* 5xx class error - problem in the remote end */
|
||||
return AST_CAUSE_CONGESTION;
|
||||
} else if (cause < 700 && cause >= 600) {
|
||||
/* 6xx - global errors in the 4xx class */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
}
|
||||
return AST_CAUSE_NORMAL;
|
||||
}
|
||||
/* Never reached */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chan_pjsip_session_begin(struct ast_sip_session *session)
|
||||
{
|
||||
RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
|
||||
|
@ -2993,11 +2928,21 @@ static void chan_pjsip_session_end(struct ast_sip_session *session)
|
|||
SCOPE_EXIT_RTN("No channel\n");
|
||||
}
|
||||
|
||||
|
||||
if (session->active_media_state &&
|
||||
session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO]) {
|
||||
struct ast_sip_session_media *media =
|
||||
session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
|
||||
if (media->rtp) {
|
||||
ast_rtp_instance_set_stats_vars(session->channel, media->rtp);
|
||||
}
|
||||
}
|
||||
|
||||
chan_pjsip_remove_hold(ast_channel_uniqueid(session->channel));
|
||||
|
||||
ast_set_hangupsource(session->channel, ast_channel_name(session->channel), 0);
|
||||
if (!ast_channel_hangupcause(session->channel) && session->inv_session) {
|
||||
int cause = hangup_sip2cause(session->inv_session->cause);
|
||||
int cause = ast_sip_hangup_sip2cause(session->inv_session->cause);
|
||||
|
||||
ast_queue_hangup_with_cause(session->channel, cause);
|
||||
} else {
|
||||
|
@ -3009,11 +2954,11 @@ static void chan_pjsip_session_end(struct ast_sip_session *session)
|
|||
|
||||
static void set_sipdomain_variable(struct ast_sip_session *session)
|
||||
{
|
||||
pjsip_sip_uri *sip_ruri = pjsip_uri_get_uri(session->request_uri);
|
||||
size_t size = pj_strlen(&sip_ruri->host) + 1;
|
||||
const pj_str_t *host = ast_sip_pjsip_uri_get_hostname(session->request_uri);
|
||||
size_t size = pj_strlen(host) + 1;
|
||||
char *domain = ast_alloca(size);
|
||||
|
||||
ast_copy_pj_str(domain, &sip_ruri->host, size);
|
||||
ast_copy_pj_str(domain, host, size);
|
||||
|
||||
pbx_builtin_setvar_helper(session->channel, "SIPDOMAIN", domain);
|
||||
return;
|
||||
|
@ -3191,7 +3136,7 @@ static void chan_pjsip_incoming_response_update_cause(struct ast_sip_session *se
|
|||
snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "SIP %d %.*s", status.code,
|
||||
(int) pj_strlen(&status.reason), pj_strbuf(&status.reason));
|
||||
|
||||
cause_code->ast_cause = hangup_sip2cause(status.code);
|
||||
cause_code->ast_cause = ast_sip_hangup_sip2cause(status.code);
|
||||
ast_queue_control_data(session->channel, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
|
||||
ast_channel_hangupcause_hash_set(session->channel, cause_code, data_size);
|
||||
|
||||
|
@ -3213,6 +3158,7 @@ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct
|
|||
pjsip_rdata_sdp_info *sdp = pjsip_rdata_get_sdp_info(rdata);
|
||||
if (sdp && sdp->body.ptr) {
|
||||
ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session));
|
||||
session->early_confirmed = pjsip_100rel_is_reliable(rdata) == PJ_TRUE;
|
||||
ast_queue_control(session->channel, AST_CONTROL_PROGRESS);
|
||||
} else {
|
||||
ast_trace(-1, "%s: Queueing RINGING\n", ast_sip_session_get_name(session));
|
||||
|
@ -3233,6 +3179,7 @@ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct
|
|||
ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session));
|
||||
ast_trace(1, "%s Method: %.*s Status: %d Queueing PROGRESS with SDP\n", ast_sip_session_get_name(session),
|
||||
(int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code);
|
||||
session->early_confirmed = pjsip_100rel_is_reliable(rdata) == PJ_TRUE;
|
||||
ast_queue_control(session->channel, AST_CONTROL_PROGRESS);
|
||||
}
|
||||
} else {
|
||||
|
@ -3267,6 +3214,18 @@ static int chan_pjsip_incoming_ack(struct ast_sip_session *session, struct pjsip
|
|||
SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(session));
|
||||
}
|
||||
|
||||
static int chan_pjsip_incoming_prack(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
|
||||
{
|
||||
SCOPE_ENTER(3, "%s\n", ast_sip_session_get_name(session));
|
||||
|
||||
if (pj_strcmp2(&rdata->msg_info.msg->line.req.method.name, "PRACK") == 0 &&
|
||||
pjmedia_sdp_neg_get_state(session->inv_session->neg) == PJMEDIA_SDP_NEG_STATE_DONE) {
|
||||
|
||||
session->early_confirmed = 1;
|
||||
}
|
||||
SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(session));
|
||||
}
|
||||
|
||||
static int update_devstate(void *obj, void *arg, int flags)
|
||||
{
|
||||
ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE,
|
||||
|
@ -3307,6 +3266,8 @@ static struct ast_custom_function session_refresh_function = {
|
|||
.write = pjsip_acf_session_refresh_write,
|
||||
};
|
||||
|
||||
static char *app_pjsip_hangup = "PJSIPHangup";
|
||||
|
||||
/*!
|
||||
* \brief Load the module
|
||||
*
|
||||
|
@ -3364,6 +3325,13 @@ static int load_module(void)
|
|||
goto end;
|
||||
}
|
||||
|
||||
if (ast_register_application_xml(app_pjsip_hangup, pjsip_app_hangup)) {
|
||||
ast_log(LOG_WARNING, "Unable to register PJSIPHangup dialplan application\n");
|
||||
goto end;
|
||||
}
|
||||
ast_manager_register_xml(app_pjsip_hangup, EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, pjsip_action_hangup);
|
||||
|
||||
|
||||
ast_sip_register_service(&refer_callback_module);
|
||||
|
||||
ast_sip_session_register_supplement(&chan_pjsip_supplement);
|
||||
|
@ -3379,6 +3347,7 @@ static int load_module(void)
|
|||
ast_sip_session_register_supplement(&call_pickup_supplement);
|
||||
ast_sip_session_register_supplement(&pbx_start_supplement);
|
||||
ast_sip_session_register_supplement(&chan_pjsip_ack_supplement);
|
||||
ast_sip_session_register_supplement(&chan_pjsip_prack_supplement);
|
||||
|
||||
if (pjsip_channel_cli_register()) {
|
||||
ast_log(LOG_ERROR, "Unable to register PJSIP Channel CLI\n");
|
||||
|
@ -3398,6 +3367,7 @@ end:
|
|||
ao2_cleanup(pjsip_uids_onhold);
|
||||
pjsip_uids_onhold = NULL;
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement);
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_prack_supplement);
|
||||
ast_sip_session_unregister_supplement(&pbx_start_supplement);
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response);
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_supplement);
|
||||
|
@ -3409,6 +3379,9 @@ end:
|
|||
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
|
||||
ast_custom_function_unregister(&chan_pjsip_parse_uri_function);
|
||||
ast_custom_function_unregister(&session_refresh_function);
|
||||
ast_unregister_application(app_pjsip_hangup);
|
||||
ast_manager_unregister(app_pjsip_hangup);
|
||||
|
||||
ast_channel_unregister(&chan_pjsip_tech);
|
||||
ast_rtp_glue_unregister(&chan_pjsip_rtp_glue);
|
||||
|
||||
|
@ -3427,6 +3400,7 @@ static int unload_module(void)
|
|||
ast_sip_session_unregister_supplement(&chan_pjsip_supplement);
|
||||
ast_sip_session_unregister_supplement(&pbx_start_supplement);
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement);
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_prack_supplement);
|
||||
ast_sip_session_unregister_supplement(&call_pickup_supplement);
|
||||
|
||||
ast_sip_unregister_service(&refer_callback_module);
|
||||
|
@ -3437,6 +3411,8 @@ static int unload_module(void)
|
|||
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
|
||||
ast_custom_function_unregister(&chan_pjsip_parse_uri_function);
|
||||
ast_custom_function_unregister(&session_refresh_function);
|
||||
ast_unregister_application(app_pjsip_hangup);
|
||||
ast_manager_unregister(app_pjsip_hangup);
|
||||
|
||||
ast_channel_unregister(&chan_pjsip_tech);
|
||||
ao2_ref(chan_pjsip_tech.capabilities, -1);
|
||||
|
|
|
@ -129,7 +129,8 @@ static struct ast_format *derive_format_from_cap(struct ast_format_cap *cap)
|
|||
* assignments. Signed linear @ 8kHz does not map, so if that is our
|
||||
* only capability, we force μ-law instead.
|
||||
*/
|
||||
fmt = ast_format_ulaw;
|
||||
ao2_ref(fmt, -1);
|
||||
fmt = ao2_bump(ast_format_ulaw);
|
||||
}
|
||||
|
||||
return fmt;
|
||||
|
@ -211,7 +212,7 @@ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_fo
|
|||
}
|
||||
|
||||
chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", assignedids,
|
||||
requestor, 0, "MulticastRTP/%p", instance);
|
||||
requestor, 0, "MulticastRTP/%s-%p", args.destination, instance);
|
||||
if (!chan) {
|
||||
ast_rtp_instance_destroy(instance);
|
||||
goto failure;
|
||||
|
@ -249,6 +250,7 @@ failure:
|
|||
enum {
|
||||
OPT_RTP_CODEC = (1 << 0),
|
||||
OPT_RTP_ENGINE = (1 << 1),
|
||||
OPT_RTP_GLUE = (1 << 2),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -263,8 +265,14 @@ AST_APP_OPTIONS(unicast_rtp_options, BEGIN_OPTIONS
|
|||
AST_APP_OPTION_ARG('c', OPT_RTP_CODEC, OPT_ARG_RTP_CODEC),
|
||||
/*! Set the RTP engine to use for unicast RTP */
|
||||
AST_APP_OPTION_ARG('e', OPT_RTP_ENGINE, OPT_ARG_RTP_ENGINE),
|
||||
/*! Provide RTP glue for the channel */
|
||||
AST_APP_OPTION('g', OPT_RTP_GLUE),
|
||||
END_OPTIONS );
|
||||
|
||||
static const struct ast_datastore_info chan_rtp_datastore_info = {
|
||||
.type = "CHAN_RTP_GLUE",
|
||||
};
|
||||
|
||||
/*! \brief Function called when we should prepare to call the unicast destination */
|
||||
static struct ast_channel *unicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
|
||||
{
|
||||
|
@ -372,6 +380,13 @@ static struct ast_channel *unicast_rtp_request(const char *type, struct ast_form
|
|||
|
||||
ast_channel_tech_set(chan, &unicast_rtp_tech);
|
||||
|
||||
if (ast_test_flag(&opts, OPT_RTP_GLUE)) {
|
||||
struct ast_datastore *datastore;
|
||||
if ((datastore = ast_datastore_alloc(&chan_rtp_datastore_info, NULL))) {
|
||||
ast_channel_datastore_add(chan, datastore);
|
||||
}
|
||||
}
|
||||
|
||||
ast_format_cap_append(caps, fmt, 0);
|
||||
ast_channel_nativeformats_set(chan, caps);
|
||||
ast_channel_set_writeformat(chan, fmt);
|
||||
|
@ -381,9 +396,9 @@ static struct ast_channel *unicast_rtp_request(const char *type, struct ast_form
|
|||
|
||||
ast_channel_tech_pvt_set(chan, instance);
|
||||
|
||||
ast_rtp_instance_get_local_address(instance, &local_address);
|
||||
pbx_builtin_setvar_helper(chan, "UNICASTRTP_LOCAL_ADDRESS",
|
||||
ast_sockaddr_stringify_addr(&local_address));
|
||||
ast_rtp_instance_get_local_address(instance, &local_address);
|
||||
pbx_builtin_setvar_helper(chan, "UNICASTRTP_LOCAL_PORT",
|
||||
ast_sockaddr_stringify_port(&local_address));
|
||||
|
||||
|
@ -401,6 +416,61 @@ failure:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*! \brief Function called by RTP engine to get peer capabilities */
|
||||
static void chan_rtp_get_codec(struct ast_channel *chan, struct ast_format_cap *result)
|
||||
{
|
||||
SCOPE_ENTER(1, "%s Native formats %s\n", ast_channel_name(chan),
|
||||
ast_str_tmp(AST_FORMAT_CAP_NAMES_LEN, ast_format_cap_get_names(ast_channel_nativeformats(chan), &STR_TMP)));
|
||||
ast_format_cap_append_from_cap(result, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_UNKNOWN);
|
||||
SCOPE_EXIT_RTN();
|
||||
}
|
||||
|
||||
/*! \brief Function called by RTP engine to change where the remote party should send media.
|
||||
*
|
||||
* chan_rtp is not able to actually update the peer, so this function has no effect.
|
||||
* */
|
||||
static int chan_rtp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *tpeer, const struct ast_format_cap *cap, int nat_active)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! \brief Function called by RTP engine to get local audio RTP peer */
|
||||
static enum ast_rtp_glue_result chan_rtp_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
|
||||
{
|
||||
return AST_RTP_GLUE_RESULT_FORBID;
|
||||
}
|
||||
|
||||
/*! \brief Function called by RTP engine to get local audio RTP peer */
|
||||
static enum ast_rtp_glue_result chan_rtp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
|
||||
{
|
||||
struct ast_rtp_instance *rtp_instance = ast_channel_tech_pvt(chan);
|
||||
struct ast_datastore *datastore;
|
||||
|
||||
if (!rtp_instance) {
|
||||
return AST_RTP_GLUE_RESULT_FORBID;
|
||||
}
|
||||
|
||||
if ((datastore = ast_channel_datastore_find(chan, &chan_rtp_datastore_info, NULL))) {
|
||||
ao2_ref(datastore, -1);
|
||||
|
||||
*instance = rtp_instance;
|
||||
ao2_ref(*instance, +1);
|
||||
|
||||
return AST_RTP_GLUE_RESULT_LOCAL;
|
||||
}
|
||||
|
||||
return AST_RTP_GLUE_RESULT_FORBID;
|
||||
}
|
||||
|
||||
/*! \brief Local glue for interacting with the RTP engine core */
|
||||
static struct ast_rtp_glue unicast_rtp_glue = {
|
||||
.type = "UnicastRTP",
|
||||
.get_rtp_info = chan_rtp_get_rtp_peer,
|
||||
.get_vrtp_info = chan_rtp_get_vrtp_peer,
|
||||
.get_codec = chan_rtp_get_codec,
|
||||
.update_peer = chan_rtp_set_rtp_peer,
|
||||
};
|
||||
|
||||
/*! \brief Function called when our module is unloaded */
|
||||
static int unload_module(void)
|
||||
{
|
||||
|
@ -412,6 +482,8 @@ static int unload_module(void)
|
|||
ao2_cleanup(unicast_rtp_tech.capabilities);
|
||||
unicast_rtp_tech.capabilities = NULL;
|
||||
|
||||
ast_rtp_glue_unregister(&unicast_rtp_glue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -421,6 +493,9 @@ static int load_module(void)
|
|||
if (!(multicast_rtp_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
ast_rtp_glue_register(&unicast_rtp_glue);
|
||||
|
||||
ast_format_cap_append_by_type(multicast_rtp_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN);
|
||||
if (ast_channel_register(&multicast_rtp_tech)) {
|
||||
ast_log(LOG_ERROR, "Unable to register channel class 'MulticastRTP'\n");
|
||||
|
|
|
@ -424,7 +424,7 @@ static void dump_ies(unsigned char *iedata, int len)
|
|||
|
||||
if (len < 2)
|
||||
return;
|
||||
while(len > 2) {
|
||||
while(len >= 2) {
|
||||
ie = iedata[0];
|
||||
ielen = iedata[1];
|
||||
if (ielen + 2> len) {
|
||||
|
@ -1063,7 +1063,7 @@ int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
|
|||
if (len == (int)sizeof(unsigned int)) {
|
||||
ies->calling_ani2 = ntohl(get_unaligned_uint32(data + 2));
|
||||
} else {
|
||||
snprintf(tmp, (int)sizeof(tmp), "callingani2 was %d long: %s\n", len, data + 2);
|
||||
snprintf(tmp, sizeof(tmp), "Expected callingani2 to be %zu bytes but was %d\n", sizeof(unsigned int), len);
|
||||
errorf(tmp);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -343,6 +343,7 @@ static int cli_channelstats_print_body(void *obj, void *arg, int flags)
|
|||
struct ast_sip_session *session;
|
||||
struct ast_sip_session_media *media;
|
||||
struct ast_rtp_instance_stats stats;
|
||||
struct ast_stream *stream;
|
||||
char *print_name = NULL;
|
||||
char *print_time = alloca(32);
|
||||
char codec_in_use[7];
|
||||
|
@ -359,16 +360,29 @@ static int cli_channelstats_print_body(void *obj, void *arg, int flags)
|
|||
|
||||
cpvt = ast_channel_tech_pvt(channel);
|
||||
session = cpvt ? cpvt->session : NULL;
|
||||
if (!session) {
|
||||
|
||||
if (!session
|
||||
|| !session->active_media_state
|
||||
|| !session->active_media_state->topology) {
|
||||
ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->base->name);
|
||||
ast_channel_unlock(channel);
|
||||
ao2_cleanup(channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream = ast_stream_topology_get_first_stream_by_type(
|
||||
session->active_media_state->topology, AST_MEDIA_TYPE_AUDIO);
|
||||
|
||||
if (!stream) {
|
||||
ast_str_append(&context->output_buffer, 0, " %s no audio streams\n", snapshot->base->name);
|
||||
ast_channel_unlock(channel);
|
||||
ao2_cleanup(channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
media = session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
|
||||
if (!media || !media->rtp) {
|
||||
ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->base->name);
|
||||
if (!media || media->type != AST_MEDIA_TYPE_AUDIO || !media->rtp) {
|
||||
ast_str_append(&context->output_buffer, 0, " %s corrupted default audio session\n", snapshot->base->name);
|
||||
ast_channel_unlock(channel);
|
||||
ao2_cleanup(channel);
|
||||
return 0;
|
||||
|
|
|
@ -29,508 +29,6 @@
|
|||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<function name="PJSIP_DIAL_CONTACTS" language="en_US">
|
||||
<synopsis>
|
||||
Return a dial string for dialing all contacts on an AOR.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="endpoint" required="true">
|
||||
<para>Name of the endpoint</para>
|
||||
</parameter>
|
||||
<parameter name="aor" required="false">
|
||||
<para>Name of an AOR to use, if not specified the configured AORs on the endpoint are used</para>
|
||||
</parameter>
|
||||
<parameter name="request_user" required="false">
|
||||
<para>Optional request user to use in the request URI</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Returns a properly formatted dial string for dialing all contacts on an AOR.</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_MEDIA_OFFER" language="en_US">
|
||||
<synopsis>
|
||||
Media and codec offerings to be set on an outbound SIP channel prior to dialing.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="media" required="true">
|
||||
<para>types of media offered</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the codecs offered based upon the media choice.</para>
|
||||
<para>When written, sets the codecs to offer when an outbound dial attempt is made,
|
||||
or when a session refresh is sent using <replaceable>PJSIP_SEND_SESSION_REFRESH</replaceable>.
|
||||
</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">PJSIP_SEND_SESSION_REFRESH</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="PJSIP_DTMF_MODE" language="en_US">
|
||||
<since>
|
||||
<version>13.18.0</version>
|
||||
<version>14.7.0</version>
|
||||
<version>15.1.0</version>
|
||||
<version>16.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Get or change the DTMF mode for a SIP call.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the current DTMF mode</para>
|
||||
<para>When written, sets the current DTMF mode</para>
|
||||
<para>This function uses the same DTMF mode naming as the dtmf_mode configuration option</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_MOH_PASSTHROUGH" language="en_US">
|
||||
<synopsis>
|
||||
Get or change the on-hold behavior for a SIP call.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the current moh passthrough mode</para>
|
||||
<para>When written, sets the current moh passthrough mode</para>
|
||||
<para>If <replaceable>yes</replaceable>, on-hold re-INVITEs are sent. If <replaceable>no</replaceable>, music on hold is generated.</para>
|
||||
<para>This function can be used to override the moh_passthrough configuration option</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_SEND_SESSION_REFRESH" language="en_US">
|
||||
<since>
|
||||
<version>13.12.0</version>
|
||||
<version>14.1.0</version>
|
||||
<version>15.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="update_type" required="false">
|
||||
<para>The type of update to send. Default is <literal>invite</literal>.</para>
|
||||
<enumlist>
|
||||
<enum name="invite">
|
||||
<para>Send the session refresh as a re-INVITE.</para>
|
||||
</enum>
|
||||
<enum name="update">
|
||||
<para>Send the session refresh as an UPDATE.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This function will cause the PJSIP stack to immediately refresh
|
||||
the media session for the channel. This will be done using either a
|
||||
re-INVITE (default) or an UPDATE request.
|
||||
</para>
|
||||
<para>This is most useful when combined with the <replaceable>PJSIP_MEDIA_OFFER</replaceable>
|
||||
dialplan function, as it allows the formats in use on a channel to be
|
||||
re-negotiated after call setup.</para>
|
||||
<warning>
|
||||
<para>The formats the endpoint supports are <emphasis>not</emphasis>
|
||||
checked or enforced by this function. Using this function to offer
|
||||
formats not supported by the endpoint <emphasis>may</emphasis> result
|
||||
in a loss of media.</para>
|
||||
</warning>
|
||||
<example title="Re-negotiate format to g722">
|
||||
; Within some existing extension on an answered channel
|
||||
same => n,Set(PJSIP_MEDIA_OFFER(audio)=!all,g722)
|
||||
same => n,Set(PJSIP_SEND_SESSION_REFRESH()=invite)
|
||||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">PJSIP_MEDIA_OFFER</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="PJSIP_PARSE_URI" language="en_US">
|
||||
<since>
|
||||
<version>13.24.0</version>
|
||||
<version>16.1.0</version>
|
||||
<version>17.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Parse an uri and return a type part of the URI.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="uri" required="true">
|
||||
<para>URI to parse</para>
|
||||
</parameter>
|
||||
<parameter name="type" required="true">
|
||||
<para>The <literal>type</literal> parameter specifies which URI part to read</para>
|
||||
<enumlist>
|
||||
<enum name="display">
|
||||
<para>Display name.</para>
|
||||
</enum>
|
||||
<enum name="scheme">
|
||||
<para>URI scheme.</para>
|
||||
</enum>
|
||||
<enum name="user">
|
||||
<para>User part.</para>
|
||||
</enum>
|
||||
<enum name="passwd">
|
||||
<para>Password part.</para>
|
||||
</enum>
|
||||
<enum name="host">
|
||||
<para>Host part.</para>
|
||||
</enum>
|
||||
<enum name="port">
|
||||
<para>Port number, or zero.</para>
|
||||
</enum>
|
||||
<enum name="user_param">
|
||||
<para>User parameter.</para>
|
||||
</enum>
|
||||
<enum name="method_param">
|
||||
<para>Method parameter.</para>
|
||||
</enum>
|
||||
<enum name="transport_param">
|
||||
<para>Transport parameter.</para>
|
||||
</enum>
|
||||
<enum name="ttl_param">
|
||||
<para>TTL param, or -1.</para>
|
||||
</enum>
|
||||
<enum name="lr_param">
|
||||
<para>Loose routing param, or zero.</para>
|
||||
</enum>
|
||||
<enum name="maddr_param">
|
||||
<para>Maddr param.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Parse an URI and return a specified part of the URI.</para>
|
||||
</description>
|
||||
</function>
|
||||
<info name="CHANNEL" language="en_US" tech="PJSIP">
|
||||
<enumlist>
|
||||
<enum name="rtp">
|
||||
<para>R/O Retrieve media related information.</para>
|
||||
<parameter name="type" required="true">
|
||||
<para>When <replaceable>rtp</replaceable> is specified, the
|
||||
<literal>type</literal> parameter must be provided. It specifies
|
||||
which RTP parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="src">
|
||||
<para>Retrieve the local address for RTP.</para>
|
||||
</enum>
|
||||
<enum name="dest">
|
||||
<para>Retrieve the remote address for RTP.</para>
|
||||
</enum>
|
||||
<enum name="direct">
|
||||
<para>If direct media is enabled, this address is the remote address
|
||||
used for RTP.</para>
|
||||
</enum>
|
||||
<enum name="secure">
|
||||
<para>Whether or not the media stream is encrypted.</para>
|
||||
<enumlist>
|
||||
<enum name="0">
|
||||
<para>The media stream is not encrypted.</para>
|
||||
</enum>
|
||||
<enum name="1">
|
||||
<para>The media stream is encrypted.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="hold">
|
||||
<para>Whether or not the media stream is currently restricted
|
||||
due to a call hold.</para>
|
||||
<enumlist>
|
||||
<enum name="0">
|
||||
<para>The media stream is not held.</para>
|
||||
</enum>
|
||||
<enum name="1">
|
||||
<para>The media stream is held.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="media_type" required="false">
|
||||
<para>When <replaceable>rtp</replaceable> is specified, the
|
||||
<literal>media_type</literal> parameter may be provided. It specifies
|
||||
which media stream the chosen RTP parameter should be retrieved
|
||||
from.</para>
|
||||
<enumlist>
|
||||
<enum name="audio">
|
||||
<para>Retrieve information from the audio media stream.</para>
|
||||
<note><para>If not specified, <literal>audio</literal> is used
|
||||
by default.</para></note>
|
||||
</enum>
|
||||
<enum name="video">
|
||||
<para>Retrieve information from the video media stream.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
<enum name="rtcp">
|
||||
<para>R/O Retrieve RTCP statistics.</para>
|
||||
<parameter name="statistic" required="true">
|
||||
<para>When <replaceable>rtcp</replaceable> is specified, the
|
||||
<literal>statistic</literal> parameter must be provided. It specifies
|
||||
which RTCP statistic parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="all">
|
||||
<para>Retrieve a summary of all RTCP statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="ssrc">
|
||||
<para>Our Synchronization Source identifier</para>
|
||||
</enum>
|
||||
<enum name="themssrc">
|
||||
<para>Their Synchronization Source identifier</para>
|
||||
</enum>
|
||||
<enum name="lp">
|
||||
<para>Our lost packet count</para>
|
||||
</enum>
|
||||
<enum name="rxjitter">
|
||||
<para>Received packet jitter</para>
|
||||
</enum>
|
||||
<enum name="rxcount">
|
||||
<para>Received packet count</para>
|
||||
</enum>
|
||||
<enum name="txjitter">
|
||||
<para>Transmitted packet jitter</para>
|
||||
</enum>
|
||||
<enum name="txcount">
|
||||
<para>Transmitted packet count</para>
|
||||
</enum>
|
||||
<enum name="rlp">
|
||||
<para>Remote lost packet count</para>
|
||||
</enum>
|
||||
<enum name="rtt">
|
||||
<para>Round trip time</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_jitter">
|
||||
<para>Retrieve a summary of all RTCP Jitter statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrxjitter">
|
||||
<para>Our minimum jitter</para>
|
||||
</enum>
|
||||
<enum name="maxrxjitter">
|
||||
<para>Our max jitter</para>
|
||||
</enum>
|
||||
<enum name="avgrxjitter">
|
||||
<para>Our average jitter</para>
|
||||
</enum>
|
||||
<enum name="stdevrxjitter">
|
||||
<para>Our jitter standard deviation</para>
|
||||
</enum>
|
||||
<enum name="reported_minjitter">
|
||||
<para>Their minimum jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_maxjitter">
|
||||
<para>Their max jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_avgjitter">
|
||||
<para>Their average jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_stdevjitter">
|
||||
<para>Their jitter standard deviation</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_loss">
|
||||
<para>Retrieve a summary of all RTCP packet loss statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrxlost">
|
||||
<para>Our minimum lost packets</para>
|
||||
</enum>
|
||||
<enum name="maxrxlost">
|
||||
<para>Our max lost packets</para>
|
||||
</enum>
|
||||
<enum name="avgrxlost">
|
||||
<para>Our average lost packets</para>
|
||||
</enum>
|
||||
<enum name="stdevrxlost">
|
||||
<para>Our lost packets standard deviation</para>
|
||||
</enum>
|
||||
<enum name="reported_minlost">
|
||||
<para>Their minimum lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_maxlost">
|
||||
<para>Their max lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_avglost">
|
||||
<para>Their average lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_stdevlost">
|
||||
<para>Their lost packets standard deviation</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_rtt">
|
||||
<para>Retrieve a summary of all RTCP round trip time information.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrtt">
|
||||
<para>Minimum round trip time</para>
|
||||
</enum>
|
||||
<enum name="maxrtt">
|
||||
<para>Maximum round trip time</para>
|
||||
</enum>
|
||||
<enum name="avgrtt">
|
||||
<para>Average round trip time</para>
|
||||
</enum>
|
||||
<enum name="stdevrtt">
|
||||
<para>Standard deviation round trip time</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="txcount"><para>Transmitted packet count</para></enum>
|
||||
<enum name="rxcount"><para>Received packet count</para></enum>
|
||||
<enum name="txjitter"><para>Transmitted packet jitter</para></enum>
|
||||
<enum name="rxjitter"><para>Received packet jitter</para></enum>
|
||||
<enum name="remote_maxjitter"><para>Their max jitter</para></enum>
|
||||
<enum name="remote_minjitter"><para>Their minimum jitter</para></enum>
|
||||
<enum name="remote_normdevjitter"><para>Their average jitter</para></enum>
|
||||
<enum name="remote_stdevjitter"><para>Their jitter standard deviation</para></enum>
|
||||
<enum name="local_maxjitter"><para>Our max jitter</para></enum>
|
||||
<enum name="local_minjitter"><para>Our minimum jitter</para></enum>
|
||||
<enum name="local_normdevjitter"><para>Our average jitter</para></enum>
|
||||
<enum name="local_stdevjitter"><para>Our jitter standard deviation</para></enum>
|
||||
<enum name="txploss"><para>Transmitted packet loss</para></enum>
|
||||
<enum name="rxploss"><para>Received packet loss</para></enum>
|
||||
<enum name="remote_maxrxploss"><para>Their max lost packets</para></enum>
|
||||
<enum name="remote_minrxploss"><para>Their minimum lost packets</para></enum>
|
||||
<enum name="remote_normdevrxploss"><para>Their average lost packets</para></enum>
|
||||
<enum name="remote_stdevrxploss"><para>Their lost packets standard deviation</para></enum>
|
||||
<enum name="local_maxrxploss"><para>Our max lost packets</para></enum>
|
||||
<enum name="local_minrxploss"><para>Our minimum lost packets</para></enum>
|
||||
<enum name="local_normdevrxploss"><para>Our average lost packets</para></enum>
|
||||
<enum name="local_stdevrxploss"><para>Our lost packets standard deviation</para></enum>
|
||||
<enum name="rtt"><para>Round trip time</para></enum>
|
||||
<enum name="maxrtt"><para>Maximum round trip time</para></enum>
|
||||
<enum name="minrtt"><para>Minimum round trip time</para></enum>
|
||||
<enum name="normdevrtt"><para>Average round trip time</para></enum>
|
||||
<enum name="stdevrtt"><para>Standard deviation round trip time</para></enum>
|
||||
<enum name="local_ssrc"><para>Our Synchronization Source identifier</para></enum>
|
||||
<enum name="remote_ssrc"><para>Their Synchronization Source identifier</para></enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="media_type" required="false">
|
||||
<para>When <replaceable>rtcp</replaceable> is specified, the
|
||||
<literal>media_type</literal> parameter may be provided. It specifies
|
||||
which media stream the chosen RTCP parameter should be retrieved
|
||||
from.</para>
|
||||
<enumlist>
|
||||
<enum name="audio">
|
||||
<para>Retrieve information from the audio media stream.</para>
|
||||
<note><para>If not specified, <literal>audio</literal> is used
|
||||
by default.</para></note>
|
||||
</enum>
|
||||
<enum name="video">
|
||||
<para>Retrieve information from the video media stream.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
<enum name="endpoint">
|
||||
<para>R/O The name of the endpoint associated with this channel.
|
||||
Use the <replaceable>PJSIP_ENDPOINT</replaceable> function to obtain
|
||||
further endpoint related information.</para>
|
||||
</enum>
|
||||
<enum name="contact">
|
||||
<para>R/O The name of the contact associated with this channel.
|
||||
Use the <replaceable>PJSIP_CONTACT</replaceable> function to obtain
|
||||
further contact related information. Note this may not be present and if so
|
||||
is only available on outgoing legs.</para>
|
||||
</enum>
|
||||
<enum name="aor">
|
||||
<para>R/O The name of the AOR associated with this channel.
|
||||
Use the <replaceable>PJSIP_AOR</replaceable> function to obtain
|
||||
further AOR related information. Note this may not be present and if so
|
||||
is only available on outgoing legs.</para>
|
||||
</enum>
|
||||
<enum name="pjsip">
|
||||
<para>R/O Obtain information about the current PJSIP channel and its
|
||||
session.</para>
|
||||
<parameter name="type" required="true">
|
||||
<para>When <replaceable>pjsip</replaceable> is specified, the
|
||||
<literal>type</literal> parameter must be provided. It specifies
|
||||
which signalling parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="call-id">
|
||||
<para>The SIP call-id.</para>
|
||||
</enum>
|
||||
<enum name="secure">
|
||||
<para>Whether or not the signalling uses a secure transport.</para>
|
||||
<enumlist>
|
||||
<enum name="0"><para>The signalling uses a non-secure transport.</para></enum>
|
||||
<enum name="1"><para>The signalling uses a secure transport.</para></enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="target_uri">
|
||||
<para>The contact URI where requests are sent.</para>
|
||||
</enum>
|
||||
<enum name="local_uri">
|
||||
<para>The local URI.</para>
|
||||
</enum>
|
||||
<enum name="local_tag">
|
||||
<para>Tag in From header</para>
|
||||
</enum>
|
||||
<enum name="remote_uri">
|
||||
<para>The remote URI.</para>
|
||||
</enum>
|
||||
<enum name="remote_tag">
|
||||
<para>Tag in To header</para>
|
||||
</enum>
|
||||
<enum name="request_uri">
|
||||
<para>The request URI of the incoming <literal>INVITE</literal>
|
||||
associated with the creation of this channel.</para>
|
||||
</enum>
|
||||
<enum name="t38state">
|
||||
<para>The current state of any T.38 fax on this channel.</para>
|
||||
<enumlist>
|
||||
<enum name="DISABLED"><para>T.38 faxing is disabled on this channel.</para></enum>
|
||||
<enum name="LOCAL_REINVITE"><para>Asterisk has sent a <literal>re-INVITE</literal> to the remote end to initiate a T.38 fax.</para></enum>
|
||||
<enum name="REMOTE_REINVITE"><para>The remote end has sent a <literal>re-INVITE</literal> to Asterisk to initiate a T.38 fax.</para></enum>
|
||||
<enum name="ENABLED"><para>A T.38 fax session has been enabled.</para></enum>
|
||||
<enum name="REJECTED"><para>A T.38 fax session was attempted but was rejected.</para></enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="local_addr">
|
||||
<para>On inbound calls, the full IP address and port number that
|
||||
the <literal>INVITE</literal> request was received on. On outbound
|
||||
calls, the full IP address and port number that the <literal>INVITE</literal>
|
||||
request was transmitted from.</para>
|
||||
</enum>
|
||||
<enum name="remote_addr">
|
||||
<para>On inbound calls, the full IP address and port number that
|
||||
the <literal>INVITE</literal> request was received from. On outbound
|
||||
calls, the full IP address and port number that the <literal>INVITE</literal>
|
||||
request was transmitted to.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</info>
|
||||
<info name="CHANNEL_EXAMPLES" language="en_US" tech="PJSIP">
|
||||
<example title="PJSIP specific CHANNEL examples">
|
||||
; Log the current Call-ID
|
||||
same => n,Log(NOTICE, ${CHANNEL(pjsip,call-id)})
|
||||
|
||||
; Log the destination address of the audio stream
|
||||
same => n,Log(NOTICE, ${CHANNEL(rtp,dest)})
|
||||
|
||||
; Store the round-trip time associated with a
|
||||
; video stream in the CDR field video-rtt
|
||||
same => n,Set(CDR(video-rtt)=${CHANNEL(rtcp,rtt,video)})
|
||||
</example>
|
||||
</info>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <pjsip.h>
|
||||
|
@ -541,6 +39,7 @@
|
|||
#include "asterisk/module.h"
|
||||
#include "asterisk/acl.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/conversions.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/stream.h"
|
||||
#include "asterisk/format.h"
|
||||
|
@ -678,6 +177,8 @@ static int channel_read_rtcp(struct ast_channel *chan, const char *type, const c
|
|||
stat_field = AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT;
|
||||
} else if (!strcasecmp(type, "all_loss")) {
|
||||
stat_field = AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS;
|
||||
} else if (!strcasecmp(type, "all_mes")) {
|
||||
stat_field = AST_RTP_INSTANCE_STAT_FIELD_QUALITY_MES;
|
||||
}
|
||||
|
||||
if (!ast_rtp_instance_get_quality(media->rtp, stat_field, buf, buflen)) {
|
||||
|
@ -724,6 +225,16 @@ static int channel_read_rtcp(struct ast_channel *chan, const char *type, const c
|
|||
{ "stdevrtt", DBL, { .d8 = &stats.stdevrtt, }, },
|
||||
{ "local_ssrc", INT, { .i4 = &stats.local_ssrc, }, },
|
||||
{ "remote_ssrc", INT, { .i4 = &stats.remote_ssrc, }, },
|
||||
{ "txmes", DBL, { .d8 = &stats.txmes, }, },
|
||||
{ "rxmes", DBL, { .d8 = &stats.rxmes, }, },
|
||||
{ "remote_maxmes", DBL, { .d8 = &stats.remote_maxmes, }, },
|
||||
{ "remote_minmes", DBL, { .d8 = &stats.remote_minmes, }, },
|
||||
{ "remote_normdevmes", DBL, { .d8 = &stats.remote_normdevmes, }, },
|
||||
{ "remote_stdevmes", DBL, { .d8 = &stats.remote_stdevmes, }, },
|
||||
{ "local_maxmes", DBL, { .d8 = &stats.local_maxmes, }, },
|
||||
{ "local_minmes", DBL, { .d8 = &stats.local_minmes, }, },
|
||||
{ "local_normdevmes", DBL, { .d8 = &stats.local_normdevmes, }, },
|
||||
{ "local_stdevmes", DBL, { .d8 = &stats.local_stdevmes, }, },
|
||||
{ NULL, },
|
||||
};
|
||||
|
||||
|
@ -1717,3 +1228,121 @@ int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, c
|
|||
|
||||
return ast_sip_push_task_wait_serializer(channel->session->serializer, refresh_write_cb, &rdata);
|
||||
}
|
||||
|
||||
struct hangup_data {
|
||||
struct ast_sip_session *session;
|
||||
int response_code;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Serializer task to hangup channel
|
||||
*/
|
||||
static int pjsip_hangup(void *obj)
|
||||
{
|
||||
struct hangup_data *hdata = obj;
|
||||
pjsip_tx_data *packet = NULL;
|
||||
|
||||
if ((hdata->session->inv_session->state != PJSIP_INV_STATE_DISCONNECTED) &&
|
||||
(pjsip_inv_answer(hdata->session->inv_session, hdata->response_code, NULL, NULL, &packet) == PJ_SUCCESS)) {
|
||||
ast_sip_session_send_response(hdata->session, packet);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Callback that validates the response code
|
||||
*/
|
||||
static int response_code_validator(const char *channel_name,
|
||||
const char *response) {
|
||||
int response_code;
|
||||
|
||||
int rc = ast_str_to_int(response, &response_code);
|
||||
if (rc != 0) {
|
||||
response_code = ast_sip_str2rc(response);
|
||||
if (response_code < 0) {
|
||||
ast_log(LOG_WARNING, "%s: Unrecognized response code parameter '%s'."
|
||||
" Defaulting to 603 DECLINE\n",
|
||||
channel_name, response);
|
||||
return PJSIP_SC_DECLINE;
|
||||
}
|
||||
}
|
||||
|
||||
if (response_code < 400 || response_code > 699) {
|
||||
ast_log(LOG_WARNING, "%s: Response code %d is out of range 400 -> 699."
|
||||
" Defaulting to 603 DECLINE\n",
|
||||
channel_name, response_code);
|
||||
return PJSIP_SC_DECLINE;
|
||||
}
|
||||
return response_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Called by pjsip_app_hangup and pjsip_action_hangup
|
||||
* to actually perform the hangup
|
||||
*/
|
||||
static void pjsip_app_hangup_handler(struct ast_channel *chan, int response_code)
|
||||
{
|
||||
struct ast_sip_channel_pvt *channel;
|
||||
struct hangup_data hdata = { NULL, -1 };
|
||||
const char *tag = ast_channel_name(chan);
|
||||
|
||||
hdata.response_code = response_code;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
if (strcmp(ast_channel_tech(chan)->type, "PJSIP")) {
|
||||
ast_log(LOG_WARNING, "%s: Not a PJSIP channel\n", tag);
|
||||
ast_channel_unlock(chan);
|
||||
return;
|
||||
}
|
||||
|
||||
channel = ast_channel_tech_pvt(chan);
|
||||
hdata.session = channel->session;
|
||||
|
||||
if (hdata.session->inv_session->role != PJSIP_ROLE_UAS || (
|
||||
hdata.session->inv_session->state != PJSIP_INV_STATE_INCOMING &&
|
||||
hdata.session->inv_session->state != PJSIP_INV_STATE_EARLY)) {
|
||||
ast_log(LOG_WARNING, "%s: Not an incoming channel or invalid state '%s'\n",
|
||||
tag, pjsip_inv_state_name(hdata.session->inv_session->state));
|
||||
ast_channel_unlock(chan);
|
||||
return;
|
||||
}
|
||||
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
if (ast_sip_push_task_wait_serializer(channel->session->serializer,
|
||||
pjsip_hangup, &hdata) != 0) {
|
||||
ast_log(LOG_WARNING, "%s: failed to push hangup task to serializer\n", tag);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief PJSIPHangup Dialplan App
|
||||
*/
|
||||
int pjsip_app_hangup(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
int response_code;
|
||||
const char *tag = ast_channel_name(chan);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "%s: Missing response code parameter\n", tag);
|
||||
return -1;
|
||||
}
|
||||
|
||||
response_code = response_code_validator(tag, data);
|
||||
|
||||
pjsip_app_hangup_handler(chan, response_code);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief PJSIPHangup Manager Action
|
||||
*/
|
||||
int pjsip_action_hangup(struct mansession *s, const struct message *m)
|
||||
{
|
||||
return ast_manager_hangup_helper(s, m,
|
||||
pjsip_app_hangup_handler, response_code_validator);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,659 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE docs SYSTEM "appdocsxml.dtd">
|
||||
<docs>
|
||||
<application name="PJSIPHangup" language="en_US">
|
||||
<synopsis>
|
||||
Hangup an incoming PJSIP channel with a SIP response code
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Cause" required="true">
|
||||
<para>May be one of...</para>
|
||||
<enumlist>
|
||||
<enum name="Response code"><para>A numeric response code in the range 400 ->699</para></enum>
|
||||
<enum name="Response code name"><para>A response code name from
|
||||
<literal>third-party/pjproject/source/pjsip/include/pjsip/sip_msg.h</literal>
|
||||
such as <literal>USE_IDENTITY_HEADER</literal> or
|
||||
<literal>PJSIP_SC_USE_IDENTITY_HEADER</literal></para></enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>
|
||||
Hangs up an incoming PJSIP channel and returns the
|
||||
specified SIP response code in the final response to the caller.
|
||||
</para>
|
||||
<para>
|
||||
</para>
|
||||
<warning><para>
|
||||
This function must be called BEFORE anything that
|
||||
might cause any other final (non 1XX) response to be sent.
|
||||
For example calling <literal>Answer()</literal> or
|
||||
<literal>Playback</literal> without the
|
||||
<literal>noanswer</literal> option will cause the call
|
||||
to be answered and a final 200 response to be sent.
|
||||
</para></warning>
|
||||
<para>
|
||||
</para>
|
||||
<para>As with the <literal>Hangup</literal> application,
|
||||
the dialplan will terminate after calling this function.</para>
|
||||
<para>
|
||||
</para>
|
||||
<para>The cause code set on the channel will be translated to
|
||||
a standard ISDN cause code using the table defined in
|
||||
ast_sip_hangup_sip2cause() in res_pjsip.c</para>
|
||||
<para>
|
||||
</para>
|
||||
<example title="Terminate call with 437 response code">
|
||||
same = n,PJSIPHangup(437)
|
||||
</example>
|
||||
<example title="Terminate call with 437 response code using the response code name">
|
||||
same = n,PJSIPHangup(UNSUPPORTED_CERTIFICATE)
|
||||
</example>
|
||||
<example title="Terminate call with 437 response code based on condition">
|
||||
same = n,ExecIf($[${SOMEVALUE} = ${SOME_BAD_VALUE}]?PJSIPHangup(437))
|
||||
</example>
|
||||
</description>
|
||||
</application>
|
||||
|
||||
<manager name="PJSIPHangup" language="en_US">
|
||||
<synopsis>
|
||||
Hangup an incoming PJSIP channel with a SIP response code
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Hangup']/syntax/parameter[@name='Channel'])" />
|
||||
<xi:include xpointer="xpointer(/docs/application[@name='PJSIPHangup']/syntax/parameter[@name='Cause'])" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>
|
||||
Hangs up an incoming PJSIP channel and returns the
|
||||
specified SIP response code in the final response to the caller.
|
||||
</para>
|
||||
<para>
|
||||
</para>
|
||||
<warning><para>
|
||||
This function must be called BEFORE anything that
|
||||
might cause any other final (non 1XX) response to be sent.
|
||||
For example calling <literal>Answer()</literal> or
|
||||
<literal>Playback</literal> without the
|
||||
<literal>noanswer</literal> option will cause the call
|
||||
to be answered and a final 200 response to be sent.
|
||||
</para></warning>
|
||||
<para>
|
||||
</para>
|
||||
<para>The cause code set on the channel will be translated to
|
||||
a standard ISDN cause code using the table defined in
|
||||
ast_sip_hangup_sip2cause() in res_pjsip.c</para>
|
||||
<para>
|
||||
</para>
|
||||
<example title="Terminate call with 437 response code">
|
||||
Action: PJSIPHangup
|
||||
ActionID: 12345678
|
||||
Channel: PJSIP/alice-00000002
|
||||
Cause: 437
|
||||
</example>
|
||||
<example title="Terminate call with 437 response code using the response code name">
|
||||
Action: PJSIPHangup
|
||||
ActionID: 12345678
|
||||
Channel: PJSIP/alice-00000002
|
||||
Cause: UNSUPPORTED_CERTIFICATE
|
||||
</example>
|
||||
</description>
|
||||
</manager>
|
||||
|
||||
<function name="PJSIP_DIAL_CONTACTS" language="en_US">
|
||||
<synopsis>
|
||||
Return a dial string for dialing all contacts on an AOR.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="endpoint" required="true">
|
||||
<para>Name of the endpoint</para>
|
||||
</parameter>
|
||||
<parameter name="aor" required="false">
|
||||
<para>Name of an AOR to use, if not specified the configured AORs on the endpoint are used</para>
|
||||
</parameter>
|
||||
<parameter name="request_user" required="false">
|
||||
<para>Optional request user to use in the request URI</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Returns a properly formatted dial string for dialing all contacts on an AOR.</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_MEDIA_OFFER" language="en_US">
|
||||
<synopsis>
|
||||
Media and codec offerings to be set on an outbound SIP channel prior to dialing.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="media" required="true">
|
||||
<para>types of media offered</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the codecs offered based upon the media choice.</para>
|
||||
<para>When written, sets the codecs to offer when an outbound dial attempt is made,
|
||||
or when a session refresh is sent using <replaceable>PJSIP_SEND_SESSION_REFRESH</replaceable>.
|
||||
</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">PJSIP_SEND_SESSION_REFRESH</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="PJSIP_DTMF_MODE" language="en_US">
|
||||
<since>
|
||||
<version>13.18.0</version>
|
||||
<version>14.7.0</version>
|
||||
<version>15.1.0</version>
|
||||
<version>16.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Get or change the DTMF mode for a SIP call.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the current DTMF mode</para>
|
||||
<para>When written, sets the current DTMF mode</para>
|
||||
<para>This function uses the same DTMF mode naming as the dtmf_mode configuration option</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_MOH_PASSTHROUGH" language="en_US">
|
||||
<synopsis>
|
||||
Get or change the on-hold behavior for a SIP call.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the current moh passthrough mode</para>
|
||||
<para>When written, sets the current moh passthrough mode</para>
|
||||
<para>If <replaceable>yes</replaceable>, on-hold re-INVITEs are sent. If <replaceable>no</replaceable>, music on hold is generated.</para>
|
||||
<para>This function can be used to override the moh_passthrough configuration option</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_SEND_SESSION_REFRESH" language="en_US">
|
||||
<since>
|
||||
<version>13.12.0</version>
|
||||
<version>14.1.0</version>
|
||||
<version>15.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="update_type" required="false">
|
||||
<para>The type of update to send. Default is <literal>invite</literal>.</para>
|
||||
<enumlist>
|
||||
<enum name="invite">
|
||||
<para>Send the session refresh as a re-INVITE.</para>
|
||||
</enum>
|
||||
<enum name="update">
|
||||
<para>Send the session refresh as an UPDATE.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This function will cause the PJSIP stack to immediately refresh
|
||||
the media session for the channel. This will be done using either a
|
||||
re-INVITE (default) or an UPDATE request.
|
||||
</para>
|
||||
<para>This is most useful when combined with the <replaceable>PJSIP_MEDIA_OFFER</replaceable>
|
||||
dialplan function, as it allows the formats in use on a channel to be
|
||||
re-negotiated after call setup.</para>
|
||||
<warning>
|
||||
<para>The formats the endpoint supports are <emphasis>not</emphasis>
|
||||
checked or enforced by this function. Using this function to offer
|
||||
formats not supported by the endpoint <emphasis>may</emphasis> result
|
||||
in a loss of media.</para>
|
||||
</warning>
|
||||
<example title="Re-negotiate format to g722">
|
||||
; Within some existing extension on an answered channel
|
||||
same => n,Set(PJSIP_MEDIA_OFFER(audio)=!all,g722)
|
||||
same => n,Set(PJSIP_SEND_SESSION_REFRESH()=invite)
|
||||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">PJSIP_MEDIA_OFFER</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="PJSIP_PARSE_URI" language="en_US">
|
||||
<since>
|
||||
<version>13.24.0</version>
|
||||
<version>16.1.0</version>
|
||||
<version>17.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Parse an uri and return a type part of the URI.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="uri" required="true">
|
||||
<para>URI to parse</para>
|
||||
</parameter>
|
||||
<parameter name="type" required="true">
|
||||
<para>The <literal>type</literal> parameter specifies which URI part to read</para>
|
||||
<enumlist>
|
||||
<enum name="display">
|
||||
<para>Display name.</para>
|
||||
</enum>
|
||||
<enum name="scheme">
|
||||
<para>URI scheme.</para>
|
||||
</enum>
|
||||
<enum name="user">
|
||||
<para>User part.</para>
|
||||
</enum>
|
||||
<enum name="passwd">
|
||||
<para>Password part.</para>
|
||||
</enum>
|
||||
<enum name="host">
|
||||
<para>Host part.</para>
|
||||
</enum>
|
||||
<enum name="port">
|
||||
<para>Port number, or zero.</para>
|
||||
</enum>
|
||||
<enum name="user_param">
|
||||
<para>User parameter.</para>
|
||||
</enum>
|
||||
<enum name="method_param">
|
||||
<para>Method parameter.</para>
|
||||
</enum>
|
||||
<enum name="transport_param">
|
||||
<para>Transport parameter.</para>
|
||||
</enum>
|
||||
<enum name="ttl_param">
|
||||
<para>TTL param, or -1.</para>
|
||||
</enum>
|
||||
<enum name="lr_param">
|
||||
<para>Loose routing param, or zero.</para>
|
||||
</enum>
|
||||
<enum name="maddr_param">
|
||||
<para>Maddr param.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Parse an URI and return a specified part of the URI.</para>
|
||||
</description>
|
||||
</function>
|
||||
|
||||
<info name="CHANNEL" language="en_US" tech="PJSIP">
|
||||
<enumlist>
|
||||
<enum name="rtp">
|
||||
<para>R/O Retrieve media related information.</para>
|
||||
<parameter name="type" required="true">
|
||||
<para>When <replaceable>rtp</replaceable> is specified, the
|
||||
<literal>type</literal> parameter must be provided. It specifies
|
||||
which RTP parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="src">
|
||||
<para>Retrieve the local address for RTP.</para>
|
||||
</enum>
|
||||
<enum name="dest">
|
||||
<para>Retrieve the remote address for RTP.</para>
|
||||
</enum>
|
||||
<enum name="direct">
|
||||
<para>If direct media is enabled, this address is the remote address
|
||||
used for RTP.</para>
|
||||
</enum>
|
||||
<enum name="secure">
|
||||
<para>Whether or not the media stream is encrypted.</para>
|
||||
<enumlist>
|
||||
<enum name="0">
|
||||
<para>The media stream is not encrypted.</para>
|
||||
</enum>
|
||||
<enum name="1">
|
||||
<para>The media stream is encrypted.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="hold">
|
||||
<para>Whether or not the media stream is currently restricted
|
||||
due to a call hold.</para>
|
||||
<enumlist>
|
||||
<enum name="0">
|
||||
<para>The media stream is not held.</para>
|
||||
</enum>
|
||||
<enum name="1">
|
||||
<para>The media stream is held.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="media_type" required="false">
|
||||
<para>When <replaceable>rtp</replaceable> is specified, the
|
||||
<literal>media_type</literal> parameter may be provided. It specifies
|
||||
which media stream the chosen RTP parameter should be retrieved
|
||||
from.</para>
|
||||
<enumlist>
|
||||
<enum name="audio">
|
||||
<para>Retrieve information from the audio media stream.</para>
|
||||
<note><para>If not specified, <literal>audio</literal> is used
|
||||
by default.</para></note>
|
||||
</enum>
|
||||
<enum name="video">
|
||||
<para>Retrieve information from the video media stream.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
<enum name="rtcp">
|
||||
<para>R/O Retrieve RTCP statistics.</para>
|
||||
<parameter name="statistic" required="true">
|
||||
<para>When <replaceable>rtcp</replaceable> is specified, the
|
||||
<literal>statistic</literal> parameter must be provided. It specifies
|
||||
which RTCP statistic parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="all">
|
||||
<para>Retrieve a summary of all RTCP statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="ssrc">
|
||||
<para>Our Synchronization Source identifier</para>
|
||||
</enum>
|
||||
<enum name="themssrc">
|
||||
<para>Their Synchronization Source identifier</para>
|
||||
</enum>
|
||||
<enum name="lp">
|
||||
<para>Our lost packet count</para>
|
||||
</enum>
|
||||
<enum name="rxjitter">
|
||||
<para>Received packet jitter</para>
|
||||
</enum>
|
||||
<enum name="rxcount">
|
||||
<para>Received packet count</para>
|
||||
</enum>
|
||||
<enum name="txjitter">
|
||||
<para>Transmitted packet jitter</para>
|
||||
</enum>
|
||||
<enum name="txcount">
|
||||
<para>Transmitted packet count</para>
|
||||
</enum>
|
||||
<enum name="rlp">
|
||||
<para>Remote lost packet count</para>
|
||||
</enum>
|
||||
<enum name="rtt">
|
||||
<para>Round trip time</para>
|
||||
</enum>
|
||||
<enum name="txmes">
|
||||
<para>Transmitted Media Experience Score</para>
|
||||
</enum>
|
||||
<enum name="rxmes">
|
||||
<para>Received Media Experience Score</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_jitter">
|
||||
<para>Retrieve a summary of all RTCP Jitter statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrxjitter">
|
||||
<para>Our minimum jitter</para>
|
||||
</enum>
|
||||
<enum name="maxrxjitter">
|
||||
<para>Our max jitter</para>
|
||||
</enum>
|
||||
<enum name="avgrxjitter">
|
||||
<para>Our average jitter</para>
|
||||
</enum>
|
||||
<enum name="stdevrxjitter">
|
||||
<para>Our jitter standard deviation</para>
|
||||
</enum>
|
||||
<enum name="reported_minjitter">
|
||||
<para>Their minimum jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_maxjitter">
|
||||
<para>Their max jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_avgjitter">
|
||||
<para>Their average jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_stdevjitter">
|
||||
<para>Their jitter standard deviation</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_loss">
|
||||
<para>Retrieve a summary of all RTCP packet loss statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrxlost">
|
||||
<para>Our minimum lost packets</para>
|
||||
</enum>
|
||||
<enum name="maxrxlost">
|
||||
<para>Our max lost packets</para>
|
||||
</enum>
|
||||
<enum name="avgrxlost">
|
||||
<para>Our average lost packets</para>
|
||||
</enum>
|
||||
<enum name="stdevrxlost">
|
||||
<para>Our lost packets standard deviation</para>
|
||||
</enum>
|
||||
<enum name="reported_minlost">
|
||||
<para>Their minimum lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_maxlost">
|
||||
<para>Their max lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_avglost">
|
||||
<para>Their average lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_stdevlost">
|
||||
<para>Their lost packets standard deviation</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_rtt">
|
||||
<para>Retrieve a summary of all RTCP round trip time information.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrtt">
|
||||
<para>Minimum round trip time</para>
|
||||
</enum>
|
||||
<enum name="maxrtt">
|
||||
<para>Maximum round trip time</para>
|
||||
</enum>
|
||||
<enum name="avgrtt">
|
||||
<para>Average round trip time</para>
|
||||
</enum>
|
||||
<enum name="stdevrtt">
|
||||
<para>Standard deviation round trip time</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_mes">
|
||||
<para>Retrieve a summary of all RTCP Media Experience Score information.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minmes">
|
||||
<para>Minimum MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="maxmes">
|
||||
<para>Maximum MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="avgmes">
|
||||
<para>Average MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="stdevmes">
|
||||
<para>Standard deviation MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="reported_minmes">
|
||||
<para>Minimum MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
<enum name="reported_maxmes">
|
||||
<para>Maximum MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
<enum name="reported_avgmes">
|
||||
<para>Average MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
<enum name="reported_stdevmes">
|
||||
<para>Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="txcount"><para>Transmitted packet count</para></enum>
|
||||
<enum name="rxcount"><para>Received packet count</para></enum>
|
||||
<enum name="txjitter"><para>Transmitted packet jitter</para></enum>
|
||||
<enum name="rxjitter"><para>Received packet jitter</para></enum>
|
||||
<enum name="remote_maxjitter"><para>Their max jitter</para></enum>
|
||||
<enum name="remote_minjitter"><para>Their minimum jitter</para></enum>
|
||||
<enum name="remote_normdevjitter"><para>Their average jitter</para></enum>
|
||||
<enum name="remote_stdevjitter"><para>Their jitter standard deviation</para></enum>
|
||||
<enum name="local_maxjitter"><para>Our max jitter</para></enum>
|
||||
<enum name="local_minjitter"><para>Our minimum jitter</para></enum>
|
||||
<enum name="local_normdevjitter"><para>Our average jitter</para></enum>
|
||||
<enum name="local_stdevjitter"><para>Our jitter standard deviation</para></enum>
|
||||
<enum name="txploss"><para>Transmitted packet loss</para></enum>
|
||||
<enum name="rxploss"><para>Received packet loss</para></enum>
|
||||
<enum name="remote_maxrxploss"><para>Their max lost packets</para></enum>
|
||||
<enum name="remote_minrxploss"><para>Their minimum lost packets</para></enum>
|
||||
<enum name="remote_normdevrxploss"><para>Their average lost packets</para></enum>
|
||||
<enum name="remote_stdevrxploss"><para>Their lost packets standard deviation</para></enum>
|
||||
<enum name="local_maxrxploss"><para>Our max lost packets</para></enum>
|
||||
<enum name="local_minrxploss"><para>Our minimum lost packets</para></enum>
|
||||
<enum name="local_normdevrxploss"><para>Our average lost packets</para></enum>
|
||||
<enum name="local_stdevrxploss"><para>Our lost packets standard deviation</para></enum>
|
||||
<enum name="rtt"><para>Round trip time</para></enum>
|
||||
<enum name="maxrtt"><para>Maximum round trip time</para></enum>
|
||||
<enum name="minrtt"><para>Minimum round trip time</para></enum>
|
||||
<enum name="normdevrtt"><para>Average round trip time</para></enum>
|
||||
<enum name="stdevrtt"><para>Standard deviation round trip time</para></enum>
|
||||
<enum name="local_ssrc"><para>Our Synchronization Source identifier</para></enum>
|
||||
<enum name="remote_ssrc"><para>Their Synchronization Source identifier</para></enum>
|
||||
<enum name="txmes"><para>
|
||||
Current MES based on us analyzing rtt, jitter and loss
|
||||
in the actual received RTP stream received from the remote end.
|
||||
I.E. This is the MES for the incoming audio stream.
|
||||
</para></enum>
|
||||
<enum name="rxmes"><para>
|
||||
Current MES based on rtt and the jitter and loss values in
|
||||
RTCP sender and receiver reports we receive from the
|
||||
remote end. I.E. This is the MES for the outgoing audio stream.
|
||||
</para></enum>
|
||||
<enum name="remote_maxmes"><para>Max MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="remote_minmes"><para>Min MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="remote_normdevmes"><para>Average MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="remote_stdevmes"><para>Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="local_maxmes"><para>Max MES based on us analyzing the received RTP stream</para></enum>
|
||||
<enum name="local_minmes"><para>Min MES based on us analyzing the received RTP stream</para></enum>
|
||||
<enum name="local_normdevmes"><para>Average MES based on us analyzing the received RTP stream</para></enum>
|
||||
<enum name="local_stdevmes"><para>Standard deviation MES based on us analyzing the received RTP stream</para></enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="media_type" required="false">
|
||||
<para>When <replaceable>rtcp</replaceable> is specified, the
|
||||
<literal>media_type</literal> parameter may be provided. It specifies
|
||||
which media stream the chosen RTCP parameter should be retrieved
|
||||
from.</para>
|
||||
<enumlist>
|
||||
<enum name="audio">
|
||||
<para>Retrieve information from the audio media stream.</para>
|
||||
<note><para>If not specified, <literal>audio</literal> is used
|
||||
by default.</para></note>
|
||||
</enum>
|
||||
<enum name="video">
|
||||
<para>Retrieve information from the video media stream.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
<enum name="endpoint">
|
||||
<para>R/O The name of the endpoint associated with this channel.
|
||||
Use the <replaceable>PJSIP_ENDPOINT</replaceable> function to obtain
|
||||
further endpoint related information.</para>
|
||||
</enum>
|
||||
<enum name="contact">
|
||||
<para>R/O The name of the contact associated with this channel.
|
||||
Use the <replaceable>PJSIP_CONTACT</replaceable> function to obtain
|
||||
further contact related information. Note this may not be present and if so
|
||||
is only available on outgoing legs.</para>
|
||||
</enum>
|
||||
<enum name="aor">
|
||||
<para>R/O The name of the AOR associated with this channel.
|
||||
Use the <replaceable>PJSIP_AOR</replaceable> function to obtain
|
||||
further AOR related information. Note this may not be present and if so
|
||||
is only available on outgoing legs.</para>
|
||||
</enum>
|
||||
<enum name="pjsip">
|
||||
<para>R/O Obtain information about the current PJSIP channel and its
|
||||
session.</para>
|
||||
<parameter name="type" required="true">
|
||||
<para>When <replaceable>pjsip</replaceable> is specified, the
|
||||
<literal>type</literal> parameter must be provided. It specifies
|
||||
which signalling parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="call-id">
|
||||
<para>The SIP call-id.</para>
|
||||
</enum>
|
||||
<enum name="secure">
|
||||
<para>Whether or not the signalling uses a secure transport.</para>
|
||||
<enumlist>
|
||||
<enum name="0"><para>The signalling uses a non-secure transport.</para></enum>
|
||||
<enum name="1"><para>The signalling uses a secure transport.</para></enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="target_uri">
|
||||
<para>The contact URI where requests are sent.</para>
|
||||
</enum>
|
||||
<enum name="local_uri">
|
||||
<para>The local URI.</para>
|
||||
</enum>
|
||||
<enum name="local_tag">
|
||||
<para>Tag in From header</para>
|
||||
</enum>
|
||||
<enum name="remote_uri">
|
||||
<para>The remote URI.</para>
|
||||
</enum>
|
||||
<enum name="remote_tag">
|
||||
<para>Tag in To header</para>
|
||||
</enum>
|
||||
<enum name="request_uri">
|
||||
<para>The request URI of the incoming <literal>INVITE</literal>
|
||||
associated with the creation of this channel.</para>
|
||||
</enum>
|
||||
<enum name="t38state">
|
||||
<para>The current state of any T.38 fax on this channel.</para>
|
||||
<enumlist>
|
||||
<enum name="DISABLED"><para>T.38 faxing is disabled on this channel.</para></enum>
|
||||
<enum name="LOCAL_REINVITE"><para>Asterisk has sent a <literal>re-INVITE</literal> to the remote end to initiate a T.38 fax.</para></enum>
|
||||
<enum name="REMOTE_REINVITE"><para>The remote end has sent a <literal>re-INVITE</literal> to Asterisk to initiate a T.38 fax.</para></enum>
|
||||
<enum name="ENABLED"><para>A T.38 fax session has been enabled.</para></enum>
|
||||
<enum name="REJECTED"><para>A T.38 fax session was attempted but was rejected.</para></enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="local_addr">
|
||||
<para>On inbound calls, the full IP address and port number that
|
||||
the <literal>INVITE</literal> request was received on. On outbound
|
||||
calls, the full IP address and port number that the <literal>INVITE</literal>
|
||||
request was transmitted from.</para>
|
||||
</enum>
|
||||
<enum name="remote_addr">
|
||||
<para>On inbound calls, the full IP address and port number that
|
||||
the <literal>INVITE</literal> request was received from. On outbound
|
||||
calls, the full IP address and port number that the <literal>INVITE</literal>
|
||||
request was transmitted to.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</info>
|
||||
<info name="CHANNEL_EXAMPLES" language="en_US" tech="PJSIP">
|
||||
<example title="PJSIP specific CHANNEL examples">
|
||||
; Log the current Call-ID
|
||||
same => n,Log(NOTICE, ${CHANNEL(pjsip,call-id)})
|
||||
|
||||
; Log the destination address of the audio stream
|
||||
same => n,Log(NOTICE, ${CHANNEL(rtp,dest)})
|
||||
|
||||
; Store the round-trip time associated with a
|
||||
; video stream in the CDR field video-rtt
|
||||
same => n,Set(CDR(video-rtt)=${CHANNEL(rtcp,rtt,video)})
|
||||
</example>
|
||||
</info>
|
||||
</docs>
|
|
@ -148,4 +148,24 @@ int pjsip_acf_dial_contacts_read(struct ast_channel *chan, const char *cmd, char
|
|||
*/
|
||||
int pjsip_acf_parse_uri_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
|
||||
|
||||
#endif /* _PJSIP_DIALPLAN_FUNCTIONS */
|
||||
/*!
|
||||
* \brief Hang up an incoming PJSIP channel with a SIP response code
|
||||
* \param chan The channel the function is called on
|
||||
* \param data SIP response code or name
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int pjsip_app_hangup(struct ast_channel *chan, const char *data);
|
||||
|
||||
/*!
|
||||
* \brief Manager action to hang up an incoming PJSIP channel with a SIP response code
|
||||
* \param s session
|
||||
* \param m message
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int pjsip_action_hangup(struct mansession *s, const struct message *m);
|
||||
|
||||
#endif /* _PJSIP_DIALPLAN_FUNCTIONS */
|
||||
|
|
|
@ -805,6 +805,11 @@ int analog_available(struct analog_pvt *p)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* If line is being held, definitely not (don't allow call waitings to an on-hook phone) */
|
||||
if (p->cshactive) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If no owner definitely available */
|
||||
if (!p->owner) {
|
||||
offhook = analog_is_off_hook(p);
|
||||
|
@ -1073,6 +1078,8 @@ int analog_call(struct analog_pvt *p, struct ast_channel *ast, const char *rdest
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/* Name and Number */
|
||||
n = ast_channel_connected(ast)->id.name.valid ? ast_channel_connected(ast)->id.name.str : NULL;
|
||||
l = ast_channel_connected(ast)->id.number.valid ? ast_channel_connected(ast)->id.number.str : NULL;
|
||||
if (l) {
|
||||
|
@ -1087,12 +1094,25 @@ int analog_call(struct analog_pvt *p, struct ast_channel *ast, const char *rdest
|
|||
}
|
||||
|
||||
if (p->use_callerid) {
|
||||
const char *qual_var;
|
||||
|
||||
/* Caller ID Name and Number */
|
||||
p->caller.id.name.str = p->lastcid_name;
|
||||
p->caller.id.number.str = p->lastcid_num;
|
||||
p->caller.id.name.valid = ast_channel_connected(ast)->id.name.valid;
|
||||
p->caller.id.number.valid = ast_channel_connected(ast)->id.number.valid;
|
||||
p->caller.id.name.presentation = ast_channel_connected(ast)->id.name.presentation;
|
||||
p->caller.id.number.presentation = ast_channel_connected(ast)->id.number.presentation;
|
||||
|
||||
/* Redirecting Reason */
|
||||
p->redirecting_reason = ast_channel_redirecting(ast)->from.number.valid ? ast_channel_redirecting(ast)->reason.code : -1;
|
||||
|
||||
/* Call Qualifier */
|
||||
ast_channel_lock(ast);
|
||||
/* XXX In the future, we may want to make this a CALLERID or CHANNEL property and fetch it from there. */
|
||||
qual_var = pbx_builtin_getvar_helper(ast, "CALL_QUALIFIER");
|
||||
p->call_qualifier = ast_true(qual_var) ? 1 : 0;
|
||||
ast_channel_unlock(ast);
|
||||
}
|
||||
|
||||
ast_setstate(ast, AST_STATE_RINGING);
|
||||
|
@ -1285,6 +1305,7 @@ int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
|
|||
p->channel, idx, p->subs[ANALOG_SUB_REAL].allocd, p->subs[ANALOG_SUB_CALLWAIT].allocd, p->subs[ANALOG_SUB_THREEWAY].allocd);
|
||||
if (idx > -1) {
|
||||
/* Real channel, do some fixup */
|
||||
p->cshactive = 0;
|
||||
p->subs[idx].owner = NULL;
|
||||
p->polarity = POLARITY_IDLE;
|
||||
analog_set_linear_mode(p, idx, 0);
|
||||
|
@ -1451,6 +1472,8 @@ int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
|
|||
ast_channel_setoption(ast,AST_OPTION_TDD,&x,sizeof(char),0);
|
||||
p->callwaitcas = 0;
|
||||
analog_set_callwaiting(p, p->permcallwaiting);
|
||||
/* In theory, the below is not necessary since we set hidecallerid = permhidecaller when calls start,
|
||||
* but this ensures the setting is defaulted properly when channels are idle, too. */
|
||||
p->hidecallerid = p->permhidecallerid;
|
||||
analog_set_dialing(p, 0);
|
||||
analog_update_conf(p);
|
||||
|
@ -1741,10 +1764,7 @@ static void *__analog_ss_thread(void *data)
|
|||
|
||||
ast_debug(1, "%s %d\n", __FUNCTION__, p->channel);
|
||||
|
||||
if (!chan) {
|
||||
/* What happened to the channel? */
|
||||
goto quit;
|
||||
}
|
||||
ast_assert(chan != NULL);
|
||||
|
||||
if ((callid = ast_channel_callid(chan))) {
|
||||
ast_callid_threadassoc_add(callid);
|
||||
|
@ -2133,12 +2153,26 @@ static void *__analog_ss_thread(void *data)
|
|||
case ANALOG_SIG_FXOLS:
|
||||
case ANALOG_SIG_FXOGS:
|
||||
case ANALOG_SIG_FXOKS:
|
||||
/* Set our default presentation.
|
||||
* This is necessary because the presentation for each call is independent
|
||||
* (though the default may be the same).
|
||||
* For example, if hidecallerid=yes and somebody makes a call with *82,
|
||||
* then makes a 3-way call, the presentation for the 2nd call should still
|
||||
* be blocked, unless that also had a *82.
|
||||
* For this reason, setting hidecallerid = permhidecallerid on hangup
|
||||
* is NOT sufficient, as the *82 from the first call could "leak" into
|
||||
* subsequent ones made before a hangup, improperly leaking a number
|
||||
* that should have been hidden.
|
||||
*/
|
||||
p->hidecallerid = p->permhidecallerid;
|
||||
|
||||
/* Read the first digit */
|
||||
timeout = analog_get_firstdigit_timeout(p);
|
||||
/* If starting a threeway call, never timeout on the first digit so someone
|
||||
can use flash-hook as a "hold" feature */
|
||||
if (p->subs[ANALOG_SUB_THREEWAY].owner) {
|
||||
timeout = 999999;
|
||||
* can use flash-hook as a "hold" feature...
|
||||
* ...Unless three-way dial tone should time out to silence, in which case the default suffices. */
|
||||
if (!p->threewaysilenthold && p->subs[ANALOG_SUB_THREEWAY].owner) {
|
||||
timeout = INT_MAX;
|
||||
}
|
||||
while (len < AST_MAX_EXTENSION-1) {
|
||||
int is_exten_parking = 0;
|
||||
|
@ -2190,18 +2224,18 @@ static void *__analog_ss_thread(void *data)
|
|||
res = analog_play_tone(p, idx, -1);
|
||||
ast_channel_lock(chan);
|
||||
ast_channel_exten_set(chan, exten);
|
||||
if (!ast_strlen_zero(p->cid_num)) {
|
||||
if (!p->hidecallerid) {
|
||||
ast_set_callerid(chan, p->cid_num, NULL, p->cid_num);
|
||||
} else {
|
||||
ast_set_callerid(chan, NULL, NULL, p->cid_num);
|
||||
}
|
||||
}
|
||||
if (!ast_strlen_zero(p->cid_name)) {
|
||||
if (!p->hidecallerid) {
|
||||
ast_set_callerid(chan, NULL, p->cid_name, NULL);
|
||||
}
|
||||
|
||||
/* Properly set the presentation.
|
||||
* We need to do this here as well, because p->hidecallerid might be set
|
||||
* due to permanent blocking, not star-67/star-82 usage. */
|
||||
if (p->hidecallerid) {
|
||||
ast_channel_caller(chan)->id.number.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
|
||||
ast_channel_caller(chan)->id.name.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
|
||||
} else {
|
||||
ast_channel_caller(chan)->id.number.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
|
||||
ast_channel_caller(chan)->id.name.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
|
||||
}
|
||||
|
||||
ast_setstate(chan, AST_STATE_RING);
|
||||
ast_channel_unlock(chan);
|
||||
analog_set_echocanceller(p, 1);
|
||||
|
@ -2219,7 +2253,11 @@ static void *__analog_ss_thread(void *data)
|
|||
}
|
||||
} else if (res == 0) {
|
||||
ast_debug(1, "not enough digits (and no ambiguous match)...\n");
|
||||
res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
|
||||
if (p->threewaysilenthold) {
|
||||
ast_debug(1, "Nothing dialed at three-way dial tone, timed out to silent hold\n");
|
||||
} else {
|
||||
res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
|
||||
}
|
||||
analog_wait_event(p);
|
||||
ast_hangup(chan);
|
||||
goto quit;
|
||||
|
@ -2263,9 +2301,11 @@ static void *__analog_ss_thread(void *data)
|
|||
ast_hangup(chan);
|
||||
goto quit;
|
||||
}
|
||||
|
||||
/* While the DMS-100 allows dialing as many *67s and *82s in succession as one's heart may desire,
|
||||
* the 5ESS does not, it only allows pure toggling (and only once!). So, it's not incorrect
|
||||
* to prevent people from dialing *67 if that won't actually do anything. */
|
||||
} else if (!p->hidecallerid && !strcmp(exten, "*67")) {
|
||||
ast_verb(3, "Disabling Caller*ID on %s\n", ast_channel_name(chan));
|
||||
ast_verb(3, "Blocking Caller*ID on %s\n", ast_channel_name(chan));
|
||||
/* Disable Caller*ID if enabled */
|
||||
p->hidecallerid = 1;
|
||||
ast_channel_caller(chan)->id.number.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
|
||||
|
@ -2352,7 +2392,7 @@ static void *__analog_ss_thread(void *data)
|
|||
len = 0;
|
||||
}
|
||||
} else if (p->hidecallerid && !strcmp(exten, "*82")) {
|
||||
ast_verb(3, "Enabling Caller*ID on %s\n", ast_channel_name(chan));
|
||||
ast_verb(3, "Allowing Caller*ID on %s\n", ast_channel_name(chan));
|
||||
/* Enable Caller*ID if enabled */
|
||||
p->hidecallerid = 0;
|
||||
ast_channel_caller(chan)->id.number.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
|
||||
|
@ -2713,6 +2753,7 @@ int analog_ss_thread_start(struct analog_pvt *p, struct ast_channel *chan)
|
|||
{
|
||||
pthread_t threadid;
|
||||
|
||||
p->ss_astchan = chan;
|
||||
return ast_pthread_create_detached(&threadid, NULL, __analog_ss_thread, p);
|
||||
}
|
||||
|
||||
|
@ -2775,9 +2816,13 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
|
|||
analog_set_pulsedial(p, (res & ANALOG_EVENT_PULSEDIGIT) ? 1 : 0);
|
||||
ast_debug(1, "Detected %sdigit '%c'\n", (res & ANALOG_EVENT_PULSEDIGIT) ? "pulse ": "", res & 0xff);
|
||||
analog_confmute(p, 0);
|
||||
p->subs[idx].f.frametype = AST_FRAME_DTMF_END;
|
||||
p->subs[idx].f.subclass.integer = res & 0xff;
|
||||
analog_handle_dtmf(p, ast, idx, &f);
|
||||
if (p->dialmode == ANALOG_DIALMODE_BOTH || p->dialmode == ANALOG_DIALMODE_PULSE) {
|
||||
p->subs[idx].f.frametype = AST_FRAME_DTMF_END;
|
||||
p->subs[idx].f.subclass.integer = res & 0xff;
|
||||
analog_handle_dtmf(p, ast, idx, &f);
|
||||
} else {
|
||||
ast_debug(1, "Dropping pulse digit '%c' because pulse dialing disabled on channel %d\n", res & 0xff, p->channel);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
|
@ -2892,6 +2937,34 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
|
|||
analog_get_and_handle_alarms(p);
|
||||
cause_code->ast_cause = AST_CAUSE_NETWORK_OUT_OF_ORDER;
|
||||
case ANALOG_EVENT_ONHOOK:
|
||||
if (p->calledsubscriberheld && (p->sig == ANALOG_SIG_FXOLS || p->sig == ANALOG_SIG_FXOGS || p->sig == ANALOG_SIG_FXOKS) && idx == ANALOG_SUB_REAL) {
|
||||
ast_debug(4, "Channel state on %s is %d\n", ast_channel_name(ast), ast_channel_state(ast));
|
||||
/* Called Subscriber Held: don't let the called party hang up on an incoming call immediately (if it's the only call). */
|
||||
if (p->subs[ANALOG_SUB_CALLWAIT].owner || p->subs[ANALOG_SUB_THREEWAY].owner) {
|
||||
ast_debug(2, "Letting this call hang up normally, since it's not the only call\n");
|
||||
} else if (!p->owner || !p->subs[ANALOG_SUB_REAL].owner || ast_channel_state(ast) != AST_STATE_UP) {
|
||||
ast_debug(2, "Called Subscriber Held does not apply: channel state is %d\n", ast_channel_state(ast));
|
||||
} else if (!p->owner || !p->subs[ANALOG_SUB_REAL].owner || strcmp(ast_channel_appl(p->subs[ANALOG_SUB_REAL].owner), "AppDial")) {
|
||||
/* Called Subscriber held only applies to incoming calls, not outgoing calls.
|
||||
* We can't use p->outgoing because that is always true, for both incoming and outgoing calls, so it's not accurate.
|
||||
* We can check the channel application/data instead.
|
||||
* For incoming calls to the channel, it will look like: AppDial / (Outgoing Line)
|
||||
* We only want this behavior for regular calls anyways (and not, say, Queue),
|
||||
* so this would actually work great. But accessing ast_channel_appl can cause a crash if there are no calls left,
|
||||
* so this check must occur AFTER we confirm the channel state *is* still UP.
|
||||
*/
|
||||
ast_debug(2, "Called Subscriber Held does not apply: not an incoming call\n");
|
||||
} else if (analog_is_off_hook(p)) {
|
||||
ast_log(LOG_WARNING, "Got ONHOOK but channel %d is off hook?\n", p->channel); /* Shouldn't happen */
|
||||
} else {
|
||||
ast_verb(3, "Holding incoming call %s for channel %d\n", ast_channel_name(ast), p->channel);
|
||||
/* Inhibit dahdi_hangup from getting called, and do nothing else now.
|
||||
* When the DAHDI channel goes off hook again, it'll just get reconnected with the incoming call,
|
||||
* to which, as far as its concerned, nothing has happened. */
|
||||
p->cshactive = 1; /* Keep track that this DAHDI channel is currently being held by an incoming call. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
ast_queue_control_data(ast, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
|
||||
ast_channel_hangupcause_hash_set(ast, cause_code, data_size);
|
||||
switch (p->sig) {
|
||||
|
@ -3731,6 +3804,32 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
|
|||
/* Handle an event on a given channel for the monitor thread. */
|
||||
switch (event) {
|
||||
case ANALOG_EVENT_WINKFLASH:
|
||||
case ANALOG_EVENT_RINGBEGIN:
|
||||
switch (i->sig) {
|
||||
case ANALOG_SIG_FXSLS:
|
||||
case ANALOG_SIG_FXSGS:
|
||||
case ANALOG_SIG_FXSKS:
|
||||
if (i->immediate) {
|
||||
if (i->use_callerid || i->usedistinctiveringdetection) {
|
||||
ast_log(LOG_WARNING, "Can't start PBX immediately, must wait for Caller ID / distinctive ring\n");
|
||||
} else {
|
||||
/* If we don't care about Caller ID or Distinctive Ring, then there's
|
||||
* no need to wait for anything before accepting the call, as
|
||||
* waiting will buy us nothing.
|
||||
* So if the channel is configured for immediate, actually start immediately
|
||||
* and get the show on the road as soon as possible. */
|
||||
ast_debug(1, "Disabling ring timeout (previously %d) to begin handling immediately\n", i->ringt_base);
|
||||
analog_set_ringtimeout(i, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
if (!(ISTRUNK(i) && i->immediate && !i->use_callerid && !i->usedistinctiveringdetection)) {
|
||||
break;
|
||||
}
|
||||
case ANALOG_EVENT_RINGOFFHOOK:
|
||||
if (i->inalarm) {
|
||||
break;
|
||||
|
@ -3742,6 +3841,7 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
|
|||
case ANALOG_SIG_FXOKS:
|
||||
res = analog_off_hook(i);
|
||||
i->fxsoffhookstate = 1;
|
||||
i->cshactive = 0;
|
||||
if (res && (errno == EBUSY)) {
|
||||
break;
|
||||
}
|
||||
|
@ -3753,7 +3853,10 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
|
|||
if (i->immediate) {
|
||||
analog_set_echocanceller(i, 1);
|
||||
/* The channel is immediately up. Start right away */
|
||||
res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE);
|
||||
if (i->immediatering) {
|
||||
/* Play fake ringing, if we've been told to... */
|
||||
res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE);
|
||||
}
|
||||
chan = analog_new_ast_channel(i, AST_STATE_RING, 1, ANALOG_SUB_REAL, NULL);
|
||||
if (!chan) {
|
||||
ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel);
|
||||
|
|
|
@ -116,6 +116,13 @@ enum analog_dsp_digitmode {
|
|||
ANALOG_DIGITMODE_MF,
|
||||
};
|
||||
|
||||
enum analog_dialmode {
|
||||
ANALOG_DIALMODE_BOTH = 0,
|
||||
ANALOG_DIALMODE_PULSE,
|
||||
ANALOG_DIALMODE_DTMF,
|
||||
ANALOG_DIALMODE_NONE,
|
||||
};
|
||||
|
||||
enum analog_cid_start {
|
||||
ANALOG_CID_START_POLARITY = 1,
|
||||
ANALOG_CID_START_POLARITY_IN,
|
||||
|
@ -282,16 +289,19 @@ struct analog_pvt {
|
|||
unsigned int ani_wink_time:16; /* Safe wait time before we wink to start ANI spill */
|
||||
|
||||
unsigned int answeronpolarityswitch:1;
|
||||
unsigned int calledsubscriberheld:1; /*!< TRUE if a single incoming call can hold an FXS channel */
|
||||
unsigned int callreturn:1;
|
||||
unsigned int cancallforward:1;
|
||||
unsigned int canpark:1;
|
||||
unsigned int dahditrcallerid:1; /*!< should we use the callerid from incoming call on dahdi transfer or not */
|
||||
unsigned int hanguponpolarityswitch:1;
|
||||
unsigned int immediate:1;
|
||||
unsigned int immediatering:1; /*!< TRUE if ringing should be provided for immediate execution */
|
||||
unsigned int permcallwaiting:1; /*!< TRUE if call waiting is enabled. (Configured option) */
|
||||
unsigned int permhidecallerid:1; /*!< Whether to hide our outgoing caller ID or not */
|
||||
unsigned int pulse:1;
|
||||
unsigned int threewaycalling:1;
|
||||
unsigned int threewaysilenthold:1; /*!< Whether to time out a three-way dial tone to silence */
|
||||
unsigned int transfer:1;
|
||||
unsigned int transfertobusy:1; /*!< allow flash-transfers to busy channels */
|
||||
unsigned int use_callerid:1; /*!< Whether or not to use caller id on this channel */
|
||||
|
@ -308,6 +318,7 @@ struct analog_pvt {
|
|||
int channel; /*!< Channel Number */
|
||||
|
||||
enum analog_sigtype outsigmod;
|
||||
enum analog_dialmode dialmode; /*!< Which of pulse and/or tone dialing to support */
|
||||
int echotraining;
|
||||
int cid_signalling; /*!< Asterisk callerid type we're using */
|
||||
int polarityonanswerdelay;
|
||||
|
@ -320,6 +331,7 @@ struct analog_pvt {
|
|||
|
||||
/* XXX: All variables after this are internal */
|
||||
unsigned int callwaiting:1; /*!< TRUE if call waiting is enabled. (Active option) */
|
||||
unsigned int cshactive:1; /*!< TRUE if FXS channel is currently held by an incoming call */
|
||||
unsigned int dialednone:1;
|
||||
unsigned int dialing:1; /*!< TRUE if in the process of dialing digits or sending something */
|
||||
unsigned int dnd:1; /*!< TRUE if Do-Not-Disturb is enabled. */
|
||||
|
@ -334,12 +346,15 @@ struct analog_pvt {
|
|||
* gives a positive reply.
|
||||
*/
|
||||
unsigned int callwaitcas:1;
|
||||
unsigned int call_qualifier:1; /*!< Call qualifier delivery */
|
||||
|
||||
char callwait_num[AST_MAX_EXTENSION];
|
||||
char callwait_name[AST_MAX_EXTENSION];
|
||||
char lastcid_num[AST_MAX_EXTENSION];
|
||||
char lastcid_name[AST_MAX_EXTENSION];
|
||||
struct ast_party_caller caller;
|
||||
int redirecting_reason; /*!< Redirecting reason */
|
||||
|
||||
int cidrings; /*!< Which ring to deliver CID on */
|
||||
char echorest[20];
|
||||
int polarity;
|
||||
|
|
|
@ -597,20 +597,20 @@ static int parse_config(int reload)
|
|||
if (!strcasecmp(var->name, "quality")) {
|
||||
res = abs(atoi(var->value));
|
||||
if (res > -1 && res < 11) {
|
||||
ast_verb(3, "CODEC SPEEX: Setting Quality to %d\n",res);
|
||||
ast_verb(5, "CODEC SPEEX: Setting Quality to %d\n",res);
|
||||
quality = res;
|
||||
} else
|
||||
ast_log(LOG_ERROR,"Error Quality must be 0-10\n");
|
||||
} else if (!strcasecmp(var->name, "complexity")) {
|
||||
res = abs(atoi(var->value));
|
||||
if (res > -1 && res < 11) {
|
||||
ast_verb(3, "CODEC SPEEX: Setting Complexity to %d\n",res);
|
||||
ast_verb(5, "CODEC SPEEX: Setting Complexity to %d\n",res);
|
||||
complexity = res;
|
||||
} else
|
||||
ast_log(LOG_ERROR,"Error! Complexity must be 0-10\n");
|
||||
} else if (!strcasecmp(var->name, "vbr_quality")) {
|
||||
if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0 && res_f <= 10) {
|
||||
ast_verb(3, "CODEC SPEEX: Setting VBR Quality to %f\n",res_f);
|
||||
ast_verb(5, "CODEC SPEEX: Setting VBR Quality to %f\n",res_f);
|
||||
vbr_quality = res_f;
|
||||
} else
|
||||
ast_log(LOG_ERROR,"Error! VBR Quality must be 0-10\n");
|
||||
|
@ -618,62 +618,62 @@ static int parse_config(int reload)
|
|||
ast_log(LOG_ERROR,"Error! ABR Quality setting obsolete, set ABR to desired bitrate\n");
|
||||
} else if (!strcasecmp(var->name, "enhancement")) {
|
||||
enhancement = ast_true(var->value) ? 1 : 0;
|
||||
ast_verb(3, "CODEC SPEEX: Perceptual Enhancement Mode. [%s]\n",enhancement ? "on" : "off");
|
||||
ast_verb(5, "CODEC SPEEX: Perceptual Enhancement Mode. [%s]\n",enhancement ? "on" : "off");
|
||||
} else if (!strcasecmp(var->name, "vbr")) {
|
||||
vbr = ast_true(var->value) ? 1 : 0;
|
||||
ast_verb(3, "CODEC SPEEX: VBR Mode. [%s]\n",vbr ? "on" : "off");
|
||||
ast_verb(5, "CODEC SPEEX: VBR Mode. [%s]\n",vbr ? "on" : "off");
|
||||
} else if (!strcasecmp(var->name, "abr")) {
|
||||
res = abs(atoi(var->value));
|
||||
if (res >= 0) {
|
||||
if (res > 0)
|
||||
ast_verb(3, "CODEC SPEEX: Setting ABR target bitrate to %d\n",res);
|
||||
ast_verb(5, "CODEC SPEEX: Setting ABR target bitrate to %d\n",res);
|
||||
else
|
||||
ast_verb(3, "CODEC SPEEX: Disabling ABR\n");
|
||||
ast_verb(5, "CODEC SPEEX: Disabling ABR\n");
|
||||
abr = res;
|
||||
} else
|
||||
ast_log(LOG_ERROR,"Error! ABR target bitrate must be >= 0\n");
|
||||
} else if (!strcasecmp(var->name, "vad")) {
|
||||
vad = ast_true(var->value) ? 1 : 0;
|
||||
ast_verb(3, "CODEC SPEEX: VAD Mode. [%s]\n",vad ? "on" : "off");
|
||||
ast_verb(5, "CODEC SPEEX: VAD Mode. [%s]\n",vad ? "on" : "off");
|
||||
} else if (!strcasecmp(var->name, "dtx")) {
|
||||
dtx = ast_true(var->value) ? 1 : 0;
|
||||
ast_verb(3, "CODEC SPEEX: DTX Mode. [%s]\n",dtx ? "on" : "off");
|
||||
ast_verb(5, "CODEC SPEEX: DTX Mode. [%s]\n",dtx ? "on" : "off");
|
||||
} else if (!strcasecmp(var->name, "preprocess")) {
|
||||
preproc = ast_true(var->value) ? 1 : 0;
|
||||
ast_verb(3, "CODEC SPEEX: Preprocessing. [%s]\n",preproc ? "on" : "off");
|
||||
ast_verb(5, "CODEC SPEEX: Preprocessing. [%s]\n",preproc ? "on" : "off");
|
||||
} else if (!strcasecmp(var->name, "pp_vad")) {
|
||||
pp_vad = ast_true(var->value) ? 1 : 0;
|
||||
ast_verb(3, "CODEC SPEEX: Preprocessor VAD. [%s]\n",pp_vad ? "on" : "off");
|
||||
ast_verb(5, "CODEC SPEEX: Preprocessor VAD. [%s]\n",pp_vad ? "on" : "off");
|
||||
} else if (!strcasecmp(var->name, "pp_agc")) {
|
||||
pp_agc = ast_true(var->value) ? 1 : 0;
|
||||
ast_verb(3, "CODEC SPEEX: Preprocessor AGC. [%s]\n",pp_agc ? "on" : "off");
|
||||
ast_verb(5, "CODEC SPEEX: Preprocessor AGC. [%s]\n",pp_agc ? "on" : "off");
|
||||
} else if (!strcasecmp(var->name, "pp_agc_level")) {
|
||||
if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0) {
|
||||
ast_verb(3, "CODEC SPEEX: Setting preprocessor AGC Level to %f\n",res_f);
|
||||
ast_verb(5, "CODEC SPEEX: Setting preprocessor AGC Level to %f\n",res_f);
|
||||
pp_agc_level = res_f;
|
||||
} else
|
||||
ast_log(LOG_ERROR,"Error! Preprocessor AGC Level must be >= 0\n");
|
||||
} else if (!strcasecmp(var->name, "pp_denoise")) {
|
||||
pp_denoise = ast_true(var->value) ? 1 : 0;
|
||||
ast_verb(3, "CODEC SPEEX: Preprocessor Denoise. [%s]\n",pp_denoise ? "on" : "off");
|
||||
ast_verb(5, "CODEC SPEEX: Preprocessor Denoise. [%s]\n",pp_denoise ? "on" : "off");
|
||||
} else if (!strcasecmp(var->name, "pp_dereverb")) {
|
||||
pp_dereverb = ast_true(var->value) ? 1 : 0;
|
||||
ast_verb(3, "CODEC SPEEX: Preprocessor Dereverb. [%s]\n",pp_dereverb ? "on" : "off");
|
||||
ast_verb(5, "CODEC SPEEX: Preprocessor Dereverb. [%s]\n",pp_dereverb ? "on" : "off");
|
||||
} else if (!strcasecmp(var->name, "pp_dereverb_decay")) {
|
||||
if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0) {
|
||||
ast_verb(3, "CODEC SPEEX: Setting preprocessor Dereverb Decay to %f\n",res_f);
|
||||
ast_verb(5, "CODEC SPEEX: Setting preprocessor Dereverb Decay to %f\n",res_f);
|
||||
pp_dereverb_decay = res_f;
|
||||
} else
|
||||
ast_log(LOG_ERROR,"Error! Preprocessor Dereverb Decay must be >= 0\n");
|
||||
} else if (!strcasecmp(var->name, "pp_dereverb_level")) {
|
||||
if (sscanf(var->value, "%30f", &res_f) == 1 && res_f >= 0) {
|
||||
ast_verb(3, "CODEC SPEEX: Setting preprocessor Dereverb Level to %f\n",res_f);
|
||||
ast_verb(5, "CODEC SPEEX: Setting preprocessor Dereverb Level to %f\n",res_f);
|
||||
pp_dereverb_level = res_f;
|
||||
} else
|
||||
ast_log(LOG_ERROR,"Error! Preprocessor Dereverb Level must be >= 0\n");
|
||||
} else if (!strcasecmp(var->name, "experimental_rtcp_feedback")) {
|
||||
exp_rtcp_fb = ast_true(var->value) ? 1 : 0;
|
||||
ast_verb(3, "CODEC SPEEX: Experimental RTCP Feedback. [%s]\n",exp_rtcp_fb ? "on" : "off");
|
||||
ast_verb(5, "CODEC SPEEX: Experimental RTCP Feedback. [%s]\n",exp_rtcp_fb ? "on" : "off");
|
||||
}
|
||||
}
|
||||
ast_config_destroy(cfg);
|
||||
|
|
|
@ -8,8 +8,8 @@ If you intend to use this configuration as a template for your own, then
|
|||
you will need to change many values in the various configuration files to
|
||||
match your own devices, network, SIP ITSP accounts and more.
|
||||
|
||||
For further documentation on this configuration see the Asterisk wiki:
|
||||
https://wiki.asterisk.org/wiki/display/AST/Reference+Use+Cases+for+Asterisk.
|
||||
For further documentation on this configuration see the Asterisk documentation:
|
||||
https://docs.asterisk.org/Deployment/Reference-Use-Cases-for-Asterisk/.
|
||||
|
||||
Please report bugs or errors in configuration on the Asterisk issue tracker:
|
||||
https://wiki.asterisk.org/wiki/display/AST/Asterisk+Issue+Guidelines
|
||||
https://docs.asterisk.org/Asterisk-Community/Asterisk-Issue-Guidelines/
|
||||
|
|
|
@ -8,6 +8,11 @@ total_analysis_time = 5000 ; Maximum time allowed for the algorithm to decide
|
|||
silence_threshold = 256 ; If the average level of noise in a sample does not reach
|
||||
; this value, from a scale of 0 to 32767, then we will consider
|
||||
; it to be silence.
|
||||
;playback_file = ; Audio file to play while AMD is running, so the caller
|
||||
; does not just hear silence. Note that specifying this here
|
||||
; will apply to ALL AMD runs, so you may wish to set it
|
||||
; in the dialplan as an argument to AMD() instead.
|
||||
; Default is no audio file (not to play anything).
|
||||
|
||||
; Greeting ;
|
||||
initial_silence = 2500 ; Maximum silence duration before the greeting.
|
||||
|
@ -19,7 +24,7 @@ greeting = 1500 ; Maximum length of a greeting. If exceeded, then the
|
|||
|
||||
; Word detection ;
|
||||
min_word_length = 100 ; Minimum duration of Voice to considered as a word
|
||||
maximum_word_length = 5000 ; Maximum duration of a single Voice utterance allowed.
|
||||
maximum_word_length = 5000 ; Maximum duration of a single Voice utterance allowed.
|
||||
between_words_silence = 50 ; Minimum duration of silence after a word to consider
|
||||
; the audio what follows as a new word
|
||||
|
||||
|
|
|
@ -95,10 +95,13 @@ documentation_language = en_US ; Set the language you want documentation
|
|||
; documented in extensions.conf.sample.
|
||||
; Default gosub.
|
||||
;live_dangerously = no ; Enable the execution of 'dangerous' dialplan
|
||||
; functions from external sources (AMI,
|
||||
; etc.) These functions (such as SHELL) are
|
||||
; considered dangerous because they can allow
|
||||
; privilege escalation.
|
||||
; functions and configuration file access from
|
||||
; external sources (AMI, etc.) These functions
|
||||
; (such as SHELL) are considered dangerous
|
||||
; because they can allow privilege escalation.
|
||||
; Configuration files are considered dangerous
|
||||
; if they exist outside of the Asterisk
|
||||
; configuration directory.
|
||||
; Default no
|
||||
;entityid=00:11:22:33:44:55 ; Entity ID.
|
||||
; This is in the form of a MAC address.
|
||||
|
@ -125,6 +128,10 @@ documentation_language = en_US ; Set the language you want documentation
|
|||
; housekeeping AMI and ARI channel events. This can
|
||||
; reduce the load on the manager and ARI applications
|
||||
; when the Digium Phone Module for Asterisk is in use.
|
||||
;sounds_search_custom_dir = no; This option, if enabled, will
|
||||
; cause Asterisk to search for sounds files in
|
||||
; AST_DATA_DIR/sounds/custom before searching the
|
||||
; normal directories like AST_DATA_DIR/sounds/<lang>.
|
||||
|
||||
; Changing the following lines may compromise your security.
|
||||
;[files]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
; --- Call Completion Supplementary Services ---
|
||||
;
|
||||
; For more information about CCSS, see the CCSS user documentation
|
||||
; https://wiki.asterisk.org/wiki/display/AST/Call+Completion+Supplementary+Services+(CCSS)
|
||||
; https://docs.asterisk.org/Deployment/PSTN-Connectivity/Call-Completion-Supplementary-Services-CCSS/
|
||||
;
|
||||
|
||||
[general]
|
||||
|
|
|
@ -32,6 +32,17 @@
|
|||
; is "no".
|
||||
;congestion = no
|
||||
|
||||
; Define whether or not to ignore bridging changes in CDRs. This prevents
|
||||
; bridging changes from resulting in multiple CDRs for different parts of
|
||||
; a call. Default is "no". This setting cannot be changed on a reload.
|
||||
;ignorestatechanges = no
|
||||
|
||||
; Define whether or not to ignore dial updates in CDRs. This prevents
|
||||
; dial updates from resulting in multiple CDRs for different parts of
|
||||
; a call. The last disposition on the channel will be used for the CDR.
|
||||
; Use with caution. Default is "no".
|
||||
;ignoredialchanges = no
|
||||
|
||||
; Normally, CDR's are not closed out until after all extensions are finished
|
||||
; executing. By enabling this option, the CDR will be ended before executing
|
||||
; the "h" extension and hangup handlers so that CDR values such as "end" and
|
||||
|
@ -102,8 +113,6 @@
|
|||
; Here are all the possible back ends:
|
||||
;
|
||||
; csv, custom, manager, odbc, pgsql, radius, sqlite, tds
|
||||
; (also, mysql is available via the asterisk-addons, due to licensing
|
||||
; requirements)
|
||||
; (please note, also, that other backends can be created, by creating
|
||||
; a new backend module in the source cdr/ directory!)
|
||||
;
|
||||
|
|
|
@ -572,6 +572,15 @@ context=public
|
|||
;
|
||||
usecallerid=yes
|
||||
;
|
||||
; NOTE: If the CALL_QUALIFIER variable on the channel is set to 1,
|
||||
; the Stentor Call Qualifier parameter will be sent for Caller ID spills
|
||||
; using the Multiple Data Message Format (MDMF).
|
||||
; This is used by capable Caller ID units to activate the
|
||||
; "LDC" (Long Distance Call) indicator.
|
||||
; This variable is not automatically set anywhere. You are responsible
|
||||
; for setting it in the dialplan if you want to activate the indicator,
|
||||
; and you must have compatible CPE.
|
||||
;
|
||||
; Type of caller ID signalling in use
|
||||
; bell = bell202 as used in US (default)
|
||||
; v23 = v23 as used in the UK
|
||||
|
@ -586,7 +595,7 @@ usecallerid=yes
|
|||
; polarity = polarity reversal signals the start
|
||||
; polarity_IN = polarity reversal signals the start, for India,
|
||||
; for dtmf dialtone detection; using DTMF.
|
||||
; (see https://wiki.asterisk.org/wiki/display/AST/Caller+ID+in+India)
|
||||
; (see https://wiki.asterisk.org/wiki/display/AST/Caller+ID+in+India)
|
||||
; dtmf = causes monitor loop to look for dtmf energy on the
|
||||
; incoming channel to initate cid acquisition
|
||||
;
|
||||
|
@ -746,10 +755,30 @@ usecallingpres=yes
|
|||
;
|
||||
callwaitingcallerid=yes
|
||||
;
|
||||
; Whether or not to allow users to go on-hook when receiving an incoming call
|
||||
; without disconnecting it. Users can later resume the call from any phone
|
||||
; on the same physical phone line (the same DAHDI channel).
|
||||
; This setting only has an effect on FXS (FXO-signalled) channels where there
|
||||
; is only a single incoming call to the DAHDI channel, using the Dial application.
|
||||
; (This is a convenience mechanism to avoid users wishing to resume a conversation
|
||||
; at a different phone from leaving a phone off the hook, resuming elsewhere,
|
||||
; and forgetting to restore the original phone on hook afterwards.)
|
||||
; Default is no.
|
||||
;
|
||||
;calledsubscriberheld=yes
|
||||
;
|
||||
; Support three-way calling
|
||||
;
|
||||
threewaycalling=yes
|
||||
;
|
||||
; By default, the three-way dial tone never times out, allowing it to be
|
||||
; used as a primitive "hold" mechanism. However, if you'd prefer
|
||||
; to have the dial tone time out to silence, you can use this option
|
||||
; to time out after the normal first digit timeout to silence.
|
||||
; Default is 'no'.
|
||||
;
|
||||
;threewaysilenthold=no
|
||||
;
|
||||
; For FXS ports (either direct analog or over T1/E1):
|
||||
; Support flash-hook call transfer (requires three way calling)
|
||||
; Also enables call parking (overrides the 'canpark' parameter)
|
||||
|
@ -915,6 +944,10 @@ group=1
|
|||
; you can answer it by picking up and dialing *8#. For simple offices, just
|
||||
; make these both the same. Groups range from 0 to 63.
|
||||
;
|
||||
; Call groups and pickup groups may only be specified for FXO signalled channels.
|
||||
; If you need to pick up an FXS signalled channel directly, you can have it
|
||||
; dial a Local channel and pick up the ;1 side of the Local channel instead.
|
||||
;
|
||||
callgroup=1
|
||||
pickupgroup=1
|
||||
;
|
||||
|
@ -936,13 +969,25 @@ pickupgroup=1
|
|||
; target of the transfer.
|
||||
|
||||
;
|
||||
; Specify whether the channel should be answered immediately or if the simple
|
||||
; switch should provide dialtone, read digits, etc.
|
||||
; On FXS channels (FXO signaled), specifies whether the channel should enter the dialplan
|
||||
; immediately or if the simple switch should provide dialtone, read digits, etc.
|
||||
; On FXO channels (FXS signaled), specifies whether the call should enter the dialplan
|
||||
; immediately or if we should wait for at least one ring. This is required if
|
||||
; Caller ID or distinctive ringing is enabled. If you do not need either, you can
|
||||
; skip waiting for the first ring to begin call processing sooner.
|
||||
;
|
||||
; Note: If immediate=yes the dialplan execution will always start at extension
|
||||
; 's' priority 1 regardless of the dialed number!
|
||||
;
|
||||
;immediate=yes
|
||||
;
|
||||
; On FXS channels (FXO signaled), specifies whether fake audible ringback should
|
||||
; be provided as soon as the channel goes off hook and immediate=yes.
|
||||
; If audio should come only from the dialplan, this option should be disabled.
|
||||
; Default is 'yes'
|
||||
;
|
||||
;immediatering=no
|
||||
;
|
||||
; Specify whether flash-hook transfers to 'busy' channels should complete or
|
||||
; return to the caller performing the transfer (default is yes).
|
||||
;
|
||||
|
@ -1068,6 +1113,15 @@ pickupgroup=1
|
|||
; polarity switch and hangup polarity switch.
|
||||
; (default: 600ms)
|
||||
;
|
||||
; For kewlstart FXS (FXO signalled) ports only:
|
||||
; When all calls towards a DAHDI channel have cleared, automatically
|
||||
; reoriginate and provide dial tone to the user again, so s/he can
|
||||
; make another call without having to cycle the hookswitch manually.
|
||||
; This only works for kewlstart (fxo_ks) lines!
|
||||
; Dial tone will be provided only after the loop disconnect has finished.
|
||||
;
|
||||
;autoreoriginate=yes
|
||||
;
|
||||
; On trunk interfaces (FXS) it can be useful to attempt to follow the progress
|
||||
; of a call through RINGING, BUSY, and ANSWERING. If turned on, call
|
||||
; progress attempts to determine answer, busy, and ringing on phone lines.
|
||||
|
@ -1126,10 +1180,19 @@ pickupgroup=1
|
|||
;
|
||||
; For FXO (FXS signalled) devices, whether to use pulse dial instead of DTMF
|
||||
; Pulse digits from phones (FXS devices, FXO signalling) are always
|
||||
; detected.
|
||||
; detected, unless the dialmode setting has been changed from the default.
|
||||
;
|
||||
;pulsedial=yes
|
||||
;
|
||||
; For FXS (FXO signalled) devices, the dialing modes to support for the channel.
|
||||
; By default, both pulse and tone (DTMF) dialing are always detected.
|
||||
; May be set to "pulse" if you only want to allow pulse dialing on a line.
|
||||
; May be set to "dtmf" or "tone" to only allow tone dialing on a line.
|
||||
; May be set to "none" to prevent dialing entirely.
|
||||
; You can also change this during a call using the CHANNEL function in the dialplan.
|
||||
;
|
||||
;dialmode=both
|
||||
;
|
||||
; For fax detection, uncomment one of the following lines. The default is *OFF*
|
||||
;
|
||||
;faxdetect=both
|
||||
|
@ -1516,7 +1579,7 @@ pickupgroup=1
|
|||
;#include ss7.timers
|
||||
|
||||
; For more information on setting up SS7, see the README file in libss7 or
|
||||
; https://wiki.asterisk.org/wiki/display/AST/Signaling+System+Number+7
|
||||
; https://docs.asterisk.org/Deployment/PSTN-Connectivity/Signaling-System-Number-7/
|
||||
; ----------------- SS7 Options ----------------------------------------
|
||||
|
||||
; ---------------- Options for use with signalling=mfcr2 --------------
|
||||
|
|
|
@ -58,8 +58,11 @@ type=user
|
|||
; when a channel enters a empty conference. On by default.
|
||||
;wait_marked=yes ; Sets if the user must wait for a marked user to enter before
|
||||
; joining the conference. Off by default.
|
||||
;end_marked=yes ; This option will kick every user with this option set in their
|
||||
; user profile after the last Marked user exists the conference.
|
||||
;end_marked=yes ; This option will kick every non-marked user with this option set in their
|
||||
; user profile after the last marked user exits the conference.
|
||||
;end_marked_any=no ; This option will kick every user with this option set in
|
||||
; their user profile after any marked user exits the conference.
|
||||
; Additionally, note that unlike end_marked, this includes marked users.
|
||||
|
||||
;dsp_drop_silence=yes ; This option drops what Asterisk detects as silence from
|
||||
; entering into the bridge. Enabling this option will drastically
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
;bindaddr=0.0.0.0
|
||||
;port=4520
|
||||
;
|
||||
; See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for a description of the tos parameter.
|
||||
; See https://docs.asterisk.org/Configuration/Channel-Drivers/IP-Quality-of-Service for a description of the tos parameter.
|
||||
;tos=ef
|
||||
;
|
||||
; Our entity identifier. (It should generally be the MAC address of the
|
||||
|
@ -75,6 +75,18 @@ autokill=yes
|
|||
; off by default due to performance impacts.
|
||||
;
|
||||
;storehistory=yes
|
||||
;
|
||||
; Channel technology to use for outgoing calls using SIP (Session Initiation Protocol).
|
||||
; Options are 'SIP' for chan_sip and 'PJSIP' for chan_pjsip. Default is 'PJSIP'.
|
||||
; If specified, all outgoing SIP calls using DUNDi will use the specified channel tech.
|
||||
;
|
||||
;outgoing_sip_tech=pjsip
|
||||
;
|
||||
; Name of endpoint from pjsip.conf to use for outgoing calls from this system,
|
||||
; when using the PJSIP technology to complete a call to a SIP-based destination.
|
||||
; (Required for PJSIP, since PJSIP calls must specify an endpoint explicitly).
|
||||
;
|
||||
;pjsip_outgoing_endpoint=outgoing
|
||||
|
||||
[mappings]
|
||||
;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
; Static and realtime external configuration
|
||||
; engine configuration
|
||||
;
|
||||
; See https://wiki.asterisk.org/wiki/display/AST/Realtime+Database+Configuration
|
||||
; See https://docs.asterisk.org/Fundamentals/Asterisk-Configuration/Database-Support-Configuration/Realtime-Database-Configuration/
|
||||
; for basic table formatting information.
|
||||
;
|
||||
[settings]
|
||||
|
@ -86,6 +86,7 @@
|
|||
;ps_outbound_publishes => odbc,asterisk
|
||||
;ps_inbound_publications = odbc,asterisk
|
||||
;ps_asterisk_publications = odbc,asterisk
|
||||
;stir_tn => odbc,asterisk
|
||||
;voicemail => odbc,asterisk
|
||||
;extensions => odbc,asterisk
|
||||
;meetme => mysql,general
|
||||
|
|
|
@ -30,8 +30,6 @@ globals {
|
|||
CONSOLE-AEL="Console/dsp"; // Console interface for demo
|
||||
//CONSOLE-AEL=Zap/1;
|
||||
//CONSOLE-AEL=Phone/phone0;
|
||||
IAXINFO-AEL=guest; // IAXtel username/password
|
||||
//IAXINFO-AEL="myuser:mypass";
|
||||
OUTBOUND-TRUNK="Zap/g2"; // Trunk interface
|
||||
//
|
||||
// Note the 'g2' in the OUTBOUND-TRUNK variable above. It specifies which group (defined
|
||||
|
@ -187,16 +185,6 @@ macro ael-dundi-e164(exten) {
|
|||
return;
|
||||
};
|
||||
|
||||
//
|
||||
// Here are the entries you need to participate in the IAXTEL
|
||||
// call routing system. Most IAXTEL numbers begin with 1-700, but
|
||||
// there are exceptions. For more information, and to sign
|
||||
// up, please go to www.gnophone.com or www.iaxtel.com
|
||||
//
|
||||
context ael-iaxtel700 {
|
||||
_91700XXXXXXX => Dial(IAX2/${IAXINFO-AEL}@iaxtel.com/${EXTEN:1}@iaxtel);
|
||||
};
|
||||
|
||||
//
|
||||
// The SWITCH statement permits a server to share the dialplan with
|
||||
// another server. Use with care: Reciprocal switch statements are not
|
||||
|
@ -279,13 +267,12 @@ context ael-longdistance {
|
|||
|
||||
context ael-local {
|
||||
//
|
||||
// Master context for local, toll-free, and iaxtel calls only
|
||||
// Master context for local and toll-free calls only
|
||||
//
|
||||
ignorepat => 9;
|
||||
includes {
|
||||
ael-default;
|
||||
ael-trunklocal;
|
||||
ael-iaxtel700;
|
||||
ael-trunktollfree;
|
||||
ael-iaxprovider;
|
||||
};
|
||||
|
|
|
@ -61,8 +61,9 @@ writeprotect=no
|
|||
; this value to "yes" !!
|
||||
; Please, if you try this out, and are forced to return to the
|
||||
; old pattern matcher, please report your reasons in a bug report
|
||||
; on https://issues.asterisk.org. We have made good progress in providing
|
||||
; something compatible with the old matcher; help us finish the job!
|
||||
; on https://github.com/asterisk/asterisk/issues. We have made good
|
||||
; progress in providing something compatible with the old matcher; help us
|
||||
; finish the job!
|
||||
;
|
||||
; This value can be switched at runtime using the cli command "dialplan set extenpatternmatchnew true"
|
||||
; or "dialplan set extenpatternmatchnew false", so you can experiment to your hearts content.
|
||||
|
@ -123,8 +124,6 @@ clearglobalvars=no
|
|||
CONSOLE=Console/dsp ; Console interface for demo
|
||||
;CONSOLE=DAHDI/1
|
||||
;CONSOLE=Phone/phone0
|
||||
IAXINFO=guest ; IAXtel username/password
|
||||
;IAXINFO=myuser:mypass
|
||||
TRUNK=DAHDI/G2 ; Trunk interface
|
||||
;
|
||||
; Note the 'G2' in the TRUNK variable above. It specifies which group (defined
|
||||
|
@ -365,12 +364,12 @@ include => trunkld
|
|||
|
||||
[local]
|
||||
;
|
||||
; Master context for local, toll-free, and iaxtel calls only
|
||||
; Master context for local and toll-free calls only
|
||||
;
|
||||
ignorepat => 9
|
||||
include => default
|
||||
include => trunklocal
|
||||
include => iaxtel700
|
||||
include => iax2-trunk
|
||||
include => trunktollfree
|
||||
include => iaxprovider
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue