mirror of
https://github.com/asterisk/asterisk.git
synced 2026-03-23 22:33:34 +00:00
Compare commits
468 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fa9cd9521 | ||
|
|
c6363dc1d7 | ||
|
|
0bd9a22495 | ||
|
|
63046de1cb | ||
|
|
e132aba5c9 | ||
|
|
4196243583 | ||
|
|
9ce061aa3f | ||
|
|
168ed954eb | ||
|
|
0d1660a369 | ||
|
|
ebb8253d2f | ||
|
|
11f81ad761 | ||
|
|
ad8786f355 | ||
|
|
17fd431b31 | ||
|
|
45c9175c77 | ||
|
|
8355d829bb | ||
|
|
b14b81d8b4 | ||
|
|
e875a17b48 | ||
|
|
7f9f2f6543 | ||
|
|
b2cfc35315 | ||
|
|
31c57904fd | ||
|
|
0ead801f25 | ||
|
|
009b812d76 | ||
|
|
a0d4959fc5 | ||
|
|
11c11d27bc | ||
|
|
6b36952664 | ||
|
|
afdd1c5512 | ||
|
|
ac303de4aa | ||
|
|
508b3ba688 | ||
|
|
75b0c4445a | ||
|
|
2f5ced421e | ||
|
|
d09714e12b | ||
|
|
52171d4c26 | ||
|
|
8bd93a96ac | ||
|
|
5a80b57ccf | ||
|
|
b2fb3d501c | ||
|
|
6cf8ee01a6 | ||
|
|
153119a7bc | ||
|
|
9b8ba163f2 | ||
|
|
bfac0241be | ||
|
|
09f5f15a72 | ||
|
|
84957104b2 | ||
|
|
312008513e | ||
|
|
08630905fa | ||
|
|
83ef83a5f8 | ||
|
|
2595998b8f | ||
|
|
7fe4d52579 | ||
|
|
39d4e1253b | ||
|
|
f7eada3cff | ||
|
|
405ff91e92 | ||
|
|
ce07ed72c0 | ||
|
|
150fc177cd | ||
|
|
c73ad9e6f9 | ||
|
|
cf638de6ad | ||
|
|
40ea953811 | ||
|
|
8061ba32c3 | ||
|
|
120e4bc237 | ||
|
|
4fc7030156 | ||
|
|
34d2254f70 | ||
|
|
b47ed67c84 | ||
|
|
e6f049f9bf | ||
|
|
7491dba549 | ||
|
|
3e7d040f3f | ||
|
|
c78761cc7c | ||
|
|
6b6eb7977b | ||
|
|
a795d02a30 | ||
|
|
2faf30d730 | ||
|
|
c2a39b5c64 | ||
|
|
ffc2c34f15 | ||
|
|
57cb8c168c | ||
|
|
f1274662f4 | ||
|
|
9f6e57f90a | ||
|
|
8ff6d5cabf | ||
|
|
298718ebaa | ||
|
|
171872003b | ||
|
|
5fe5bf08b2 | ||
|
|
cf238fa10d | ||
|
|
7e3d58e030 | ||
|
|
508b530e39 | ||
|
|
5e168ffd67 | ||
|
|
9e05fb4f39 | ||
|
|
a2c28355e0 | ||
|
|
0e4c9f9765 | ||
|
|
4d504015d4 | ||
|
|
3e2bd82411 | ||
|
|
2877470389 | ||
|
|
4a15544b22 | ||
|
|
9400e87db0 | ||
|
|
200ca9631d | ||
|
|
4f1e96c0b6 | ||
|
|
eac2ef9374 | ||
|
|
dd70231e97 | ||
|
|
02bb0b5e22 | ||
|
|
f14775e845 | ||
|
|
ee90d54c2e | ||
|
|
f4c2a167ed | ||
|
|
0ad6773b76 | ||
|
|
320be7fff9 | ||
|
|
0c2abdf74e | ||
|
|
0b3cfb26b4 | ||
|
|
cbc30c9163 | ||
|
|
71a395e3e3 | ||
|
|
b2073cb75f | ||
|
|
df905cf5a2 | ||
|
|
cf45c660cc | ||
|
|
2995096956 | ||
|
|
a6e8158ddc | ||
|
|
68206d0890 | ||
|
|
6dbcb94c01 | ||
|
|
91f9740856 | ||
|
|
083cf7af1d | ||
|
|
984142aebe | ||
|
|
29f7161c9b | ||
|
|
6618cec0d7 | ||
|
|
ebe377fc5c | ||
|
|
268eedc2f3 | ||
|
|
eadb02a84f | ||
|
|
c98832d092 | ||
|
|
888c329e65 | ||
|
|
548ce34d97 | ||
|
|
2e4b4764fb | ||
|
|
bf1069b3ae | ||
|
|
f717cd2719 | ||
|
|
cda604fabe | ||
|
|
6125965ff5 | ||
|
|
a6cea6892b | ||
|
|
1031a3c36d | ||
|
|
89d62574ef | ||
|
|
262e9c3003 | ||
|
|
43e32456ca | ||
|
|
fcb8ab5da6 | ||
|
|
0f5dbaf7ec | ||
|
|
8e267ac60a | ||
|
|
794d0b2c68 | ||
|
|
8c97879e99 | ||
|
|
47f2779b9d | ||
|
|
c77e7ed452 | ||
|
|
a9fd6fca9e | ||
|
|
57b353cebf | ||
|
|
07c87b82ff | ||
|
|
f1c3d761aa | ||
|
|
34f9e85a11 | ||
|
|
56cf655835 | ||
|
|
4d1e5adfdc | ||
|
|
3d263c243a | ||
|
|
334e508e66 | ||
|
|
28eedd8e44 | ||
|
|
e0aaf08b3b | ||
|
|
e16718bb2e | ||
|
|
1e70fa09cd | ||
|
|
a2f752ec6c | ||
|
|
3ad71d439c | ||
|
|
d667d39367 | ||
|
|
c96d5eabad | ||
|
|
941ba1c5be | ||
|
|
17045b502f | ||
|
|
916f6b627a | ||
|
|
e56c7ba459 | ||
|
|
34bc1ac241 | ||
|
|
4920426fbd | ||
|
|
e5f2fef425 | ||
|
|
4faec1b262 | ||
|
|
efc4fdf079 | ||
|
|
8b6aa2741c | ||
|
|
90fea452ac | ||
|
|
8451fd7f7d | ||
|
|
ed8a15efce | ||
|
|
6bee09440d | ||
|
|
d54b330a2e | ||
|
|
621b5be6cd | ||
|
|
687846975e | ||
|
|
315e0c3054 | ||
|
|
4f4009663c | ||
|
|
48f1386fff | ||
|
|
8f0e43be23 | ||
|
|
4b165caef4 | ||
|
|
4a14b7c071 | ||
|
|
e12e134ef9 | ||
|
|
7b319eb03a | ||
|
|
98d05f22ac | ||
|
|
7ba20c5024 | ||
|
|
2e6d346313 | ||
|
|
8aaa9e194e | ||
|
|
60a9627192 | ||
|
|
28a319be01 | ||
|
|
f066af663f | ||
|
|
1e2baaf12e | ||
|
|
f56ba6a441 | ||
|
|
2ea44416b2 | ||
|
|
78f6f43d19 | ||
|
|
42b84afa40 | ||
|
|
be446083ab | ||
|
|
61538bec92 | ||
|
|
4402a86f93 | ||
|
|
4ead78e851 | ||
|
|
d056623e60 | ||
|
|
a281bef2b3 | ||
|
|
4b30e9ee37 | ||
|
|
2b1a8d34d9 | ||
|
|
bc70235479 | ||
|
|
857bf5486b | ||
|
|
4d6e2801d1 | ||
|
|
f4a80f1609 | ||
|
|
26d298f693 | ||
|
|
926270782f | ||
|
|
fd7f18421d | ||
|
|
ee53dc7286 | ||
|
|
d565dc3225 | ||
|
|
fe07794bba | ||
|
|
4aa9d15438 | ||
|
|
83e1402d8a | ||
|
|
f1c93a8ff3 | ||
|
|
a004c5ca9e | ||
|
|
d6efd17100 | ||
|
|
3aeb519068 | ||
|
|
eb767e674f | ||
|
|
b4af00a20f | ||
|
|
39e574a8bd | ||
|
|
04c06471e6 | ||
|
|
687b01f298 | ||
|
|
8e59e3e38c | ||
|
|
1dec1dfb9c | ||
|
|
292059c9cb | ||
|
|
63b6e2a2a1 | ||
|
|
9420fee71d | ||
|
|
ece9341019 | ||
|
|
e608ffdaa9 | ||
|
|
684ffcce61 | ||
|
|
a346745899 | ||
|
|
5ef097f5d7 | ||
|
|
d35a82a5b6 | ||
|
|
b2ab9ffaec | ||
|
|
2e88a61fa3 | ||
|
|
9e1d3683fa | ||
|
|
528d2dcff5 | ||
|
|
40969e6b29 | ||
|
|
59814ed3e5 | ||
|
|
8f2106c5e3 | ||
|
|
37a80ea3bc | ||
|
|
a46350e315 | ||
|
|
e1a6d33ed0 | ||
|
|
4979d7fbb1 | ||
|
|
bdcbd76e13 | ||
|
|
081e28667e | ||
|
|
1ef98ac6de | ||
|
|
b90f6b40d9 | ||
|
|
2bb388ebc8 | ||
|
|
1714f299c9 | ||
|
|
5571294b91 | ||
|
|
3696407c67 | ||
|
|
76b18e2d6e | ||
|
|
d9d599a84d | ||
|
|
36d08dc8f0 | ||
|
|
3035547927 | ||
|
|
517413928f | ||
|
|
b3ee85ee4a | ||
|
|
540a2571ca | ||
|
|
53840ccffc | ||
|
|
61d87e19f1 | ||
|
|
63a98a2bed | ||
|
|
4542f92842 | ||
|
|
fa023ceccf | ||
|
|
6c5bf0d542 | ||
|
|
a3b4b4459c | ||
|
|
58b1b7f825 | ||
|
|
4f5f53706d | ||
|
|
a87fb09763 | ||
|
|
fb3de07e39 | ||
|
|
02bb7498b0 | ||
|
|
8c9f25ece1 | ||
|
|
6356638b6a | ||
|
|
659eb8bd36 | ||
|
|
2ef418765c | ||
|
|
33cffbd344 | ||
|
|
5d877f53ed | ||
|
|
334bd9f57d | ||
|
|
a6fb9eef88 | ||
|
|
d9d070e30b | ||
|
|
73f50db89e | ||
|
|
d1b6552ef7 | ||
|
|
8b079c4bf2 | ||
|
|
22db312ca1 | ||
|
|
fbc3e2e3ce | ||
|
|
d2d967d775 | ||
|
|
c69bb18184 | ||
|
|
dc5042762f | ||
|
|
efcc597742 | ||
|
|
b0a516034f | ||
|
|
2880956743 | ||
|
|
56dd7681f8 | ||
|
|
a6d0cc6c02 | ||
|
|
9c92ca2d27 | ||
|
|
f8526ef3ba | ||
|
|
96624911f0 | ||
|
|
5967d8f2bc | ||
|
|
dcd5da730a | ||
|
|
3c54495507 | ||
|
|
f9eedef7ee | ||
|
|
de117b689e | ||
|
|
5799a6a60c | ||
|
|
a97e1f873f | ||
|
|
74239cb26c | ||
|
|
fa7a75c886 | ||
|
|
4a187156b2 | ||
|
|
f45b8fcc70 | ||
|
|
1db571cb37 | ||
|
|
8289500dd5 | ||
|
|
ec9f642dc3 | ||
|
|
f2a2787b42 | ||
|
|
da0fb57df3 | ||
|
|
10378ed083 | ||
|
|
b0b9c0928b | ||
|
|
9386b25b51 | ||
|
|
9a81d7270e | ||
|
|
29062def81 | ||
|
|
ad851c87b5 | ||
|
|
b67759975d | ||
|
|
f4adada2e8 | ||
|
|
a4671a3d88 | ||
|
|
379d3a133c | ||
|
|
dd8d225b4e | ||
|
|
78693d31e9 | ||
|
|
49d18cef95 | ||
|
|
cc8996361c | ||
|
|
8060744e05 | ||
|
|
799ff464f2 | ||
|
|
b107f9c1c9 | ||
|
|
0e39f602ea | ||
|
|
a391baf1be | ||
|
|
3384173257 | ||
|
|
51ea5a2797 | ||
|
|
1298f6585d | ||
|
|
2ebc21f175 | ||
|
|
6c6ec855a7 | ||
|
|
10bbbf2c14 | ||
|
|
fcf810801f | ||
|
|
665cf4fb64 | ||
|
|
6f60468d3d | ||
|
|
00f2912aaa | ||
|
|
b32ffb8190 | ||
|
|
4ceafc96b1 | ||
|
|
bed2bda4f4 | ||
|
|
a97bf16d2d | ||
|
|
8a987daf3b | ||
|
|
8d135aa5ae | ||
|
|
0a4530cead | ||
|
|
ad1ee839cc | ||
|
|
1b53088a4b | ||
|
|
a5b4c8881e | ||
|
|
2583e00e03 | ||
|
|
7e3f8ee419 | ||
|
|
b035299213 | ||
|
|
4020387145 | ||
|
|
3e7c0ce67f | ||
|
|
0caa4033c9 | ||
|
|
cd65e922b2 | ||
|
|
90f22a990d | ||
|
|
0b74a19625 | ||
|
|
429432f5ff | ||
|
|
46f97f76a5 | ||
|
|
ec3b5c9b5b | ||
|
|
a160a8c9d5 | ||
|
|
65d6be272d | ||
|
|
1e75116e74 | ||
|
|
187942d00a | ||
|
|
418ff58e61 | ||
|
|
bb6333e01f | ||
|
|
df6c1f8b39 | ||
|
|
2b9417e2e5 | ||
|
|
f53fe9761c | ||
|
|
902661dd8e | ||
|
|
646dc459fa | ||
|
|
3d48bc2f61 | ||
|
|
28109dedf5 | ||
|
|
10781d30d2 | ||
|
|
c0c9a1a5f0 | ||
|
|
13e54102fd | ||
|
|
f4470352ff | ||
|
|
77ad568144 | ||
|
|
1b05a4639d | ||
|
|
e73c4e4a44 | ||
|
|
60c2988f53 | ||
|
|
35dd204d76 | ||
|
|
8eb984caec | ||
|
|
9801c6c63f | ||
|
|
1b754ff59d | ||
|
|
713ddd2172 | ||
|
|
d3cb67a4cd | ||
|
|
1f02d433b3 | ||
|
|
dbf6ad2ecd | ||
|
|
c6938534bd | ||
|
|
ac659af3ea | ||
|
|
bdabdcbf1e | ||
|
|
457d6f2822 | ||
|
|
0d23dbb490 | ||
|
|
3cd77bab28 | ||
|
|
a39d05855e | ||
|
|
66d0e5756f | ||
|
|
591eb8fac1 | ||
|
|
0ae97da489 | ||
|
|
014a0a178b | ||
|
|
5e8f6247fb | ||
|
|
de1c7219ea | ||
|
|
7352e07e12 | ||
|
|
bfe333ed67 | ||
|
|
ec25eab17f | ||
|
|
e212b875fc | ||
|
|
92f1ec6be4 | ||
|
|
4443cc2107 | ||
|
|
d612a5ce42 | ||
|
|
808ecc893a | ||
|
|
1e9087357b | ||
|
|
124c88fba3 | ||
|
|
ef3f4d29c9 | ||
|
|
67478da442 | ||
|
|
67c451783b | ||
|
|
517e58c5df | ||
|
|
b86dbdb22c | ||
|
|
807bbf3461 | ||
|
|
8b1cdad2f5 | ||
|
|
ce25d82aa7 | ||
|
|
d354512eca | ||
|
|
f0131ed0f2 | ||
|
|
a4111e1f93 | ||
|
|
f7098a28be | ||
|
|
fdffdc9364 | ||
|
|
d91f0dadc9 | ||
|
|
291309df4a | ||
|
|
eb08e2600f | ||
|
|
0faa51794d | ||
|
|
5f5017defd | ||
|
|
737023e9d4 | ||
|
|
e98d85bf09 | ||
|
|
6adefd13c1 | ||
|
|
5c37031b1c | ||
|
|
141a82674e | ||
|
|
2423b41fd9 | ||
|
|
f85277c7b3 | ||
|
|
d60b955324 | ||
|
|
663636b515 | ||
|
|
8bccbf9afb | ||
|
|
1d10b782b4 | ||
|
|
3cd48f9a41 | ||
|
|
d50ef1d333 | ||
|
|
fbcd14b30b | ||
|
|
09ca3690b5 | ||
|
|
6db145dd07 | ||
|
|
0ff4ffd27f | ||
|
|
ba5a68e731 | ||
|
|
df358f42e0 | ||
|
|
0e518edee4 | ||
|
|
dea58d2e88 | ||
|
|
97efb246b3 | ||
|
|
17a3454b92 | ||
|
|
3680bb43cd | ||
|
|
b0180d6665 | ||
|
|
f9be91101f | ||
|
|
e16c9c9ecd | ||
|
|
57c86cec8e | ||
|
|
d600998f7e | ||
|
|
a75365ea8e | ||
|
|
8b34f224e7 | ||
|
|
97d664a128 | ||
|
|
04bcd2ff23 | ||
|
|
3f9482f7bc | ||
|
|
ebf15d6151 | ||
|
|
38f5196684 | ||
|
|
31457d7be1 | ||
|
|
a196c598b6 |
@@ -1 +0,0 @@
|
||||
33
|
||||
@@ -1 +0,0 @@
|
||||
33
|
||||
11
BSDmakefile
11
BSDmakefile
@@ -1,11 +0,0 @@
|
||||
# This is a convenience script for systems on which BSD make is the default,
|
||||
# such that typing 'make' will do what people expect, instead of producing an
|
||||
# error (due to incompatibilities between BSD make and GNU make).
|
||||
|
||||
.include "makeopts"
|
||||
|
||||
all::
|
||||
$(MAKE)
|
||||
|
||||
$(.TARGETS)::
|
||||
$(MAKE) $(.TARGETS)
|
||||
23
BUGS
23
BUGS
@@ -1,22 +1,23 @@
|
||||
Asterisk 1.0-RC-1 Known Major Bugs
|
||||
==================================
|
||||
* Some people still have issues with H.323
|
||||
* QuickNet driver still not entirely stable
|
||||
|
||||
Asterisk Bug Tracking Information
|
||||
=================================
|
||||
|
||||
To learn about and report Asterisk bugs, please visit
|
||||
the official Asterisk Bug Tracker at:
|
||||
To learn about and report Asterisk bugs or make feature
|
||||
requests, please visit the official Asterisk Bug Tracker
|
||||
at:
|
||||
|
||||
https://issues.asterisk.org
|
||||
http://bugs.digium.com
|
||||
|
||||
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
|
||||
|
||||
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://www.digium.com/bugtracker.html
|
||||
|
||||
Thank you!
|
||||
|
||||
Mark
|
||||
|
||||
374
CHANGES
374
CHANGES
@@ -1,374 +0,0 @@
|
||||
Changes since Asterisk 1.2:
|
||||
|
||||
* over 4,000 commits since 1.2
|
||||
* queue member naming
|
||||
* CLI commands rework
|
||||
o Change the way CLI commands are structured.
|
||||
o Most commands are now <module> <verb> <args>
|
||||
* chan_h323 update
|
||||
* RTP packetization
|
||||
* SLA (Shared Line Appearance) support
|
||||
* T.38 Passthrough Support for faxing in SIP
|
||||
* Generic channel jitterbuffer (spawned from RTP)
|
||||
* Variable Length DTMF for better DTMF compatibility
|
||||
* Improved chan_iax2 scalability by using multithreading
|
||||
* AEL2 has replaced the original implementation of AEL. The "2" is removed. For more details,
|
||||
read: http://www.voip-info.org/wiki/view/Asterisk+AEL2
|
||||
AEL is no longer considered experimental.
|
||||
* New sounds; English, Spanish, and French prompts, as well as music on hold files, in
|
||||
multiple Asterisk native formats.
|
||||
* IMAP storage of voicemail
|
||||
* Jabber/GoogleTalk integration
|
||||
* New speech recognition API for interfacing to different Voice Recognition software packages
|
||||
* much more customizable and portable build system
|
||||
o also for asterisk-addons
|
||||
* Radius CDR logging
|
||||
* SNMP support
|
||||
* SMDI (Simplified Message Desk Interface) support
|
||||
* Redesign of MusicOnHold configuration settings
|
||||
* Manager over HTTP
|
||||
* Significant chan_skinny updates
|
||||
* Significant chan_misdn updates
|
||||
* Improved SIP transfers
|
||||
* SIP MWI subscription support
|
||||
* Much improved support for SIP video
|
||||
* Control over SIP transfers and subscriptions (enable/disable per device)
|
||||
* ChanSpy whisper mode (Whisper Paging)
|
||||
* Configurable language support for saying dates and times
|
||||
* Significant architecture improvements for memory usage and performance
|
||||
* Media-only IAX2 transfers
|
||||
* Updates to the Radio Repeater app code
|
||||
* Deprecation of AgentCallbackLogin in favor of a dialplan-based solution
|
||||
* uClibc builds supported
|
||||
* Work done for freeBSD portability
|
||||
* Work done for Solaris portability
|
||||
* FreeTDS-based database can be used with Realtime
|
||||
* New internal data structure, stringfields, is implemented in IAX and SIP, reducing memory consumption by about 50%.
|
||||
* Use of thread local storage for reduced memory allocation/freeing and lower stack consumption
|
||||
* Reorganized files into docs/ main/ configs/, including name changes in some cases
|
||||
* Much effort was expended in arranging documentation in source files in doxygen format
|
||||
* Improved IP TOS support for IAX and SIP
|
||||
* Builtin mini HTTP server
|
||||
* Added support for Sigma Designs cards.
|
||||
* Frame header caching to reduce memory allocation/freeing
|
||||
* Passthrough and record/playback support for G.722 wideband audio
|
||||
* using mpg123 to play MP3 files for music-on-hold will be deprecated in 1.4 (start using the "native support")
|
||||
* New Apps:
|
||||
1. AMD() ;; Answering Machine Detection
|
||||
2. ChannelRedirect() ;; asynch goto, redirect chan to context/exten/priority
|
||||
3. ContinueWhile() ;; Addition to the While() suite. Acts like "continue".
|
||||
4. ExitWhile() ;; Addition to the While() suite. Acts like "break".
|
||||
5. ExtenSpy() ;; A close cousin to ChanSpy().
|
||||
6. FollowMe() ;; findme/followme call redirect app
|
||||
7. Log() ;; Send a message to the log, based on severity level.
|
||||
8. MacroExclusive() ;; No more than one invocation of this macro allowed at any one time.
|
||||
9. MorseCode() ;; turns strings into dits and dahs. A playground for ham radio licensees!
|
||||
10. OSPAuth() ;; OSP authentication
|
||||
11. QueueLog() ;; allows you to write your own events into the queue log
|
||||
12. SLAStation() ;; Shared Line Appearance
|
||||
13. SLATrunk() ;; Shared Line Appearance
|
||||
14. SpeechCreate() ;; Voice Recognition Engine interface...
|
||||
15. SpeechActivateGrammar()
|
||||
16. SpeechStart()
|
||||
17. SpeechBackground
|
||||
18. SpeechDeactivateGrammar()
|
||||
19. SpeechProcessingSound()
|
||||
20. SpeechDestroy()
|
||||
21. SpeechLoadGrammar()
|
||||
22. SpeechUnloadGrammar()
|
||||
23. StopMixMonitor() ;; to stop the MixMonitor App.
|
||||
24. TryExec() ;; execute dialplan app without fatal consequences
|
||||
* Apps removed:
|
||||
1. CheckGroup -- do a comparison to ${GROUP()}
|
||||
2. Curl -- use the function CURL() instead
|
||||
3. Cut -- use the function CUT() instead
|
||||
4. DateTime -- use sayunixtime() app instead.
|
||||
5. DBget -- deprecated in 1.2, now removed.
|
||||
6. DBput -- deprecated in 1.2, now removed.
|
||||
7. Enumlookup -- use the function ENUMLOOKUP() instead
|
||||
8. Eval -- use the function EVAL() instead
|
||||
9. GetGroupCount -- use the function GROUP_COUNT() instead
|
||||
10. GetGroupMatchCount -- use the function GROUP_MATCH_COUNT() instead
|
||||
11. Intercom -- use the chan_oss module instead
|
||||
12. Math -- use the function MATH() instead
|
||||
13. MD5 -- use the function MD5() instead
|
||||
14. SetCIDname -- use the function CALLERID(name) instead
|
||||
15. SetCIDnum -- use the function CALLERID(number) instead
|
||||
16. SetGroup -- use Set(GROUP=group) instead
|
||||
17. SetRDNIS -- use the function CALLERID(rdnis) instead
|
||||
18. Sql_postgres -- was deprecated in 1.2, now removed
|
||||
19. Txtcidname -- use the function TXTCIDNAME instead
|
||||
* New Dialplan Functions:
|
||||
1. ARRAY()
|
||||
2. BASE_64_DECODE()
|
||||
3. BASE_64_ENCODE()
|
||||
4. CHANNEL()
|
||||
5. CURL()
|
||||
6. CUT()
|
||||
7. DB_DELETE()
|
||||
8. FILTER()
|
||||
9. GLOBAL()
|
||||
10. IFTIME()
|
||||
11. KEYPADHASH()
|
||||
12. ODBC()
|
||||
13. QUOTE()
|
||||
14. RAND()
|
||||
15. REALTIME()
|
||||
16. SHA1()
|
||||
17. SORT()
|
||||
18. SPRINTF()
|
||||
19. SQL_ESC()
|
||||
20. STAT()
|
||||
21. STRPTIME()
|
||||
22. AUDIOHOOK_INHERIT()
|
||||
* Apps that have changes to their interface:
|
||||
1. Authenticate() -- optional maxdigits argument added.
|
||||
2. ChanSpy() -- new options:
|
||||
o w -- Enable 'whisper' mode, so the spying channel can talk to...
|
||||
o W -- Enable 'private whisper' mode, so the spying channel can...
|
||||
3. DBdel() -- now marked as DEPRECATED in favor of the DB_DELETE func
|
||||
4. Dial()
|
||||
o New Option: O([x]) for Zaptel operator mode
|
||||
o New Option: K/k parking via dtmf tones
|
||||
5. Dictate() -- optional filename argument added.
|
||||
6. Directory() -- new option: e - In addition to the name, also read the extension number...
|
||||
7. ForkCDR() -- new options:
|
||||
o 'a' -- update answer time on new cdr
|
||||
o 'A' -- Lock the orig CDR answer time against changes.
|
||||
o 'D' -- Copy the disposition from the orig to the new CDR.
|
||||
o 'd' -- clear the dstcannel field in the new CDR.
|
||||
o 'e' -- set the end time of the original CDR.
|
||||
o 'R' -- do NOT reset the new CDR.
|
||||
o 's' -- Add/change var in orig CDR.
|
||||
o 'T' -- Force ast_cdr_end, answer to obey LOCKED flag for the orig. CDR.
|
||||
-- ast_cdr_setvar will be forced also (used by the CDR() func in write mode)
|
||||
8. Meetme() -- new options:
|
||||
o 'I' -- announce user join/leave without review
|
||||
o 'l' -- set listen only mode (Listen only, no talking)
|
||||
o 'o' -- set talker optimization - treats talkers who aren't speaking as...
|
||||
o '1' -- do not play message when first person enters
|
||||
9. MeetmeAdmin() -- new options:
|
||||
o 'r' -- Reset one user's volume settings
|
||||
o 'R' -- Reset all users volume settings
|
||||
o 's' -- Lower entire conference speaking volume
|
||||
o 'S' -- Raise entire conference speaking volume
|
||||
o 't' -- Lower one user's talk volume
|
||||
o 'T' -- Lower all users talk volume
|
||||
o 'u' -- Lower one user's listen volume
|
||||
o 'U' -- Lower all users listen volume
|
||||
o 'v' -- Lower entire conference listening volume
|
||||
o 'V' -- Raise entire conference listening volume
|
||||
10. OSPFinish() : now also can return ERROR result.
|
||||
11. OSPLookup() : Sets more variables, also now returns ERROR result.
|
||||
12. Page() -- New option: r - record the page into a file (see 'r' for app_meetme)
|
||||
13. Pickup() -- multiple extensions, PICKUPMARK; read the description!
|
||||
14. Queue()
|
||||
o New Argument: AGI
|
||||
o New option: i
|
||||
15. Random() -- is now deprecated in 1.4
|
||||
16. Read() -- replace 'skip' and 'noanswer' options with 's', 'n', add 'i' option.
|
||||
17. Record() -- New option: 'x' : ignore all terminator keys (DTMF) and keep recording until hangup
|
||||
18. UserEvent() -- slight change in behavior. Read the description.
|
||||
19. VoiceMailMain() -- new a(#) option, goes to folder # directly.
|
||||
20. WaitForSilence() -- new optional 3rd arg, time delay before returning.
|
||||
* Functions that have changes to their interfaces:
|
||||
1. CDR -- new options: u and s
|
||||
2. LANGUAGE -- Deprecated. Use CHANNEL(language) instead.
|
||||
3. MUSICCLASS -- Deprecated. Use CHANNEL(musicclass) instead.
|
||||
* Configuration File Changes:
|
||||
1. NEW config files:
|
||||
1. amd.conf -- Answering Machine Detection parameters
|
||||
2. followme.conf -- parameters for the findme/followme call forwarding
|
||||
3. func_odbc.conf -- define sql access functions here
|
||||
4. gtalk.conf -- how to handle gtalk protocol calls
|
||||
5. h323.conf -- h323 configuration
|
||||
6. http.conf -- config for the builtin mini-http server in asterisk
|
||||
7. jabber.conf -- jabber interface
|
||||
8. jingle.conf -- jingle protocol interface config
|
||||
10. res_snmp.conf -- to enable snmp in asterisk, and define full/sub agent status
|
||||
11. say.conf -- define per-language rules for numbers, dates, etc.
|
||||
12. skinny.conf -- for those special skinny phones you want to use...
|
||||
13. sla.conf -- Shared Line Appearance config
|
||||
14. smdi.conf -- SMDI messaging config
|
||||
15. udptl.conf -- T38's udptl transport config
|
||||
16. users.conf -- user config
|
||||
2. Changes to Existing Config files:
|
||||
1. In General:
|
||||
o Jitterbuffer support added to several channels. Usually adds these variables to a config file:
|
||||
1. jbenable
|
||||
2. jbmaxsize
|
||||
3. jbresyncthreshold
|
||||
4. jbimpl
|
||||
5. jblog
|
||||
o MusicOnHold upgrade introduces two new variables:
|
||||
1. mohinterpret
|
||||
2. mohsuggest
|
||||
2. agents.conf
|
||||
o multiplelogin variable added
|
||||
o maxlogintries variable added
|
||||
o autologoffunavail variable added
|
||||
o endcall variable added
|
||||
o goodbye variable added
|
||||
o createlink variable REMOVED
|
||||
3. alsa.conf
|
||||
o mohinterpret variable added
|
||||
o Jitterbuffer variables added
|
||||
4. cdr.conf
|
||||
o endbeforehexten variable added
|
||||
o sections for csv and radius added, with variables usegmtime, loguniqueid,
|
||||
loguserfield, and radiuscfg variables.
|
||||
5. cdr_tds.conf
|
||||
o table variable added
|
||||
6. extensions.ael
|
||||
o Many upgrades. See the info at http://www.voip-info.org/wiki/view/Asterisk+AEL2
|
||||
7. extensions.conf
|
||||
o autofallthru now set to "yes" by default
|
||||
o userscontext variable added
|
||||
o added info/examples on paging and hints.
|
||||
8. features.conf
|
||||
o parkedplay variable added (who to beep at)
|
||||
o parkedmusicclass
|
||||
o atxfernoanswertimeout variable added
|
||||
o parkcall variable added (one step parking)
|
||||
o improved documentation for dynamic feature declarations!
|
||||
o added parkedcallltransfers option to control builtin transfers with parking
|
||||
o added parkedcallparking option to control one touch parking w/ parking pickup
|
||||
o added parkedcallhangup option to control disconnect feature w/ parking pickup
|
||||
o added parkedcallrecording option to control one-touch record w/ parking pickup
|
||||
o added BRIDGE_FEATURES variable to set available features for a channel
|
||||
9. iax.conf
|
||||
o adsi variable added
|
||||
o mohinterpret variable added
|
||||
o mohsuggest variable added
|
||||
o jitterbuffer updates
|
||||
o iaxthreadcount variable added
|
||||
o iaxmaxthreadcount variable added
|
||||
o the way to specify TOS has changed.
|
||||
o mailboxdetail variable has been REMOVED.
|
||||
10. indications.conf
|
||||
o [bg] entry added (Bulgaria).
|
||||
o [il] entry added (Israel)
|
||||
o [in] entry added (India)
|
||||
o [jp] entry added (Japan)
|
||||
o [my] entry added (Malaysia)
|
||||
o [th] entry added (Thailand)
|
||||
11. manager.conf
|
||||
o webenabled variable added
|
||||
o httptimeout variable added
|
||||
o timestampevents variable added
|
||||
12. mgcp.conf
|
||||
o Jitterbuffer support added
|
||||
13. misdn.conf
|
||||
o l1watcher_timeout variable added
|
||||
o pp_l2_check variable added
|
||||
o echocancelwhenbridged variable added
|
||||
o echotraining variable added
|
||||
o max_incoming variable added
|
||||
o max_outgoing variable added
|
||||
14. modules.conf
|
||||
o a comment for preloading res_speech.so is added
|
||||
o mention of global symbols is removed
|
||||
o obsolesced entries for chan_modem_* and app_intercom have been removed
|
||||
15. musiconhold.conf
|
||||
o the default is now to do native moh from /var/lib/asterisk/moh
|
||||
16. osp.conf
|
||||
o authpolicy variable added
|
||||
17. oss.conf
|
||||
o debug variable added
|
||||
o device variable added
|
||||
o mixer variable added
|
||||
o boost variable added
|
||||
o callerid variable added
|
||||
o autohangup variable added
|
||||
o queuesize variable added
|
||||
o frags variable added
|
||||
o JitterBuffer support
|
||||
o sections to define alternate sound cards
|
||||
18. queues.conf
|
||||
o autofill variable added
|
||||
o monitor-type variable added
|
||||
o musiconhold is now musicclass, with a difference in interpretation
|
||||
o autofill variable added
|
||||
o autopause variable added
|
||||
o setinterfacevar variable added
|
||||
o ringinuse variable added
|
||||
19. res_odbc.conf
|
||||
o pooling variable added
|
||||
20. rpt.conf
|
||||
o duplex variable added
|
||||
o tailmessagetime variable added
|
||||
o tailsquashedtime variable added
|
||||
o tailmessages variable added
|
||||
21. rtp.conf
|
||||
o rtcpinterval varaible added
|
||||
22. sip.conf
|
||||
o allowguest variable can't be set to 'osp'
|
||||
o allowoverlap variable added
|
||||
o allowtransfer variable added
|
||||
o limitonpeer variable added
|
||||
o directrtpsetup variable added
|
||||
o buggymwi variable added
|
||||
o ospauth variable REMOVED
|
||||
o notifyhold variable added
|
||||
o autoframing variable added
|
||||
o tos variable REMOVED
|
||||
o tos_sip variable added
|
||||
o tos_audio variable added
|
||||
o tos_video variable added
|
||||
o minexpiry variable added
|
||||
o t1min variable added
|
||||
o musicclass variable REMOVED
|
||||
o mohinterpret variable added
|
||||
o mohsuggest variable added
|
||||
o allowsubscribe variable added
|
||||
o videosupport variable added
|
||||
o maxcallbitrate variable added
|
||||
o g726nonstandard variable added
|
||||
o dumphistory variable added
|
||||
o t38pt_udptl variable added
|
||||
o t38pt_rtp variable added
|
||||
o t38pt_tcp variable added
|
||||
o rfc2833compensate variable added
|
||||
o matchexterniplocally variable added
|
||||
o canreinvite variable can also now be set to 'nonat'
|
||||
o rtsavesysname variable added
|
||||
o JitterBuffer support added
|
||||
o t38pt_usertpsource variable added
|
||||
o regcontext variable can contains multiple contexts separated by an '&'
|
||||
23. skinny.conf
|
||||
o port variable renamed to bindport
|
||||
o JitterBuffer support added
|
||||
o model variable REMOVED
|
||||
o mohinterpret variable added
|
||||
o mohsuggest variable added
|
||||
o speeddial variable added
|
||||
o addon variable added
|
||||
24. voicemail.conf
|
||||
o userscontext variable added
|
||||
o smdiport variable added
|
||||
o attachfmt variable added
|
||||
o volgain variable added
|
||||
o tempgreetwarn variable added
|
||||
25. zapata.conf
|
||||
o pritimer variable has improved documentation
|
||||
o New signalling method: fgccama
|
||||
o New signalling method: fgccamamf
|
||||
o outsignalling variable added
|
||||
o distinctiveringaftercid variable added
|
||||
o cidsignalling now also accepts v23_jp, and smdi
|
||||
o usesmdi variable added
|
||||
o smdiport variable added
|
||||
o mohinterpret variable added
|
||||
o mohsuggest variable added
|
||||
o JitterBuffer support added
|
||||
* Removed Codecs/Channels:
|
||||
1. codec_g723 was removed because the actual codec implementation it was designed to use is not distributable
|
||||
2. chan_modem_* and related modules are gone because the kernel support for those interfaces is old, buggy and unsupported
|
||||
* New Utils:
|
||||
1. aelparse -- compile .ael files outside of asterisk
|
||||
* New manager events:
|
||||
1. OriginateResponse event comes to replace OriginateSuccess and OriginateFailure
|
||||
* iLBC source code no longer included (see UPGRADE.txt for details)
|
||||
* New CLI command "pri show version" that shows the current version of libpri
|
||||
that the library was built against (requires a version of libpri since this API
|
||||
feature was added).
|
||||
341
COPYING
341
COPYING
@@ -1,341 +0,0 @@
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
136
CREDITS
136
CREDITS
@@ -1,6 +1,6 @@
|
||||
|
||||
=== DEVELOPMENT SUPPORT ===
|
||||
We'd like to thank the following companies for helping fund development of
|
||||
I'd like to thank the following companies for helping fund development of
|
||||
Asterisk:
|
||||
|
||||
Pilosoft, Inc. - for supporting ADSI development in Asterisk
|
||||
@@ -13,8 +13,6 @@ Telesthetic - for supporting SIP development
|
||||
|
||||
Christos Ricudis - for substantial code contributions
|
||||
|
||||
nic.at - ENUM support in Asterisk
|
||||
|
||||
Paul Bagyenda, Digital Solutions - for initial Voicetronix driver development
|
||||
|
||||
=== WISHLIST CONTRIBUTERS ===
|
||||
@@ -27,151 +25,51 @@ Wasim - Hangup detect
|
||||
* Thanks to QuickNet Technologies for their donation of an Internet
|
||||
PhoneJack and Linejack card to the project. (http://www.quicknet.net)
|
||||
|
||||
* Thanks to VoipSupply for their donation of Sipura ATAs to the project for
|
||||
T.38 testing. (http://www.voipsupply.com)
|
||||
|
||||
* Thanks to Grandstream for their donation of ATAs to the project for
|
||||
T.38 testing. (http://www.grandstream.com)
|
||||
|
||||
=== MISCELLANEOUS PATCHES ===
|
||||
Jim Dixon - Zapata Telephony and app_rpt
|
||||
http://www.zapatatelephony.org/app_rpt.html
|
||||
|
||||
Russell Bryant - Asterisk 1.0 maintainer and misc. enhancements
|
||||
russelb@clemson.edu
|
||||
|
||||
Anthony Minessale II - Countless big and small fixes, and relentless forward push
|
||||
ChanSpy, ForkCDR, ControlPlayback, While/EndWhile, DumpChan, Dictate,
|
||||
MacroIf, ExecIf, ExecIfTime, RetryDial, MixMonitor applications; many realtime
|
||||
concepts and implementation pieces, including res_config_odbc; format_slin;
|
||||
cdr_custom; several features in Dial including L(), G() and enhancements to
|
||||
M() and D(); several CDR enhancements including CDR variables; attended
|
||||
transfer; one touch record; native MOH; manager eventmask; command line '-t'
|
||||
flag to allow recording/voicemail on nfs shares; #exec command and multiline
|
||||
comments in config files; setvar in iax and sip configs.
|
||||
anthmct@yahoo.com http://www.asterlink.com
|
||||
|
||||
Anthony Minessale - Countless big and small fixes, and relentless forward push
|
||||
anthmct@yahoo.com http://www.asterlink.com
|
||||
James Golovich - Innumerable contributions
|
||||
You can find him and asterisk-perl at http://asterisk.gnuinter.net
|
||||
|
||||
You can find him and asterisk-perl at http://asterisk.gnuinter.net
|
||||
Andre Bierwirth - Extension hints and status
|
||||
|
||||
Oliver Daudey - ISDN4Linux fixes
|
||||
|
||||
Pauline Middelink - ISDN4Linux patches and some general patches.
|
||||
She can be found at http://www.polyware.nl/~middelink/En/
|
||||
|
||||
She can be found at http://www.polyware.nl/~middelink/En/
|
||||
Jean-Denis Girard - Various contributions from the South Pacific Islands
|
||||
jd-girard@esoft.pf http://www.esoft.pf
|
||||
|
||||
William Jordan / Vonage - MySQL enhancements to Voicemail
|
||||
jd-girard@esoft.pf http://www.esoft.pf
|
||||
William Jordan / Vonage - MySQL enhancmenets to Voicemail
|
||||
wjordan@vonage.com
|
||||
|
||||
Jac Kersing - Various fixes
|
||||
|
||||
Steven Critchfield - Seek and Trunc functions for playback and recording
|
||||
critch@basesys.com
|
||||
|
||||
critch@basesys.com
|
||||
Jefferson Noxon - app_lookupcidname, app_db, and various other contributions
|
||||
|
||||
Klaus-Peter Junghanns - in-band DTMF on SIP and MGCP
|
||||
|
||||
Ross Finlayson - Dynamic RTP payload support
|
||||
|
||||
Mahmut Fettahlioglu - Audio recording, music-on-hold changes, alaw file
|
||||
format, and various fixes. Can be contacted at mahmut@oa.com.au
|
||||
|
||||
format, and various fixes. Can be contacted at mahmut@oa.com.au
|
||||
James Dennis - Cisco SIP compatibility patches to work with SIP service
|
||||
providers. Can be contacted at asterisk@jdennis.net
|
||||
|
||||
Tilghman Lesher - ast_localtime(); ast_say_date_with_format();
|
||||
GotoIfTime, Random, SayUnixTime, HasNewVoicemail applications;
|
||||
CUT, SORT, EVAL, CURL, FIELDQTY, STRFTIME, QUEUEAGENT* functions;
|
||||
and other innumerable bug fixes. http://asterisk.drunkcoder.com/
|
||||
|
||||
providers. Can be contacted at asterisk@jdennis.net
|
||||
Tilghman Lesher - - Route lookup code; ast_localtime(), ast_say_date_with_format();
|
||||
GotoIfTime, Random, SayUnixTime, HasNewVoicemail, and Cut applications,
|
||||
along with various other patches. http://asterisk.drunkcoder.com/
|
||||
Jayson Vantuyl - Manager protocol changes, various other bugs.
|
||||
jvantuyl@computingedge.net
|
||||
|
||||
Thorsten Lockert - OpenBSD, FreeBSD ports, making MacOS X port run on 10.3,
|
||||
dialplan include verification, route lookup on OpenBSD, SNMP agent
|
||||
support (res_snmp), various other bugs. tholo@sigmasoft.com
|
||||
|
||||
dialplan include verification, route lookup on OpenBSD, various other
|
||||
bugs. tholo@sigmasoft.com
|
||||
Brian West - ODBC support and Bug Marshaling
|
||||
|
||||
Josh Roberson - chan_zap reload support, Advanced Voicemail Features, other misc. patches,
|
||||
and Bug Marshalling. - josh@asteriasgi.com, http://www.asteriasgi.com
|
||||
|
||||
William Waites - syslog support, SIP NAT traversal for SIP-UA. ww@styx.org
|
||||
|
||||
Rich Murphey - Porting to FreeBSD, NetBSD, OpenBSD, and Darwin.
|
||||
rich@whiteoaklabs.com http://whiteoaklabs.com
|
||||
|
||||
Simon Lockhart - Porting to Solaris (based on work of Logan ???)
|
||||
simon@slimey.org
|
||||
|
||||
Olle E. Johansson - SIP RFC compliance, documentation and testing, testing, testing
|
||||
oej@edvina.net, http://edvina.net
|
||||
|
||||
Steve Kann - new jitter buffer for IAX2
|
||||
stevek@stevek.com
|
||||
|
||||
Constantine Filin - major contributions to the Asterisk Realtime Architecture
|
||||
|
||||
Steve Murphy - privacy support, $[ ] parser upgrade, AEL2 parser upgrade
|
||||
|
||||
Claude Patry - bug fixes, feature enhancements, and bug marshalling
|
||||
cpatry@gmail.com
|
||||
|
||||
Miroslav Nachev, miro@space-comm.com COSMOS Software Enterprises, Ltd.
|
||||
- for Variable for No Answer Timeout for Attended Transfer
|
||||
|
||||
Slav Klenov & Vanheuverzwijn Joachim - development of the generic jitterbuffer
|
||||
Securax Ltd. info@securax.be
|
||||
|
||||
Roy Sigurd Karlsbakk - providing funding for generic jitterbuffer development
|
||||
roy@karlsbakk.net, Briiz Telecom AS
|
||||
|
||||
Voop A/S, Nuvio Inc, Inotel S.A and Foniris Telecom A/S - funding for rewrite of SIP transfers
|
||||
|
||||
Philippe Sultan - RADIUS CDR module
|
||||
INRIA, http://www.inria.fr/
|
||||
|
||||
John Martin, Aupix - Improved video support in the SIP channel
|
||||
|
||||
Steve Underwood - Provided T.38 pass through support.
|
||||
|
||||
George Konstantoulakis - Support for Greek in voicemail added by InAccess Networks (work funded by HOL, www.hol.gr) gkon@inaccessnetworks.com
|
||||
|
||||
Daniel Nylander - Support for Swedish and Norwegian languages in voicemail. http://www.danielnylander.se/
|
||||
|
||||
Stojan Sljivic - An option for maximum number of messsages per mailbox in voicemail. Also an issue with voicemail synchronization has been fixed. GDS Partners www.gdspartners.com . stojan.sljivic@gdspartners.com
|
||||
|
||||
Bartosz Supczinski - Support for Polish added by DIR (www.dir.pl) Bartosz.Supczinski@dir.pl
|
||||
|
||||
James Rothenberger - Support for IMAP storage integration added by OneBizTone LLC Work funded by University of Pennsylvania jar@onebiztone.com
|
||||
|
||||
Paul Cadach - Bringing chan_h323 up to date, bug fixes, and more!
|
||||
|
||||
=== OTHER CONTRIBUTIONS ===
|
||||
John Todd - Monkey sounds and associated teletorture prompt
|
||||
Michael Jerris - bug marshaling
|
||||
Leif Madsen, Jared Smith and Jim van Meggelen - the Asterisk book
|
||||
available under a Creative Commons License at http://www.asteriskdocs.org
|
||||
Brian M. Clapper - poll.c emulation
|
||||
This product includes software developed by Brian M. Clapper <bmc@clapper.org>
|
||||
|
||||
=== HOLD MUSIC ===
|
||||
Music provided by www.opsound.org
|
||||
|
||||
=== OTHER SOURCE CODE IN ASTERISK ===
|
||||
Asterisk uses libedit, the lightweight readline replacement from NetBSD.
|
||||
The cdr_radius module uses libradiusclient-ng, which is also from NetBSD.
|
||||
They are BSD-licensed and require the following statement:
|
||||
|
||||
This product includes software developed by the NetBSD
|
||||
Foundation, Inc. and its contributors.
|
||||
|
||||
Digium did not implement the codecs in Asterisk. Here is the copyright on the
|
||||
I did not implement the codecs in asterisk. Here is the copyright on the
|
||||
GSM source:
|
||||
|
||||
Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
|
||||
@@ -196,7 +94,7 @@ And the copyright on the ADPCM source:
|
||||
Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The
|
||||
Netherlands.
|
||||
|
||||
All Rights Reserved
|
||||
All Rights Reserved
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose and without fee is hereby granted,
|
||||
|
||||
@@ -2,16 +2,19 @@ A PBX is only really useful if you can get calls into it. Of course, you
|
||||
can use Asterisk with VoIP calls (SIP, H.323, IAX), but you can also talk
|
||||
to the real PSTN through various cards.
|
||||
|
||||
Supported Hardware is divided into two general groups: DAHDI devices and
|
||||
non-DAHDI devices. The DAHDI compatible hardware supports pseudo-TDM
|
||||
conferencing and all call features through chan_dahdi, whereas non-DAHDI
|
||||
Supported Hardware is divided into two general groups: Zaptel devices and
|
||||
non-zaptel devices. The Zaptel compatible hardware supports pseudo-TDM
|
||||
conferencing and all call features through chan_zap, whereas non-zaptel
|
||||
compatible hardware may have different features.
|
||||
|
||||
DAHDI compatible hardware
|
||||
Zaptel compatible hardware
|
||||
==========================
|
||||
|
||||
-- Digium (Primary author of Asterisk)
|
||||
http://www.digium.com, http://store.digium.com
|
||||
http://www.digium.com, http://store.yahoo.com/asteriskpbx
|
||||
|
||||
* Wildcard X100P - Single FXO interface connects to Loopstart phone
|
||||
line
|
||||
|
||||
* Wildcard T400P (obsolete) - Quad T1 interface connects to four T1/PRI
|
||||
interfaces. Supports RBS and PRI voice and PPP, FR, and HDLC data.
|
||||
@@ -25,13 +28,16 @@ DAHDI compatible hardware
|
||||
* Wildcard E100P - Single E1 interface connects to a single E1/PRI (or PRA)
|
||||
interface. Supports PRA/PRI, EuroISDN voice and PPP, FR, HDLC data.
|
||||
|
||||
* Wildcard S100U - Single FXS interface connects to a standard analog
|
||||
telephone.
|
||||
|
||||
* Wildcard TDM400P - Quad Modular FXS interface connects to standard
|
||||
analog telephones.
|
||||
|
||||
* Wildcard TE410P - Quad T1/E1 switchable interface. Supports PRI and
|
||||
RBS signalling, as well as PPP, FR, and HDLC data modes.
|
||||
|
||||
Non-DAHDI compatible hardware
|
||||
Non-zaptel compatible hardware
|
||||
==============================
|
||||
|
||||
-- QuickNet, Inc.
|
||||
@@ -43,26 +49,16 @@ Non-DAHDI compatible hardware
|
||||
* Internet LineJack - Single FXS or FXO interface. Supports Linux
|
||||
telephony interface.
|
||||
|
||||
mISDN compatible hardware
|
||||
=========================
|
||||
mISDN homepage: http://www.isdn4linux.de/mISDN/
|
||||
|
||||
Any adapter with an mISDN driver should be compatible with
|
||||
chan_misdn. See misdn.txt for information.
|
||||
|
||||
-- beroNet
|
||||
http://www.beronet.com
|
||||
|
||||
* BN4S0 - 4 Port BRI card (TE/NT)
|
||||
|
||||
* BN8S0 - 8 Port BRI card (TE/NT)
|
||||
|
||||
* Billion Card - Single Port BRI card (TE (/NT with crossed cable) )
|
||||
|
||||
|
||||
Miscellaneous other interfaces
|
||||
==============================
|
||||
|
||||
-- ISDN4Linux
|
||||
http://www.isdn4linux.de/
|
||||
|
||||
* Any ISDN terminal adapter supported by isdn4linux should provide
|
||||
connectivity.
|
||||
|
||||
-- ALSA
|
||||
http://www.alsa-project.org
|
||||
|
||||
384
LICENSE
384
LICENSE
@@ -1,61 +1,341 @@
|
||||
Asterisk is distributed under the GNU General Public License version 2
|
||||
and is also available under alternative licenses negotiated directly
|
||||
with Digium, Inc. If you obtained Asterisk under the GPL, then the GPL
|
||||
applies to all loadable Asterisk modules used on your system as well,
|
||||
except as defined below. The GPL (version 2) is included in this
|
||||
source tree in the file COPYING.
|
||||
|
||||
This package also includes various components that are not part of
|
||||
Asterisk itself; these components are in the 'contrib' directory
|
||||
and its subdirectories. These components are also distributed under the
|
||||
GPL version 2 as well.
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Digium, Inc. (formerly Linux Support Services) holds copyright
|
||||
and/or sufficient licenses to all components of the Asterisk
|
||||
package, and therefore can grant, at its sole discretion, the ability
|
||||
for companies, individuals, or organizations to create proprietary or
|
||||
Open Source (even if not GPL) modules which may be dynamically linked at
|
||||
runtime with the portions of Asterisk which fall under our
|
||||
copyright/license umbrella, or are distributed under more flexible
|
||||
licenses than GPL.
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
If you wish to use our code in other GPL programs, don't worry --
|
||||
there is no requirement that you provide the same exception in your
|
||||
GPL'd products (although if you've written a module for Asterisk we
|
||||
would strongly encourage you to make the same exception that we do).
|
||||
Preamble
|
||||
|
||||
Specific permission is also granted to link Asterisk with OpenSSL, OpenH323
|
||||
and/or the UW IMAP Toolkit and distribute the resulting binary files.
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
In addition, Asterisk implements two management/control protocols: the
|
||||
Asterisk Manager Interface (AMI) and the Asterisk Gateway Interface
|
||||
(AGI). It is our belief that applications using these protocols to
|
||||
manage or control an Asterisk instance do not have to be licensed
|
||||
under the GPL or a compatible license, as we believe these protocols
|
||||
do not create a 'derivative work' as referred to in the GPL. However,
|
||||
should any court or other judiciary body find that these protocols do
|
||||
fall under the terms of the GPL, then we hereby grant you a license to
|
||||
use these protocols in combination with Asterisk in external
|
||||
applications licensed under any license you wish.
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
The 'Asterisk' name and logos are trademarks owned by Digium, Inc.,
|
||||
and use of them is subject to our trademark licensing policies. If you
|
||||
wish to use these trademarks for purposes other than simple
|
||||
redistribution of Asterisk source code obtained from Digium, you
|
||||
should contact our licensing department to determine the necessary
|
||||
steps you must take. For more information on this policy, please read
|
||||
http://www.digium.com/en/company/profile/trademarkpolicy.php
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
If you have any questions regarding our licensing policy, please
|
||||
contact us:
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
+1.877.344.4861 (via telephone in the USA)
|
||||
+1.256.428.6000 (via telephone outside the USA)
|
||||
+1.256.864.0464 (via FAX inside or outside the USA)
|
||||
IAX2/pbx.digium.com (via IAX2)
|
||||
licensing@digium.com (via email)
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Digium, Inc.
|
||||
445 Jan Davis Drive
|
||||
Huntsville, AL 35806
|
||||
USA
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
#
|
||||
# Asterisk -- A telephony toolkit for Linux.
|
||||
#
|
||||
# Makefile rules for subdirectories containing modules
|
||||
#
|
||||
# Copyright (C) 2006, Digium, Inc.
|
||||
#
|
||||
# Kevin P. Fleming <kpfleming@digium.com>
|
||||
#
|
||||
# This program is free software, distributed under the terms of
|
||||
# the GNU General Public License
|
||||
#
|
||||
|
||||
ifeq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
|
||||
_ASTCFLAGS+=${GC_CFLAGS}
|
||||
endif
|
||||
|
||||
ifneq ($(findstring STATIC_BUILD,$(MENUSELECT_CFLAGS)),)
|
||||
STATIC_BUILD=-static
|
||||
endif
|
||||
|
||||
include $(ASTTOPDIR)/Makefile.rules
|
||||
|
||||
comma:=,
|
||||
|
||||
$(addsuffix .o,$(C_MODS)): _ASTCFLAGS+=-DAST_MODULE=\"$*\" $(MENUSELECT_OPTS_$*:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_INCLUDE))
|
||||
$(addsuffix .oo,$(CC_MODS)): _ASTCFLAGS+=-DAST_MODULE=\"$*\" $(MENUSELECT_OPTS_$*:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_INCLUDE))
|
||||
|
||||
$(LOADABLE_MODS:%=%.so): _ASTCFLAGS+=-fPIC
|
||||
$(LOADABLE_MODS:%=%.so): LIBS+=$(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_LIB))
|
||||
$(LOADABLE_MODS:%=%.so): _ASTLDFLAGS+=$(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_LDFLAGS))
|
||||
|
||||
$(addsuffix .so,$(filter $(LOADABLE_MODS),$(C_MODS))): %.so: %.o
|
||||
$(addsuffix .so,$(filter $(LOADABLE_MODS),$(CC_MODS))): %.so: %.oo
|
||||
|
||||
modules.link: $(addsuffix .o,$(filter $(EMBEDDED_MODS),$(C_MODS)))
|
||||
modules.link: $(addsuffix .oo,$(filter $(EMBEDDED_MODS),$(CC_MODS)))
|
||||
|
||||
.PHONY: clean uninstall _all moduleinfo makeopts
|
||||
|
||||
ifneq ($(LOADABLE_MODS),)
|
||||
_all: $(LOADABLE_MODS:%=%.so)
|
||||
endif
|
||||
|
||||
ifneq ($(EMBEDDED_MODS),)
|
||||
_all: modules.link
|
||||
__embed_ldscript:
|
||||
@echo "../$(SUBDIR)/modules.link"
|
||||
__embed_ldflags:
|
||||
@echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(C_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LDFLAGS))"
|
||||
@echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(CC_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LDFLAGS))"
|
||||
__embed_libs:
|
||||
@echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(C_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LIB))"
|
||||
@echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(CC_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LIB))"
|
||||
else
|
||||
__embed_ldscript:
|
||||
__embed_ldflags:
|
||||
__embed_libs:
|
||||
endif
|
||||
|
||||
modules.link:
|
||||
@rm -f $@
|
||||
@for file in $(patsubst %,$(SUBDIR)/%,$(filter %.o,$^)); do echo "INPUT (../$${file})" >> $@; done
|
||||
@for file in $(patsubst %,$(SUBDIR)/%,$(filter-out %.o,$^)); do echo "INPUT (../$${file})" >> $@; done
|
||||
|
||||
clean::
|
||||
rm -f *.so *.o *.oo *.s *.i *.ii
|
||||
rm -f .*.d
|
||||
rm -f modules.link
|
||||
|
||||
install:: all
|
||||
for x in $(LOADABLE_MODS:%=%.so); do $(INSTALL) -m 755 $$x $(DESTDIR)$(MODULES_DIR) ; done
|
||||
|
||||
uninstall::
|
||||
|
||||
dist-clean::
|
||||
rm -f .*.moduleinfo .moduleinfo
|
||||
rm -f .*.makeopts .makeopts
|
||||
|
||||
.%.moduleinfo: %.c
|
||||
@echo "<member name=\"$*\" displayname=\"$(shell $(GREP) -e AST_MODULE_INFO $< | head -n 1 | cut -d '"' -f 2)\" remove_on_change=\"$(SUBDIR)/$*.o $(SUBDIR)/$*.so\">" > $@
|
||||
$(AWK) -f $(ASTTOPDIR)/build_tools/get_moduleinfo $< >> $@
|
||||
echo "</member>" >> $@
|
||||
|
||||
.%.moduleinfo: %.cc
|
||||
@echo "<member name=\"$*\" displayname=\"$(shell $(GREP) -e AST_MODULE_INFO $< | head -n 1 | cut -d '"' -f 2)\" remove_on_change=\"$(SUBDIR)/$*.oo $(SUBDIR)/$*.so\">" > $@
|
||||
$(AWK) -f $(ASTTOPDIR)/build_tools/get_moduleinfo $< >> $@
|
||||
echo "</member>" >> $@
|
||||
|
||||
.moduleinfo:: $(addsuffix .moduleinfo,$(addprefix .,$(ALL_C_MODS) $(ALL_CC_MODS)))
|
||||
@echo "<category name=\"MENUSELECT_$(MENUSELECT_CATEGORY)\" displayname=\"$(MENUSELECT_DESCRIPTION)\" remove_on_change=\"$(SUBDIR)/modules.link\">" > $@
|
||||
@cat $^ >> $@
|
||||
@echo "</category>" >> $@
|
||||
|
||||
moduleinfo: .moduleinfo
|
||||
@cat $<
|
||||
|
||||
.%.makeopts: %.c
|
||||
@$(AWK) -f $(ASTTOPDIR)/build_tools/get_makeopts $< > $@
|
||||
|
||||
.%.makeopts: %.cc
|
||||
@$(AWK) -f $(ASTTOPDIR)/build_tools/get_makeopts $< > $@
|
||||
|
||||
.makeopts:: $(addsuffix .makeopts,$(addprefix .,$(ALL_C_MODS) $(ALL_CC_MODS)))
|
||||
@cat $^ > $@
|
||||
|
||||
makeopts: .makeopts
|
||||
@cat $<
|
||||
|
||||
ifneq ($(wildcard .*.d),)
|
||||
include .*.d
|
||||
endif
|
||||
136
Makefile.rules
136
Makefile.rules
@@ -1,136 +0,0 @@
|
||||
#
|
||||
# Asterisk -- A telephony toolkit for Linux.
|
||||
#
|
||||
# Makefile rules
|
||||
#
|
||||
# Copyright (C) 2006-2008, Digium, Inc.
|
||||
#
|
||||
# Kevin P. Fleming <kpfleming@digium.com>
|
||||
#
|
||||
# This program is free software, distributed under the terms of
|
||||
# the GNU General Public License
|
||||
#
|
||||
|
||||
# Each command is preceded by a short comment on what to do.
|
||||
# Prefixing one or the other with @\# or @ or nothing makes the desired
|
||||
# behaviour. ECHO_PREFIX prefixes the comment, CMD_PREFIX prefixes the command.
|
||||
|
||||
-include $(ASTTOPDIR)/makeopts
|
||||
|
||||
.PHONY: dist-clean
|
||||
|
||||
# If 'make' decides to create intermediate files to satisfy a build requirement
|
||||
# (like producing a .i from a .c), we want to keep them, so tell make to keep
|
||||
# all intermediate files
|
||||
.SECONDARY:
|
||||
|
||||
# extra cflags to build dependencies. Recursively expanded.
|
||||
MAKE_DEPS=-MD -MT $@ -MF .$(subst /,_,$@).d -MP
|
||||
|
||||
ifeq ($(NOISY_BUILD),)
|
||||
ECHO_PREFIX=@
|
||||
CMD_PREFIX=@
|
||||
else
|
||||
ECHO_PREFIX=@\#
|
||||
CMD_PREFIX=
|
||||
endif
|
||||
|
||||
OPTIMIZE?=-O6
|
||||
ifneq ($(findstring darwin,$(OSARCH)),)
|
||||
ifeq ($(shell /usr/bin/sw_vers -productVersion | cut -c1-4),10.6)
|
||||
# Snow Leopard has an issue with this optimization flag on large files (like chan_sip)
|
||||
OPTIMIZE+=-fno-inline-functions
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(findstring DONT_OPTIMIZE,$(MENUSELECT_CFLAGS)),)
|
||||
_ASTCFLAGS+=$(OPTIMIZE)
|
||||
else
|
||||
_ASTCFLAGS+=-O0
|
||||
endif
|
||||
|
||||
# shortcuts for common combinations of flags; these must be recursively expanded so that
|
||||
# per-target settings will be applied
|
||||
CC_CFLAGS=$(PTHREAD_CFLAGS) $(_ASTCFLAGS) $(ASTCFLAGS)
|
||||
CXX_CFLAGS=$(PTHREAD_CFLAGS) $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(AST_DECLARATION_AFTER_STATEMENT),$(_ASTCFLAGS) $(ASTCFLAGS))
|
||||
|
||||
ifeq ($(GNU_LD),1)
|
||||
SO_SUPPRESS_SYMBOLS=-Wl,--version-script,$(if $(wildcard $(subst .so,.exports,$@)),$(subst .so,.exports,$@),$(ASTTOPDIR)/default.exports)
|
||||
endif
|
||||
|
||||
CC_LDFLAGS_SO=$(PTHREAD_CFLAGS) $(_ASTLDFLAGS) $(SOLINK) $(SO_SUPPRESS_SYMBOLS) $(ASTLDFLAGS)
|
||||
CXX_LDFLAGS_SO=$(PTHREAD_CFLAGS) $(_ASTLDFLAGS) $(SOLINK) $(SO_SUPPRESS_SYMBOLS) $(ASTLDFLAGS)
|
||||
CC_LIBS=$(PTHREAD_LIBS) $(LIBS)
|
||||
CXX_LIBS=$(PTHREAD_LIBS) $(LIBS)
|
||||
|
||||
# determine whether to double-compile so that the optimizer can report code path problems
|
||||
# this is only done when developer mode and DONT_OPTIMIZE are both enabled
|
||||
# in that case, we run the preprocessor to produce a .i or .ii file from the source
|
||||
# code, then compile once with optimizer enabled (and the output to /dev/null),
|
||||
# and if that doesn't fail then compile again with optimizer disabled
|
||||
ifeq ($(findstring DONT_OPTIMIZE,$(MENUSELECT_CFLAGS))$(AST_DEVMODE),DONT_OPTIMIZEyes)
|
||||
COMPILE_DOUBLE=yes
|
||||
endif
|
||||
|
||||
%.o: %.s
|
||||
$(ECHO_PREFIX) echo " [AS] $< -> $@"
|
||||
ifeq ($(COMPILE_DOUBLE),yes)
|
||||
$(CMD_PREFIX) $(CC) -o /dev/null -c $< $(OPTIMIZE) $(CC_CFLAGS)
|
||||
endif
|
||||
$(CMD_PREFIX) $(CC) -o $@ -c $< $(CC_CFLAGS)
|
||||
|
||||
%.o: %.i
|
||||
$(ECHO_PREFIX) echo " [CCi] $< -> $@"
|
||||
ifeq ($(COMPILE_DOUBLE),yes)
|
||||
$(CMD_PREFIX) $(CC) -o /dev/null -c $< $(OPTIMIZE) $(CC_CFLAGS)
|
||||
endif
|
||||
$(CMD_PREFIX) $(CC) -o $@ -c $< $(CC_CFLAGS)
|
||||
|
||||
ifneq ($(COMPILE_DOUBLE),yes)
|
||||
%.o: %.c
|
||||
$(ECHO_PREFIX) echo " [CC] $< -> $@"
|
||||
$(CMD_PREFIX) $(CC) -o $@ -c $< $(MAKE_DEPS) $(CC_CFLAGS)
|
||||
endif
|
||||
|
||||
%.i: %.c
|
||||
$(ECHO_PREFIX) echo " [CPP] $< -> $@"
|
||||
$(CMD_PREFIX) $(CC) -o $@ -E $< $(MAKE_DEPS) $(CC_CFLAGS)
|
||||
|
||||
%.oo: %.ii
|
||||
$(ECHO_PREFIX) echo " [CXXi] $< -> $@"
|
||||
ifeq ($(COMPILE_DOUBLE),yes)
|
||||
$(CMD_PREFIX) $(CXX) -o /dev/null -c $< $(OPTIMIZE) $(CXX_CFLAGS)
|
||||
endif
|
||||
$(CMD_PREFIX) $(CXX) -o $@ -c $< $(CXX_CFLAGS)
|
||||
|
||||
ifneq ($(COMPILE_DOUBLE),yes)
|
||||
%.oo: %.cc
|
||||
$(ECHO_PREFIX) echo " [CXX] $< -> $@"
|
||||
$(CMD_PREFIX) $(CXX) -o $@ -c $< $(MAKE_DEPS) $(CXX_CFLAGS)
|
||||
endif
|
||||
|
||||
%.ii: %.cc
|
||||
$(ECHO_PREFIX) echo " [CPP] $< -> $@"
|
||||
$(CMD_PREFIX) $(CXX) -o $@ -E $< $(MAKE_DEPS) $(CXX_CFLAGS)
|
||||
|
||||
%.c: %.y
|
||||
$(ECHO_PREFIX) echo " [BISON] $< -> $@"
|
||||
$(CMD_PREFIX) bison -o $@ -d --name-prefix=ast_yy $<
|
||||
|
||||
%.c: %.fl
|
||||
$(ECHO_PREFIX) echo " [FLEX] $< -> $@"
|
||||
$(CMD_PREFIX) flex -o $@ --full $<
|
||||
|
||||
%.so: %.o
|
||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CC) $(STATIC_BUILD) -o $@ $(CC_LDFLAGS_SO) $^ $(CC_LIBS)
|
||||
|
||||
%.so: %.oo
|
||||
$(ECHO_PREFIX) echo " [LDXX] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(CXX_LDFLAGS_SO) $^ $(CXX_LIBS)
|
||||
|
||||
%: %.o
|
||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(PTHREAD_CFLAGS) $(_ASTLDFLAGS) $^ $(CXX_LIBS) $(ASTLDFLAGS)
|
||||
|
||||
dist-clean:: clean
|
||||
185
README
185
README
@@ -1,17 +1,12 @@
|
||||
The Asterisk(R) Open Source PBX
|
||||
The Asterisk Open Source PBX
|
||||
by Mark Spencer <markster@digium.com>
|
||||
and the Asterisk.org developer community
|
||||
|
||||
Copyright (C) 2001-2009 Digium, Inc.
|
||||
and other copyright holders.
|
||||
Copyright (C) 2001-2004 Digium
|
||||
================================================================
|
||||
|
||||
* SECURITY
|
||||
It is imperative that you read and fully understand the contents of
|
||||
the security information file (doc/security.txt) before you attempt
|
||||
to configure and run an Asterisk server.
|
||||
the SECURITY file before you attempt to configure an Asterisk server.
|
||||
|
||||
* WHAT IS ASTERISK?
|
||||
* WHAT IS ASTERISK
|
||||
Asterisk is an Open Source PBX and telephony toolkit. It is, in a
|
||||
sense, middleware between Internet and telephony channels on the bottom,
|
||||
and Internet and telephony applications at the top. For more information
|
||||
@@ -19,137 +14,95 @@ on the project itself, please visit the Asterisk home page at:
|
||||
|
||||
http://www.asterisk.org
|
||||
|
||||
In addition you'll find lots of information compiled by the Asterisk
|
||||
In addition you'll find lot's of information compiled by the Asterisk
|
||||
community on this Wiki:
|
||||
|
||||
http://www.voip-info.org/wiki-Asterisk
|
||||
|
||||
There is a book on Asterisk published by O'Reilly under the
|
||||
Creative Commons License. It is available in book stores as well
|
||||
as in a downloadable version on the http://www.asteriskdocs.org
|
||||
web site.
|
||||
* LICENSING
|
||||
Asterisk is distributed under GNU General Public License. The GPL also
|
||||
must apply to all loadable modules as well, except as defined below.
|
||||
|
||||
* SUPPORTED OPERATING SYSTEMS
|
||||
Digium, Inc. (formerly Linux Support Services) retains copyright to all
|
||||
of the core Asterisk system, and therefore can grant, at its sole discretion,
|
||||
the ability for companies, individuals, or organizations to create proprietary
|
||||
or Open Source (but non-GPL'd) modules which may be dynamically linked at
|
||||
runtime with the portions of Asterisk which fall under our copyright
|
||||
umbrella, or are distributed under more flexible licenses than GPL.
|
||||
|
||||
|
||||
If you wish to use our code in other GPL programs, don't worry -- there
|
||||
is no requirement that you provide the same exemption in your GPL'd
|
||||
products (although if you've written a module for Asterisk we would
|
||||
strongly encourage you to make the same exemption that we do).
|
||||
|
||||
Specific permission is also granted to OpenSSL and OpenH323 to link to
|
||||
Asterisk.
|
||||
|
||||
If you have any questions, whatsoever, regarding our licensing policy,
|
||||
please contact us.
|
||||
|
||||
Modules that are GPL-licensed and not available under Digium's
|
||||
licensing scheme are added to the Asterisk-addons CVS module.
|
||||
|
||||
* REQUIRED COMPONENTS
|
||||
|
||||
== Linux ==
|
||||
The Asterisk Open Source PBX is developed and tested primarily on the
|
||||
GNU/Linux operating system, and is supported on every major GNU/Linux
|
||||
distribution.
|
||||
Currently, the Asterisk Open Source PBX is only known to run on the
|
||||
Linux OS, although it may be portable to other UNIX-like operating systems
|
||||
(like FreeBSD) as well.
|
||||
|
||||
== Others ==
|
||||
Asterisk has also been 'ported' and reportedly runs properly on other
|
||||
operating systems as well, including Sun Solaris, Apple's Mac OS X, and
|
||||
the BSD variants.
|
||||
|
||||
* GETTING STARTED
|
||||
|
||||
First, be sure you've got supported hardware (but note that you don't need
|
||||
ANY special hardware, not even a soundcard) to install and run Asterisk.
|
||||
|
||||
Supported telephony hardware includes:
|
||||
First, be sure you've got supported hardware (but note that you don't need ANY hardware, not even a soundcard) to install and run Asterisk. Supported are:
|
||||
|
||||
* All Wildcard (tm) products from Digium (www.digium.com)
|
||||
* QuickNet Internet PhoneJack and LineJack (http://www.quicknet.net)
|
||||
* any full duplex sound card supported by ALSA or OSS
|
||||
* any ISDN card supported by mISDN on Linux (BRI)
|
||||
* The Xorcom AstriBank channel bank
|
||||
* VoiceTronix OpenLine products
|
||||
* Full Duplex Sound Card supported by Linux
|
||||
* Adtran Atlas 800 Plus
|
||||
* ISDN4Linux compatible ISDN card
|
||||
* Tormenta Dual T1 card (www.bsdtelephony.com.mx)
|
||||
|
||||
The are several drivers for ISDN BRI cards available from third party sources.
|
||||
Check the voip-info.org wiki for more information on chan_capi and
|
||||
zaphfc.
|
||||
Hint: CAPI compatible ISDN cards can be run using the add-on channel chan_capi.
|
||||
|
||||
* UPGRADING FROM AN EARLIER VERSION
|
||||
So let's proceed:
|
||||
|
||||
If you are updating from a previous version of Asterisk, make sure you
|
||||
read the UPGRADE.txt file in the source directory. There are some files
|
||||
and configuration options that you will have to change, even though we
|
||||
made every effort possible to maintain backwards compatibility.
|
||||
1) Run "make"
|
||||
2) Run "make install"
|
||||
|
||||
In order to discover new features to use, please check the configuration
|
||||
examples in the /configs directory of the source code distribution.
|
||||
To discover the major new features of Asterisk 1.2, please visit
|
||||
http://edvina.net/asterisk1-2/
|
||||
|
||||
* NEW INSTALLATIONS
|
||||
|
||||
Ensure that your system contains a compatible compiler and development
|
||||
libraries. Asterisk requires either the GNU Compiler Collection (GCC) version
|
||||
3.0 or higher, or a compiler that supports the C99 specification and some of
|
||||
the gcc language extensions. In addition, your system needs to have the C
|
||||
library headers available, and the headers and libraries for OpenSSL,
|
||||
ncurses and zlib.
|
||||
On many distributions, these files are installed by packages with names like
|
||||
'glibc-devel', 'ncurses-devel', 'openssl-devel' and 'zlib-devel' or similar.
|
||||
|
||||
So let's proceed:
|
||||
|
||||
1) Read this README file.
|
||||
|
||||
There are more documents than this one in the doc/ directory.
|
||||
You may also want to check the configuration files that contain
|
||||
examples and reference guides. They are all in the configs/
|
||||
directory.
|
||||
|
||||
2) Run "./configure"
|
||||
|
||||
Execute the configure script to guess values for system-dependent
|
||||
variables used during compilation.
|
||||
|
||||
3) Run "make menuselect" [optional]
|
||||
|
||||
This is needed if you want to select the modules that will be
|
||||
compiled and to check modules dependencies.
|
||||
|
||||
4) Run "make"
|
||||
|
||||
Assuming the build completes successfully:
|
||||
|
||||
5) Run "make install"
|
||||
|
||||
Each time you update or checkout from the repository, you are strongly
|
||||
encouraged to ensure all previous object files are removed to avoid internal
|
||||
inconsistency in Asterisk. Normally, this is automatically done with
|
||||
the presence of the file .cleancount, which increments each time a 'make clean'
|
||||
is required, and the file .lastclean, which contains the last .cleancount used.
|
||||
|
||||
If this is your first time working with Asterisk, you may wish to install
|
||||
If this is your first time working with Asterisk, you may wish to install
|
||||
the sample PBX, with demonstration extensions, etc. If so, run:
|
||||
|
||||
6) "make samples"
|
||||
"make samples"
|
||||
|
||||
Doing so will overwrite any existing config files you have.
|
||||
Doing so will overwrite any existing config files you have. If you are lacking a soundcard you won't be able to use the DIAL command on the console, though.
|
||||
|
||||
Finally, you can launch Asterisk in the foreground mode (not a daemon)
|
||||
with:
|
||||
Finally, you can launch Asterisk with:
|
||||
|
||||
# asterisk -vvvc
|
||||
./asterisk -vvvc
|
||||
|
||||
You'll see a bunch of verbose messages fly by your screen as Asterisk
|
||||
You'll see a bunch of verbose messages fly by your screen as Asterisk
|
||||
initializes (that's the "very very verbose" mode). When it's ready, if
|
||||
you specified the "c" then you'll get a command line console, that looks
|
||||
like this:
|
||||
|
||||
*CLI>
|
||||
|
||||
You can type "help" at any time to get help with the system. For help
|
||||
You can type "help" at any time to get help with the system. For help
|
||||
with a specific command, type "help <command>". To start the PBX using
|
||||
your sound card, you can type "dial" to dial the PBX. Then you can use
|
||||
"answer", "hangup", and "dial" to simulate the actions of a telephone.
|
||||
Remember that if you don't have a full duplex sound card (and Asterisk
|
||||
will tell you somewhere in its verbose messages if you do/don't) then it
|
||||
Remember that if you don't have a full duplex sound card (And asterisk
|
||||
will tell you somewhere in its verbose messages if you do/don't) than it
|
||||
won't work right (not yet).
|
||||
|
||||
"man asterisk" at the Unix/Linux command prompt will give you detailed
|
||||
information on how to start and stop Asterisk, as well as all the command
|
||||
line options for starting Asterisk.
|
||||
|
||||
Feel free to look over the configuration files in /etc/asterisk, where
|
||||
Feel free to look over the configuration files in /etc/asterisk, where
|
||||
you'll find a lot of information about what you can do with Asterisk.
|
||||
|
||||
* ABOUT CONFIGURATION FILES
|
||||
|
||||
All Asterisk configuration files share a common format. Comments are
|
||||
All Asterisk configuration files share a common format. Comments are
|
||||
delimited by ';' (since '#' of course, being a DTMF digit, may occur in
|
||||
many places). A configuration file is divided into sections whose names
|
||||
appear in []'s. Each section typically contains two types of statements,
|
||||
@@ -158,12 +111,12 @@ parameters'. Internally the use of '=' and '=>' is exactly the same, so
|
||||
they're used only to help make the configuration file easier to
|
||||
understand, and do not affect how it is actually parsed.
|
||||
|
||||
Entries of the form 'variable=value' set the value of some parameter in
|
||||
asterisk. For example, in chan_dahdi.conf, one might specify:
|
||||
Entries of the form 'variable=value' set the value of some parameter in
|
||||
asterisk. For example, in tormenta.conf, one might specify:
|
||||
|
||||
switchtype=national
|
||||
|
||||
in order to indicate to Asterisk that the switch they are connecting to is
|
||||
In order to indicate to Asterisk that the switch they are connecting to is
|
||||
of the type "national". In general, the parameter will apply to
|
||||
instantiations which occur below its specification. For example, if the
|
||||
configuration file read:
|
||||
@@ -174,18 +127,18 @@ configuration file read:
|
||||
switchtype = dms100
|
||||
channel => 25-47
|
||||
|
||||
the "national" switchtype would be applied to channels one through
|
||||
Then, the "national" switchtype would be applied to channels one through
|
||||
four and channels 10 through 12, whereas the "dms100" switchtype would
|
||||
apply to channels 25 through 47.
|
||||
|
||||
The "object => parameters" instantiates an object with the given
|
||||
The "object => parameters" instantiates an object with the given
|
||||
parameters. For example, the line "channel => 25-47" creates objects for
|
||||
the channels 25 through 47 of the card, obtaining the settings
|
||||
the channels 25 through 47 of the tormenta card, obtaining the settings
|
||||
from the variables specified above.
|
||||
|
||||
* SPECIAL NOTE ON TIME
|
||||
|
||||
Those using SIP phones should be aware that Asterisk is sensitive to
|
||||
Those using SIP phones should be aware the Asterisk is sensitive to
|
||||
large jumps in time. Manually changing the system time using date(1)
|
||||
(or other similar commands) may cause SIP registrations and other
|
||||
internal processes to fail. If your system cannot keep accurate time
|
||||
@@ -203,7 +156,7 @@ apparent. The use of daylight savings time in a Linux system is
|
||||
purely a user interface issue and does not affect the operation of the
|
||||
Linux kernel or Asterisk. The system clock on Linux kernels operates
|
||||
on UTC. UTC does not use daylight savings time.
|
||||
|
||||
|
||||
Also note that this issue is separate from the clocking of TDM
|
||||
channels, and is known to at least affect SIP registrations.
|
||||
|
||||
@@ -243,20 +196,18 @@ these changes to take effect.
|
||||
above you can try adding the command "ulimit -n 8192" to the script
|
||||
that starts Asterisk.
|
||||
|
||||
|
||||
* MORE INFORMATION
|
||||
|
||||
See the doc directory for more documentation on various features. Again,
|
||||
please read all the configuration samples that include documentation on
|
||||
the configuration options.
|
||||
See the doc directory for more documentation.
|
||||
|
||||
Finally, you may wish to visit the web site and join the mailing list if
|
||||
Finally, you may wish to visit the web site and join the mailing list if
|
||||
you're interested in getting more information.
|
||||
|
||||
http://www.asterisk.org/support
|
||||
http://www.asterisk.org/index.php?menu=support
|
||||
|
||||
Welcome to the growing worldwide community of Asterisk users!
|
||||
Welcome to the growing worldwide community of Asterisk users!
|
||||
|
||||
Mark Spencer
|
||||
|
||||
----
|
||||
Asterisk is a trademark belonging to Digium, inc
|
||||
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
==================
|
||||
| Best Practices |
|
||||
==================
|
||||
|
||||
The purpose of this document is to define best practices when working with
|
||||
Asterisk in order to minimize possible security breaches and to provide tried
|
||||
examples in field deployments. This is a living document and is subject to
|
||||
change over time as best practices are defined.
|
||||
|
||||
--------
|
||||
Sections
|
||||
--------
|
||||
|
||||
* Filtering Data:
|
||||
How to protect yourself from redial attacks
|
||||
|
||||
* Proper Device Naming:
|
||||
Why to not use numbered extensions for devices
|
||||
|
||||
* Secure Passwords:
|
||||
Secure passwords limit your risk to brute force attacks
|
||||
|
||||
* Reducing Pattern Match Typos:
|
||||
Using the 'same' prefix, or using Goto()
|
||||
|
||||
----------------
|
||||
Additional Links
|
||||
----------------
|
||||
|
||||
Additional links that contain useful information about best practices or
|
||||
security are listed below.
|
||||
|
||||
* Seven Steps to Better SIP Security:
|
||||
http://blogs.digium.com/2009/03/28/sip-security/
|
||||
|
||||
* Asterisk VoIP Security (webinar):
|
||||
http://www.asterisk.org/security/webinar/
|
||||
|
||||
|
||||
==============
|
||||
Filtering Data
|
||||
==============
|
||||
|
||||
In the Asterisk dialplan, several channel variables contain data potentially
|
||||
supplied by outside sources. This could lead to a potential security concern
|
||||
where those outside sources may send cleverly crafted strings of data which
|
||||
could be utilized, e.g. to place calls to unexpected locations.
|
||||
|
||||
An example of this can be found in the use of pattern matching and the ${EXTEN}
|
||||
channel variable. Note that ${EXTEN} is not the only system created channel
|
||||
variable, so it is important to be aware of where the data you're using is
|
||||
coming from.
|
||||
|
||||
For example, this common dialplan takes 2 or more characters of data, starting
|
||||
with a number 0-9, and then accepts any additional information supplied by the
|
||||
request.
|
||||
|
||||
[NOTE: We use SIP in this example, but is not limited to SIP only; protocols
|
||||
such as Jabber/XMPP or IAX2 are also susceptible to the same sort of
|
||||
injection problem.]
|
||||
|
||||
|
||||
[incoming]
|
||||
exten => _X.,1,Verbose(2,Incoming call to extension ${EXTEN})
|
||||
exten => _X.,n,Dial(SIP/${EXTEN})
|
||||
exten => _X.,n,Hangup()
|
||||
|
||||
This dialplan may be utilized to accept calls to extensions, which then dial a
|
||||
numbered device name configured in one of the channel configuration files (such
|
||||
as sip.conf, iax.conf, etc...) (see the section Proper Device Naming for more
|
||||
information on why this approach is flawed).
|
||||
|
||||
The example we've given above looks harmless enough until you take into
|
||||
consideration that several channel technologies accept characters that could
|
||||
be utilized in a clever attack. For example, instead of just sending a request
|
||||
to dial extension 500 (which in our example above would create the string
|
||||
SIP/500 and is then used by the Dial() application to place a call), someone
|
||||
could potentially send a string like "500&SIP/itsp/14165551212".
|
||||
|
||||
The string "500&SIP/itsp/14165551212" would then be contained within the
|
||||
${EXTEN} channel variable, which is then utilized by the Dial() application in
|
||||
our example, thereby giving you the dialplan line of:
|
||||
|
||||
exten => _X.,n,Dial(SIP/500&SIP/itsp/14165551212)
|
||||
|
||||
Our example above has now provided someone with a method to place calls out of
|
||||
your ITSP in a place where you didn't expect to allow it. There are a couple of
|
||||
ways in which you can mitigate this impact: stricter pattern matching, or using
|
||||
the FILTER() dialplan function.
|
||||
|
||||
Strict Pattern Matching
|
||||
-----------------------
|
||||
|
||||
The simple way to mitigate this problem is with a strict pattern match that does
|
||||
not utilize the period (.) or bang (!) characters to match on one-or-more
|
||||
characters or zero-or-more characters (respectively). To fine tune our example
|
||||
to only accept three digit extensions, we could change our pattern match to
|
||||
be:
|
||||
|
||||
exten => _XXX,n,Dial(SIP/${EXTEN})
|
||||
|
||||
In this way, we have minimized our impact because we're not allowing anything
|
||||
other than the numbers zero through nine. But in some cases we really do need to
|
||||
handle variable pattern matches, such as when dialing international numbers
|
||||
or when we want to handle something like a SIP URI. In this case, we'll need to
|
||||
utilize the FILTER() dialplan function.
|
||||
|
||||
Using FILTER()
|
||||
--------------
|
||||
|
||||
The FILTER() dialplan function is used to filter strings by only allowing
|
||||
characters that you have specified. This is a perfect candidate for controlling
|
||||
which characters you want to pass to the Dial() application, or any other
|
||||
application which will contain dynamic information passed to Asterisk from an
|
||||
external source. Lets take a look at how we can use FILTER() to control what
|
||||
data we allow.
|
||||
|
||||
Using our previous example to accept any string length of 2 or more characters,
|
||||
starting with a number of zero through nine, we can use FILTER() to limit what
|
||||
we will accept to just numbers. Our example would then change to something like:
|
||||
|
||||
[incoming]
|
||||
exten => _X.,1,Verbose(2,Incoming call to extension ${EXTEN})
|
||||
exten => _X.,n,Dial(SIP/${FILTER(0123456789,${EXTEN})})
|
||||
exten => _X.,n,Hangup()
|
||||
|
||||
Note how we've wrapped the ${EXTEN} channel variable with the FILTER() function
|
||||
which will then only pass back characters that fit into the numerical range that
|
||||
we've defined.
|
||||
|
||||
Alternatively, if we didn't want to utilize the FILTER() function within the
|
||||
Dial() application directly, we could save the value to a channel variable,
|
||||
which has a side effect of being usable in other locations of your dialplan if
|
||||
necessary, and to handle error checking in a separate location.
|
||||
|
||||
[incoming]
|
||||
exten => _X.,1,Verbose(2,Incoming call to extension ${EXTEN})
|
||||
exten => _X.,n,Set(SAFE_EXTEN=${FILTER(0123456789,${EXTEN})})
|
||||
exten => _X.,n,Dial(SIP/${SAFE_EXTEN})
|
||||
exten => _X.,n,Hangup()
|
||||
|
||||
Now we can use the ${SAFE_EXTEN} channel variable anywhere throughout the rest
|
||||
of our dialplan, knowing we've already filtered it. We could also perform an
|
||||
error check to verify that what we've received in ${EXTEN} also matches the data
|
||||
passed back by FILTER(), and to fail the call if things do not match.
|
||||
|
||||
[incoming]
|
||||
exten => _X.,1,Verbose(2,Incoming call to extension ${EXTEN})
|
||||
exten => _X.,n,Set(SAFE_EXTEN=${FILTER(0123456789,${EXTEN})})
|
||||
exten => _X.,n,GotoIf($[${EXTEN} != ${SAFE_EXTEN}]?error,1)
|
||||
exten => _X.,n,Dial(SIP/${SAFE_EXTEN})
|
||||
exten => _X.,n,Hangup()
|
||||
|
||||
exten => error,1,Verbose(2,Values of EXTEN and SAFE_EXTEN did not match.)
|
||||
exten => error,n,Verbose(2,EXTEN: "${EXTEN}" -- SAFE_EXTEN: "${SAFE_EXTEN}")
|
||||
exten => error,n,Playback(silence/1&invalid)
|
||||
exten => error,n,Hangup()
|
||||
|
||||
Another example would be using FILTER() to control the characters we accept when
|
||||
we're expecting to get a SIP URI for dialing.
|
||||
|
||||
[incoming]
|
||||
exten => _[0-9a-zA-Z].,1,Verbose(2,Incoming call to extension ${EXTEN})
|
||||
exten => _[0-9a-zA-Z].,n,Dial(SIP/${FILTER(.@0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,${EXTEN})
|
||||
exten => _[0-9a-zA-Z].,n,Hangup()
|
||||
|
||||
Of course the FILTER() function doesn't check the formatting of the incoming
|
||||
request. There is also the REGEX() dialplan function which can be used to
|
||||
determine if the string passed to it matches the regular expression you've
|
||||
created, and to take proper action on whether it matches or not. The creation of
|
||||
regular expressions is left as an exercise for the reader.
|
||||
|
||||
More information about the FILTER() and REGEX() dialplan functions can be found
|
||||
by typing "core show function FILTER" and "core show function REGEX" from your
|
||||
Asterisk console.
|
||||
|
||||
|
||||
====================
|
||||
Proper Device Naming
|
||||
====================
|
||||
|
||||
In Asterisk, the concept of an extension number being tied to a specific device
|
||||
does not exist. Asterisk is aware of devices it can call or receive calls from,
|
||||
and how you define in your dialplan how to reach those devices is up to you.
|
||||
|
||||
Because it has become common practice to think of a specific device as having an
|
||||
extension number associated with it, it only becomes natural to think about
|
||||
naming your devices the same as the extension number you're providing it. But
|
||||
by doing this, you're limiting the powerful concept of separating user from
|
||||
extensions, and extensions from devices.
|
||||
|
||||
It can also be a security hazard to name your devices with a number, as this can
|
||||
open you up to brute force attacks. Many of the current exploits deal with
|
||||
device configurations which utilize a number, and even worse, a password that
|
||||
matches the devices name. For example, take a look at this poorly created device
|
||||
in sip.conf:
|
||||
|
||||
[1000]
|
||||
type=friend
|
||||
context=international_dialing
|
||||
secret=1000
|
||||
|
||||
As implied by the context, we've permitted a device named 1000 with a password
|
||||
of 1000 to place calls internationally. If your PBX system is accessible via
|
||||
the internet, then your system will be vulnerable to expensive international
|
||||
calls. Even if your system is not accessible via the internet, people within
|
||||
your organization could get access to dialing rules you'd prefer to reserve only
|
||||
for certain people.
|
||||
|
||||
A more secure example for the device would be to use something like the MAC
|
||||
address of the device, along with a strong password (see the section Secure
|
||||
Passwords). The following example would be more secure:
|
||||
|
||||
[0004f2040001]
|
||||
type=friend
|
||||
context=international_dialing
|
||||
secret=aE3%B8*$jk^G
|
||||
|
||||
Then in your dialplan, you would reference the device via the MAC address of the
|
||||
device (or if using the softphone, a MAC address of a network interface on the
|
||||
computer).
|
||||
|
||||
Also note that you should NOT use this password, as it will likely be one of the
|
||||
first ones added to the dictionary for brute force attacks.
|
||||
|
||||
|
||||
================
|
||||
Secure Passwords
|
||||
================
|
||||
|
||||
Secure passwords are necessary in many (if not all) environments, and Asterisk
|
||||
is certainly no exception, especially when it comes to expensive long distance
|
||||
calls that could potentially cost your company hundreds or thousands of dollars
|
||||
on an expensive monthly phone bill, with little to no recourse to fight the
|
||||
charges.
|
||||
|
||||
Whenever you are positioned to add a password to your system, whether that is
|
||||
for a device configuration, a database connection, or any other secure
|
||||
connection, be sure to use a secure password. A good example of a secure
|
||||
password would be something like:
|
||||
|
||||
aE3%B8*$jk^G
|
||||
|
||||
Our password also contains 12 characters with a mixture of upper and
|
||||
lower case characters, numbers, and symbols. Because these passwords are likely
|
||||
to only be entered once, or loaded via a configuration file, there is
|
||||
no need to create simple passwords, even in testing. Some of the holes found in
|
||||
production systems used for exploitations involve finding the one test extension
|
||||
that contains a weak password that was forgotten prior to putting a system into
|
||||
production.
|
||||
|
||||
Using a web search you can find several online password generators such as
|
||||
http://www.strongpasswordgenerator.com or there are several scripts that can be
|
||||
used to generate a strong password.
|
||||
|
||||
|
||||
============================
|
||||
Reducing Pattern Match Typos
|
||||
============================
|
||||
|
||||
As of Asterisk 1.6.2, a new method for reducing the number of complex pattern
|
||||
matches you need to enter, which can reduce typos in your dialplan, has been
|
||||
implemented. Traditionally, a dialplan with a complex pattern match would look
|
||||
something like:
|
||||
|
||||
exten => _[3-5]XXX,1,Verbose(Incoming call to ${EXTEN})
|
||||
exten => _[3-5]XXX,n,Set(DEVICE=${DB(device/mac_address/${EXTEN})})
|
||||
exten => _[3-5]XXX,n,Set(TECHNOLOGY=${DB(device/technology/${EXTEN})})
|
||||
exten => _[3-5]XXX,n,GotoIf($[${ISNULL(${TECHNOLOGY})} | ${ISNULL(${DEVICE})}]?error,1)
|
||||
exten => _[3-5]XXX,n,Dial(${TECHNOLOGY}/${DEVICE},${GLOBAL(TIMEOUT)})
|
||||
exten => _[3-5]XXX,n,Set(vmFlag=${IF($[${DIALSTATUS} = BUSY]?b:u)})
|
||||
exten => _[3-5]XXX,n,Voicemail(${EXTEN}@${GLOBAL(VOICEMAIL_CONTEXT)},${vmFlag})
|
||||
exten => _[3-5]XXX,n,Hangup()
|
||||
|
||||
exten => error,1,Verbose(2,Unable to lookup technology or device for extension)
|
||||
exten => error,n,Playback(silence/1&num-not-in-db)
|
||||
exten => error,n,Hangup()
|
||||
|
||||
Of course there exists the possibility for a typo when retyping the pattern
|
||||
match _[3-5]XXX which will match on extensions 3000 through 5999. We can
|
||||
minimize this error by utilizing the same => prefix on all lines beyond the
|
||||
first one. Our same dialplan with using same => would look like the following:
|
||||
|
||||
exten => _[3-5]XXX,1,Verbose(Incoming call to ${EXTEN})
|
||||
same => n,Set(DEVICE=${DB(device/mac_address/${EXTEN})})
|
||||
same => n,Set(TECHNOLOGY=${DB(device/technology/${EXTEN})})
|
||||
same => n,GotoIf($[${ISNULL(${TECHNOLOGY})} | ${ISNULL(${DEVICE})}]?error,1)
|
||||
same => n,Dial(${TECHNOLOGY}/${DEVICE},${GLOBAL(TIMEOUT)})
|
||||
same => n,Set(vmFlag=${IF($[${DIALSTATUS} = BUSY]?b:u)})
|
||||
same => n,Voicemail(${EXTEN}@${GLOBAL(VOICEMAIL_CONTEXT)},${vmFlag})
|
||||
same => n,Hangup()
|
||||
|
||||
exten => error,1,Verbose(2,Unable to lookup technology or device for extension)
|
||||
same => n,Playback(silence/1&num-not-in-db)
|
||||
same => n,Hangup()
|
||||
22
README.opsound
Normal file
22
README.opsound
Normal file
@@ -0,0 +1,22 @@
|
||||
About Hold Music
|
||||
================
|
||||
These files were obtained from http://opsound.org, where the authors placed them
|
||||
under the Creative Commons Attribution-Share Alike 2.5 license, a copy of which
|
||||
may be found at http://creativecommons.org.
|
||||
|
||||
Credits
|
||||
================
|
||||
macroform-cold_day - Paul Shuler (Macroform)
|
||||
paulshuler@gmail.com - http://macroform.bandcamp.com/
|
||||
|
||||
macroform-robot_dity - Paul Shuler (Macroform)
|
||||
paulshuler@gmail.com - http://macroform.bandcamp.com/
|
||||
|
||||
macroform-the_simplicity - Paul Shuler (Macroform)
|
||||
paulshuler@gmail.com - http://macroform.bandcamp.com/
|
||||
|
||||
manolo_camp-morning_coffee - Manolo Camp
|
||||
beatbastard@gmx.net - http://ccmixter.org/people/ManoloCamp
|
||||
|
||||
reno_project-system - Reno Project
|
||||
renoproject@hotmail.com - http://www.jamendo.com/en/album/23661
|
||||
41
SECURITY
Normal file
41
SECURITY
Normal file
@@ -0,0 +1,41 @@
|
||||
==== Security Notes with Asterisk ====
|
||||
|
||||
PLEASE READ THE FOLLOWING IMPORTANT SECURITY RELATED INFORMATION.
|
||||
IMPROPER CONFIGURATION OF ASTERISK COULD ALLOW UNAUTHORIZED USE OF YOUR
|
||||
FACILITIES, POTENTIALLY INCURRING SUBSTANTIAL CHARGES.
|
||||
|
||||
First and foremost remember this:
|
||||
|
||||
USE THE EXTENSION CONTEXTS TO ISOLATE OUTGOING OR TOLL SERVICES FROM ANY
|
||||
INCOMING CONNECTIONS.
|
||||
|
||||
You should consider that if any channel, incoming line, etc can enter an
|
||||
extension context that it has the capability of accessing any extension
|
||||
within that context.
|
||||
|
||||
Therefore, you should NOT allow access to outgoing or toll services in
|
||||
contexts that are accessible (especially without a password) from incoming
|
||||
channels, be they IAX channels, FX or other trunks, or even untrusted
|
||||
stations within you network. In particular, never ever put outgoing toll
|
||||
services in the "default" context. To make things easier, you can include
|
||||
the "default" context within other private contexts by using:
|
||||
|
||||
include => default
|
||||
|
||||
in the appropriate section. A well designed PBX might look like this:
|
||||
|
||||
[longdistance]
|
||||
exten => _91NXXNXXXXXX,1,Dial(Zap/g2/${EXTEN:1})
|
||||
include => local
|
||||
|
||||
[local]
|
||||
exten => _9NXXNXXX,1,Dial(Zap/g2/${EXTEN:1})
|
||||
include => default
|
||||
|
||||
[default]
|
||||
exten => 6123,Dial(Zap/1)
|
||||
|
||||
|
||||
DON'T FORGET TO TAKE THE DEMO CONTEXT OUT OF YOUR DEFAULT CONTEXT. There
|
||||
isn't really a security reason, it just will keep people from wanting to
|
||||
play with your asterisk setup remotely.
|
||||
210
UPGRADE-1.2.txt
210
UPGRADE-1.2.txt
@@ -1,210 +0,0 @@
|
||||
=========================================================
|
||||
=== Information for upgrading from Asterisk 1.0 to 1.2
|
||||
===
|
||||
===
|
||||
=== UPGRADE-1.2.txt -- Upgrade info for 1.0 to 1.2
|
||||
=== UPGRADE.txt -- Upgrade info for 1.2 to 1.4
|
||||
=========================================================
|
||||
|
||||
Compiling:
|
||||
|
||||
* The Asterisk 1.2 source code now uses C language features
|
||||
supported only by 'modern' C compilers. Generally, this means GCC
|
||||
version 3.0 or higher, although some GCC 2.96 releases will also
|
||||
work. Some non-GCC compilers that support C99 and the common GCC
|
||||
extensions (including anonymous structures and unions) will also
|
||||
work. All releases of GCC 2.95 do _not_ have the requisite feature
|
||||
support; systems using that compiler will need to be upgraded to
|
||||
a more recent compiler release.
|
||||
|
||||
Dialplan Expressions:
|
||||
|
||||
* The dialplan expression parser (which handles $[ ... ] constructs)
|
||||
has gone through a major upgrade, but has one incompatible change:
|
||||
spaces are no longer required around expression operators, including
|
||||
string comparisons. However, you can now use quoting to keep strings
|
||||
together for comparison. For more details, please read the
|
||||
doc/README.variables file, and check over your dialplan for possible
|
||||
problems.
|
||||
|
||||
Agents:
|
||||
|
||||
* The default for ackcall has been changed to "no" instead of "yes"
|
||||
because of a bug which caused the "yes" behavior to generally act like
|
||||
"no". You may need to adjust the value if your agents behave
|
||||
differently than you expect with respect to acknowledgement.
|
||||
|
||||
* The AgentCallBackLogin application now requires a second '|' before
|
||||
specifying an extension@context. This is to distinguish the options
|
||||
string from the extension, so that they do not conflict. See
|
||||
'show application AgentCallbackLogin' for more details.
|
||||
|
||||
Parking:
|
||||
|
||||
* Parking behavior has changed slightly; when a parked call times out,
|
||||
Asterisk will attempt to deliver the call back to the extension that
|
||||
parked it, rather than the 's' extension. If that extension is busy
|
||||
or unavailable, the parked call will be lost.
|
||||
|
||||
Dialing:
|
||||
|
||||
* The Caller*ID of the outbound leg is now the extension that was
|
||||
called, rather than the Caller*ID of the inbound leg of the call. The
|
||||
"o" flag for Dial can be used to restore the original behavior if
|
||||
desired. Note that if you are looking for the originating callerid
|
||||
from the manager event, there is a new manager event "Dial" which
|
||||
provides the source and destination channels and callerid.
|
||||
|
||||
IAX:
|
||||
|
||||
* The naming convention for IAX channels has changed in two ways:
|
||||
1. The call number follows a "-" rather than a "/" character.
|
||||
2. The name of the channel has been simplified to IAX2/peer-callno,
|
||||
rather than IAX2/peer@peer-callno or even IAX2/peer@peer/callno.
|
||||
|
||||
SIP:
|
||||
|
||||
* The global option "port" in 1.0.X that is used to set which port to
|
||||
bind to has been changed to "bindport" to be more consistent with
|
||||
the other channel drivers and to avoid confusion with the "port"
|
||||
option for users/peers.
|
||||
|
||||
* The "Registry" event now uses "Username" rather than "User" for
|
||||
consistency with IAX.
|
||||
|
||||
Applications:
|
||||
|
||||
* With the addition of dialplan functions (which operate similarly
|
||||
to variables), the SetVar application has been renamed to Set.
|
||||
|
||||
* The CallerPres application has been removed. Use SetCallerPres
|
||||
instead. It accepts both numeric and symbolic names.
|
||||
|
||||
* The applications GetGroupCount, GetGroupMatchCount, SetGroup, and
|
||||
CheckGroup have been deprecated in favor of functions. Here is a
|
||||
table of their replacements:
|
||||
|
||||
GetGroupCount([groupname][@category] GROUP_COUNT([groupname][@category]) Set(GROUPCOUNT=${GROUP_COUNT()})
|
||||
GroupMatchCount(groupmatch[@category]) GROUP_MATCH_COUNT(groupmatch[@category]) Set(GROUPCOUNT=${GROUP_MATCH_COUNT(SIP/.*)})
|
||||
SetGroup(groupname[@category]) GROUP([category])=groupname Set(GROUP()=test)
|
||||
CheckGroup(max[@category]) N/A GotoIf($[ ${GROUP_COUNT()} > 5 ]?103)
|
||||
|
||||
Note that CheckGroup does not have a direct replacement. There is
|
||||
also a new function called GROUP_LIST() which will return a space
|
||||
separated list of all of the groups set on a channel. The GROUP()
|
||||
function can also return the name of the group set on a channel when
|
||||
used in a read environment.
|
||||
|
||||
* The applications DBGet and DBPut have been deprecated in favor of
|
||||
functions. Here is a table of their replacements:
|
||||
|
||||
DBGet(foo=family/key) Set(foo=${DB(family/key)})
|
||||
DBPut(family/key=${foo}) Set(DB(family/key)=${foo})
|
||||
|
||||
* The application SetLanguage has been deprecated in favor of the
|
||||
function LANGUAGE().
|
||||
|
||||
SetLanguage(fr) Set(LANGUAGE()=fr)
|
||||
|
||||
The LANGUAGE function can also return the currently set language:
|
||||
|
||||
Set(MYLANG=${LANGUAGE()})
|
||||
|
||||
* The applications AbsoluteTimeout, DigitTimeout, and ResponseTimeout
|
||||
have been deprecated in favor of the function TIMEOUT(timeouttype):
|
||||
|
||||
AbsoluteTimeout(300) Set(TIMEOUT(absolute)=300)
|
||||
DigitTimeout(15) Set(TIMEOUT(digit)=15)
|
||||
ResponseTimeout(15) Set(TIMEOUT(response)=15)
|
||||
|
||||
The TIMEOUT() function can also return the currently set timeouts:
|
||||
|
||||
Set(DTIMEOUT=${TIMEOUT(digit)})
|
||||
|
||||
* The applications SetCIDName, SetCIDNum, and SetRDNIS have been
|
||||
deprecated in favor of the CALLERID(datatype) function:
|
||||
|
||||
SetCIDName(Joe Cool) Set(CALLERID(name)=Joe Cool)
|
||||
SetCIDNum(2025551212) Set(CALLERID(number)=2025551212)
|
||||
SetRDNIS(2024561414) Set(CALLERID(RDNIS)=2024561414)
|
||||
|
||||
* The application Record now uses the period to separate the filename
|
||||
from the format, rather than the colon.
|
||||
|
||||
* The application VoiceMail now supports a 'temporary' greeting for each
|
||||
mailbox. This greeting can be recorded by using option 4 in the
|
||||
'mailbox options' menu, and 'change your password' option has been
|
||||
moved to option 5.
|
||||
|
||||
* The application VoiceMailMain now only matches the 'default' context if
|
||||
none is specified in the arguments. (This was the previously
|
||||
documented behavior, however, we didn't follow that behavior.) The old
|
||||
behavior can be restored by setting searchcontexts=yes in voicemail.conf.
|
||||
|
||||
Queues:
|
||||
|
||||
* A queue is now considered empty not only if there are no members but if
|
||||
none of the members are available (e.g. agents not logged on). To
|
||||
restore the original behavior, use "leavewhenempty=strict" or
|
||||
"joinwhenempty=strict" instead of "=yes" for those options.
|
||||
|
||||
* It is now possible to use multi-digit extensions in the exit context
|
||||
for a queue (although you should not have overlapping extensions,
|
||||
as there is no digit timeout). This means that the EXITWITHKEY event
|
||||
in queue_log can now contain a key field with more than a single
|
||||
character in it.
|
||||
|
||||
Extensions:
|
||||
|
||||
* By default, there is a new option called "autofallthrough" in
|
||||
extensions.conf that is set to yes. Asterisk 1.0 (and earlier)
|
||||
behavior was to wait for an extension to be dialed after there were no
|
||||
more extensions to execute. "autofallthrough" changes this behavior
|
||||
so that the call will immediately be terminated with BUSY,
|
||||
CONGESTION, or HANGUP based on Asterisk's best guess. If you are
|
||||
writing an extension for IVR, you must use the WaitExten application
|
||||
if "autofallthrough" is set to yes.
|
||||
|
||||
AGI:
|
||||
|
||||
* AGI scripts did not always get SIGHUP at the end, previously. That
|
||||
behavior has been fixed. If you do not want your script to terminate
|
||||
at the end of AGI being called (e.g. on a hangup) then set SIGHUP to
|
||||
be ignored within your application.
|
||||
|
||||
* CallerID is reported with agi_callerid and agi_calleridname instead
|
||||
of a single parameter holding both.
|
||||
|
||||
Music On Hold:
|
||||
|
||||
* The preferred format for musiconhold.conf has changed; please see the
|
||||
sample configuration file for the new format. The existing format
|
||||
is still supported but will generate warnings when the module is loaded.
|
||||
|
||||
chan_modem:
|
||||
|
||||
* All the chan_modem channel drivers (aopen, bestdata and i4l) are deprecated
|
||||
in this release, and will be removed in the next major Asterisk release.
|
||||
Please migrate to chan_misdn for ISDN interfaces; there is no upgrade
|
||||
path for aopen and bestdata modem users.
|
||||
|
||||
MeetMe:
|
||||
|
||||
* The conference application now allows users to increase/decrease their
|
||||
speaking volume and listening volume (independently of each other and
|
||||
other users); the 'admin' and 'user' menus have changed, and new sound
|
||||
files are included with this release. However, if a user calling in
|
||||
over a Zaptel channel that does NOT have hardware DTMF detection
|
||||
increases their speaking volume, it is likely they will no longer be
|
||||
able to enter/exit the menu or make any further adjustments, as the
|
||||
software DTMF detector will not be able to recognize the DTMF coming
|
||||
from their device.
|
||||
|
||||
GetVar Manager Action:
|
||||
|
||||
* Previously, the behavior of the GetVar manager action reported the value
|
||||
of a variable in the following manner:
|
||||
> name: value
|
||||
This has been changed to a manner similar to the SetVar action and is now
|
||||
> Variable: name
|
||||
> Value: value
|
||||
529
UPGRADE.txt
529
UPGRADE.txt
@@ -1,529 +0,0 @@
|
||||
=========================================================
|
||||
=== Information for upgrading from Asterisk 1.2 to 1.4
|
||||
===
|
||||
===
|
||||
=== UPGRADE-1.2.txt -- Upgrade info for 1.0 to 1.2
|
||||
=== UPGRADE.txt -- Upgrade info for 1.2 to 1.4
|
||||
=========================================================
|
||||
|
||||
IAX2:
|
||||
|
||||
* The firmware for the IAXy has been removed from Asterisk. It can be
|
||||
downloaded from http://downloads.digium.com/pub/iaxy/. To have Asterisk
|
||||
install the firmware into its proper location, place the firmware in the
|
||||
contrib/firmware/iax/ directory in the Asterisk source tree before running
|
||||
"make install".
|
||||
|
||||
* There have been some changes to the IAX2 protocol to address the security
|
||||
concerns documented in the security advisory AST-2009-006. Please see the
|
||||
IAX2 security document, doc/IAX2-security.pdf, for information regarding
|
||||
backwards compatibility with versions of Asterisk that do not contain these
|
||||
changes to IAX2.
|
||||
|
||||
Build Process (configure script):
|
||||
|
||||
Asterisk now uses an autoconf-generated configuration script to learn how it
|
||||
should build itself for your system. As it is a standard script, running:
|
||||
|
||||
$ ./configure --help
|
||||
|
||||
will show you all the options available. This script can be used to tell the
|
||||
build process what libraries you have on your system (if it cannot find them
|
||||
automatically), which libraries you wish to have ignored even though they may
|
||||
be present, etc.
|
||||
|
||||
You must run the configure script before Asterisk will build, although it will
|
||||
attempt to automatically run it for you with no options specified; for most
|
||||
users, that will result in a similar build to what they would have had before
|
||||
the configure script was added to the build process (except for having to run
|
||||
'make' again after the configure script is run). Note that the configure script
|
||||
does NOT need to be re-run just to rebuild Asterisk; you only need to re-run it
|
||||
when your system configuration changes or you wish to build Asterisk with
|
||||
different options.
|
||||
|
||||
Build Process (module selection):
|
||||
|
||||
The Asterisk source tree now includes a basic module selection and build option
|
||||
selection tool called 'menuselect'. Run 'make menuselect' to make your choices.
|
||||
In this tool, you can disable building of modules that you don't care about,
|
||||
turn on/off global options for the build and see which modules will not
|
||||
(and cannot) be built because your system does not have the required external
|
||||
dependencies installed.
|
||||
|
||||
The resulting file from menuselect is called 'menuselect.makeopts'. Note that
|
||||
the resulting menuselect.makeopts file generally contains which modules *not*
|
||||
to build. The modules listed in this file indicate which modules have unmet
|
||||
dependencies, a present conflict, or have been disabled by the user in the
|
||||
menuselect interface. Compiler Flags can also be set in the menuselect
|
||||
interface. In this case, the resulting file contains which CFLAGS are in use,
|
||||
not which ones are not in use.
|
||||
|
||||
If you would like to save your choices and have them applied against all
|
||||
builds, the file can be copied to '~/.asterisk.makeopts' or
|
||||
'/etc/asterisk.makeopts'.
|
||||
|
||||
Build Process (Makefile targets):
|
||||
|
||||
The 'valgrind' and 'dont-optimize' targets have been removed; their functionality
|
||||
is available by enabling the DONT_OPTIMIZE setting in the 'Compiler Flags' menu
|
||||
in the menuselect tool.
|
||||
|
||||
It is now possible to run most make targets against a single subdirectory; from
|
||||
the top level directory, for example, 'make channels' will run 'make all' in the
|
||||
'channels' subdirectory. This also is true for 'clean', 'distclean' and 'depend'.
|
||||
|
||||
Sound (prompt) and Music On Hold files:
|
||||
|
||||
Beginning with Asterisk 1.4, the sound files and music on hold files supplied for
|
||||
use with Asterisk have been replaced with new versions produced from high quality
|
||||
master recordings, and are available in three languages (English, French and
|
||||
Spanish) and in five formats (WAV (uncompressed), mu-Law, a-Law, GSM and G.729).
|
||||
In addition, the music on hold files provided by opsound.org Music are now available
|
||||
in the same five formats, but no longer available in MP3 format.
|
||||
|
||||
The Asterisk 1.4 tarball packages will only include English prompts in GSM format,
|
||||
(as were supplied with previous releases) and the opsound.org MOH files in WAV format.
|
||||
All of the other variations can be installed by running 'make menuselect' and
|
||||
selecting the packages you wish to install; when you run 'make install', those
|
||||
packages will be downloaded and installed along with the standard files included
|
||||
in the tarball.
|
||||
|
||||
If for some reason you expect to not have Internet access at the time you will be
|
||||
running 'make install', you can make your package selections using menuselect and
|
||||
then run 'make sounds' to download (only) the sound packages; this will leave the
|
||||
sound packages in the 'sounds' subdirectory to be used later during installation.
|
||||
|
||||
WARNING: Asterisk 1.4 supports a new layout for sound files in multiple languages;
|
||||
instead of the alternate-language files being stored in subdirectories underneath
|
||||
the existing files (for French, that would be digits/fr, letters/fr, phonetic/fr,
|
||||
etc.) the new layout creates one directory under /var/lib/asterisk/sounds for the
|
||||
language itself, then places all the sound files for that language under that
|
||||
directory and its subdirectories. This is the layout that will be created if you
|
||||
select non-English languages to be installed via menuselect, HOWEVER Asterisk does
|
||||
not default to this layout and will not find the files in the places it expects them
|
||||
to be. If you wish to use this layout, make sure you put 'languageprefix=yes' in your
|
||||
/etc/asterisk/asterisk.conf file, so that Asterisk will know how the files were
|
||||
installed.
|
||||
|
||||
PBX Core:
|
||||
|
||||
* The (very old and undocumented) ability to use BYEXTENSION for dialing
|
||||
instead of ${EXTEN} has been removed.
|
||||
|
||||
* Builtin (res_features) transfer functionality attempts to use the context
|
||||
defined in TRANSFER_CONTEXT variable of the transferer channel first. If
|
||||
not set, it uses the transferee variable. If not set in any channel, it will
|
||||
attempt to use the last non macro context. If not possible, it will default
|
||||
to the current context.
|
||||
|
||||
* The autofallthrough setting introduced in Asterisk 1.2 now defaults to 'yes';
|
||||
if your dialplan relies on the ability to 'run off the end' of an extension
|
||||
and wait for a new extension without using WaitExten() to accomplish that,
|
||||
you will need set autofallthrough to 'no' in your extensions.conf file.
|
||||
|
||||
Language Support:
|
||||
|
||||
* Support for Taiwanese was incorrectly supported with the "tw" language code.
|
||||
In reality, the "tw" language code is reserved for the Twi language, native
|
||||
to Ghana. If you were previously using the "tw" language code, you should
|
||||
switch to using either "zh" (for Mandarin Chinese) or "zh_TW" for Taiwan
|
||||
specific localizations. Additionally, "mx" should be changed to "es_MX",
|
||||
Georgian was incorrectly specified as "ge" but should be "ka", and Czech is
|
||||
"cs", not "cz".
|
||||
|
||||
Command Line Interface:
|
||||
|
||||
* 'show channels concise', designed to be used by applications that will parse
|
||||
its output, previously used ':' characters to separate fields. However, some
|
||||
of those fields can easily contain that character, making the output not
|
||||
parseable. The delimiter has been changed to '!'.
|
||||
|
||||
Applications:
|
||||
|
||||
* In previous Asterisk releases, many applications would jump to priority n+101
|
||||
to indicate some kind of status or error condition. This functionality was
|
||||
marked deprecated in Asterisk 1.2. An option to disable it was provided with
|
||||
the default value set to 'on'. The default value for the global priority
|
||||
jumping option is now 'off'.
|
||||
|
||||
* The applications Cut, Sort, DBGet, DBPut, SetCIDNum, SetCIDName, SetRDNIS,
|
||||
AbsoluteTimeout, DigitTimeout, ResponseTimeout, SetLanguage, GetGroupCount,
|
||||
and GetGroupMatchCount were all deprecated in version 1.2, and therefore have
|
||||
been removed in this version. You should use the equivalent dialplan
|
||||
function in places where you have previously used one of these applications.
|
||||
|
||||
* The application SetGlobalVar has been deprecated. You should replace uses
|
||||
of this application with the following combination of Set and GLOBAL():
|
||||
Set(GLOBAL(name)=value). You may also access global variables exclusively by
|
||||
using the GLOBAL() dialplan function, instead of relying on variable
|
||||
interpolation falling back to globals when no channel variable is set.
|
||||
|
||||
* The application SetVar has been renamed to Set. The syntax SetVar was marked
|
||||
deprecated in version 1.2 and is no longer recognized in this version. The
|
||||
use of Set with multiple argument pairs has also been deprecated. Please
|
||||
separate each name/value pair into its own dialplan line.
|
||||
|
||||
* app_read has been updated to use the newer options codes, using "skip" or
|
||||
"noanswer" will not work. Use s or n. Also there is a new feature i, for
|
||||
using indication tones, so typing in skip would give you unexpected results.
|
||||
|
||||
* OSPAuth is added to authenticate OSP tokens in in_bound call setup messages.
|
||||
|
||||
* The CONNECT event in the queue_log from app_queue now has a second field
|
||||
in addition to the holdtime field. It contains the unique ID of the
|
||||
queue member channel that is taking the call. This is useful when trying
|
||||
to link recording filenames back to a particular call from the queue.
|
||||
|
||||
* The old/current behavior of app_queue has a serial type behavior
|
||||
in that the queue will make all waiting callers wait in the queue
|
||||
even if there is more than one available member ready to take
|
||||
calls until the head caller is connected with the member they
|
||||
were trying to get to. The next waiting caller in line then
|
||||
becomes the head caller, and they are then connected with the
|
||||
next available member and all available members and waiting callers
|
||||
waits while this happens. This cycle continues until there are
|
||||
no more available members or waiting callers, whichever comes first.
|
||||
The new behavior, enabled by setting autofill=yes in queues.conf
|
||||
either at the [general] level to default for all queues or
|
||||
to set on a per-queue level, makes sure that when the waiting
|
||||
callers are connecting with available members in a parallel fashion
|
||||
until there are no more available members or no more waiting callers,
|
||||
whichever comes first. This is probably more along the lines of how
|
||||
one would expect a queue should work and in most cases, you will want
|
||||
to enable this new behavior. If you do not specify or comment out this
|
||||
option, it will default to "no" to keep backward compatability with the old
|
||||
behavior.
|
||||
|
||||
* Queues depend on the channel driver reporting the proper state
|
||||
for each member of the queue. To get proper signalling on
|
||||
queue members that use the SIP channel driver, you need to
|
||||
enable a call limit (could be set to a high value so it
|
||||
is not put into action) and also make sure that both inbound
|
||||
and outbound calls are accounted for.
|
||||
|
||||
Example:
|
||||
|
||||
[general]
|
||||
limitonpeer = yes
|
||||
|
||||
[peername]
|
||||
type=friend
|
||||
call-limit=10
|
||||
|
||||
|
||||
* The app_queue application now has the ability to use MixMonitor to
|
||||
record conversations queue members are having with queue callers. Please
|
||||
see configs/queues.conf.sample for more information on this option.
|
||||
|
||||
* The app_queue application strategy called 'roundrobin' has been deprecated
|
||||
for this release. Users are encouraged to use 'rrmemory' instead, since it
|
||||
provides more 'true' round-robin call delivery. For the Asterisk 1.6 release,
|
||||
'rrmemory' will be renamed 'roundrobin'.
|
||||
|
||||
* The app_queue application option called 'monitor-join' has been deprecated
|
||||
for this release. Users are encouraged to use 'monitor-type=mixmonitor' instead,
|
||||
since it provides the same functionality but is not dependent on soxmix or some
|
||||
other external program in order to mix the audio.
|
||||
|
||||
* app_meetme: The 'm' option (monitor) is renamed to 'l' (listen only), and
|
||||
the 'm' option now provides the functionality of "initially muted".
|
||||
In practice, most existing dialplans using the 'm' flag should not notice
|
||||
any difference, unless the keypad menu is enabled, allowing the user
|
||||
to unmute themsleves.
|
||||
|
||||
* ast_play_and_record would attempt to cancel the recording if a DTMF
|
||||
'0' was received. This behavior was not documented in most of the
|
||||
applications that used ast_play_and_record and the return codes from
|
||||
ast_play_and_record weren't checked for properly.
|
||||
ast_play_and_record has been changed so that '0' no longer cancels a
|
||||
recording. If you want to allow DTMF digits to cancel an
|
||||
in-progress recording use ast_play_and_record_full which allows you
|
||||
to specify which DTMF digits can be used to accept a recording and
|
||||
which digits can be used to cancel a recording.
|
||||
|
||||
* ast_app_messagecount has been renamed to ast_app_inboxcount. There is now a
|
||||
new ast_app_messagecount function which takes a single context/mailbox/folder
|
||||
mailbox specification and returns the message count for that folder only.
|
||||
This addresses the deficiency of not being able to count the number of
|
||||
messages in folders other than INBOX and Old.
|
||||
|
||||
* The exit behavior of the AGI applications has changed. Previously, when
|
||||
a connection to an AGI server failed, the application would cause the channel
|
||||
to immediately stop dialplan execution and hangup. Now, the only time that
|
||||
the AGI applications will cause the channel to stop dialplan execution is
|
||||
when the channel itself requests hangup. The AGI applications now set an
|
||||
AGISTATUS variable which will allow you to find out whether running the AGI
|
||||
was successful or not.
|
||||
|
||||
Previously, there was no way to handle the case where Asterisk was unable to
|
||||
locally execute an AGI script for some reason. In this case, dialplan
|
||||
execution will continue as it did before, but the AGISTATUS variable will be
|
||||
set to "FAILURE".
|
||||
|
||||
A locally executed AGI script can now exit with a non-zero exit code and this
|
||||
failure will be detected by Asterisk. If an AGI script exits with a non-zero
|
||||
exit code, the AGISTATUS variable will be set to "FAILURE" as opposed to
|
||||
"SUCCESS".
|
||||
|
||||
* app_voicemail: The ODBC_STORAGE capability now requires the extended table format
|
||||
previously used only by EXTENDED_ODBC_STORAGE. This means that you will need to update
|
||||
your table format using the schema provided in doc/odbcstorage.txt
|
||||
|
||||
* app_waitforsilence: Fixes have been made to this application which changes the
|
||||
default behavior with how quickly it returns. You can maintain "old-style" behavior
|
||||
with the addition/use of a third "timeout" parameter.
|
||||
Please consult the application documentation and make changes to your dialplan
|
||||
if appropriate.
|
||||
|
||||
Manager:
|
||||
|
||||
* After executing the 'status' manager action, the "Status" manager events
|
||||
included the header "CallerID:" which was actually only the CallerID number,
|
||||
and not the full CallerID string. This header has been renamed to
|
||||
"CallerIDNum". For compatibility purposes, the CallerID parameter will remain
|
||||
until after the release of 1.4, when it will be removed. Please use the time
|
||||
during the 1.4 release to make this transition.
|
||||
|
||||
* The AgentConnect event now has an additional field called "BridgedChannel"
|
||||
which contains the unique ID of the queue member channel that is taking the
|
||||
call. This is useful when trying to link recording filenames back to
|
||||
a particular call from the queue.
|
||||
|
||||
* app_userevent has been modified to always send Event: UserEvent with the
|
||||
additional header UserEvent: <userspec>. Also, the Channel and UniqueID
|
||||
headers are not automatically sent, unless you specify them as separate
|
||||
arguments. Please see the application help for the new syntax.
|
||||
|
||||
* app_meetme: Mute and Unmute events are now reported via the Manager API.
|
||||
Native Manager API commands MeetMeMute and MeetMeUnmute are provided, which
|
||||
are easier to use than "Action Command:". The MeetMeStopTalking event has
|
||||
also been deprecated in favor of the already existing MeetmeTalking event
|
||||
with a "Status" of "on" or "off" added.
|
||||
|
||||
* OriginateFailure and OriginateSuccess events were replaced by event
|
||||
OriginateResponse with a header named "Response" to indicate success or
|
||||
failure
|
||||
|
||||
Variables:
|
||||
|
||||
* The builtin variables ${CALLERID}, ${CALLERIDNAME}, ${CALLERIDNUM},
|
||||
${CALLERANI}, ${DNID}, ${RDNIS}, ${DATETIME}, ${TIMESTAMP}, ${ACCOUNTCODE},
|
||||
and ${LANGUAGE} have all been deprecated in favor of their related dialplan
|
||||
functions. You are encouraged to move towards the associated dialplan
|
||||
function, as these variables will be removed in a future release.
|
||||
|
||||
* The CDR-CSV variables uniqueid, userfield, and basing time on GMT are now
|
||||
adjustable from cdr.conf, instead of recompiling.
|
||||
|
||||
* OSP applications exports several new variables, ${OSPINHANDLE},
|
||||
${OSPOUTHANDLE}, ${OSPINTOKEN}, ${OSPOUTTOKEN}, ${OSPCALLING},
|
||||
${OSPINTIMELIMIT}, and ${OSPOUTTIMELIMIT}
|
||||
|
||||
* Builtin transfer functionality sets the variable ${TRANSFERERNAME} in the new
|
||||
created channel. This variables holds the channel name of the transferer.
|
||||
|
||||
* The dial plan variable PRI_CAUSE will be removed from future versions
|
||||
of Asterisk.
|
||||
It is replaced by adding a cause value to the hangup() application.
|
||||
|
||||
Functions:
|
||||
|
||||
* The function ${CHECK_MD5()} has been deprecated in favor of using an
|
||||
expression: $[${MD5(<string>)} = ${saved_md5}].
|
||||
|
||||
* The 'builtin' functions that used to be combined in pbx_functions.so are
|
||||
now built as separate modules. If you are not using 'autoload=yes' in your
|
||||
modules.conf file then you will need to explicitly load the modules that
|
||||
contain the functions you want to use.
|
||||
|
||||
* The ENUMLOOKUP() function with the 'c' option (for counting the number of
|
||||
records), but the lookup fails to match any records, the returned value will
|
||||
now be "0" instead of blank.
|
||||
|
||||
* The REALTIME() function is now available in version 1.4 and app_realtime has
|
||||
been deprecated in favor of the new function. app_realtime will be removed
|
||||
completely with the version 1.6 release so please take the time between
|
||||
releases to make any necessary changes
|
||||
|
||||
* The QUEUEAGENTCOUNT() function has been deprecated in favor of
|
||||
QUEUE_MEMBER_COUNT().
|
||||
|
||||
The IAX2 channel:
|
||||
|
||||
* It is possible that previous configurations depended on the order in which
|
||||
peers and users were specified in iax.conf for forcing the order in which
|
||||
chan_iax2 matched against them. This behavior is going away and is considered
|
||||
deprecated in this version. Avoid having ambiguous peer and user entries and
|
||||
to make things easy on yourself, always set the "username" option for users
|
||||
so that the remote end can match on that exactly instead of trying to infer
|
||||
which user you want based on host.
|
||||
|
||||
If you would like to go ahead and use the new behavior which doesn't use the
|
||||
order in the config file to influence matching order, then change the
|
||||
MAX_PEER_BUCKETS define in chan_iax2.c to a value greater than one. An
|
||||
example is provided there. By changing this, you will get *much* better
|
||||
performance on systems that do a lot of peer and user lookups as they will be
|
||||
stored in memory in a much more efficient manner.
|
||||
|
||||
* The "mailboxdetail" option has been deprecated. Previously, if this option
|
||||
was not enabled, the 2 byte MSGCOUNT information element would be set to all
|
||||
1's to indicate there there is some number of messages waiting. With this
|
||||
option enabled, the number of new messages were placed in one byte and the
|
||||
number of old messages are placed in the other. This is now the default
|
||||
(and the only) behavior.
|
||||
|
||||
The SIP channel:
|
||||
|
||||
* The "incominglimit" setting is replaced by the "call-limit" setting in
|
||||
sip.conf.
|
||||
|
||||
* OSP support code is removed from SIP channel to OSP applications. ospauth
|
||||
option in sip.conf is removed to osp.conf as authpolicy. allowguest option
|
||||
in sip.conf cannot be set as osp anymore.
|
||||
|
||||
* The Asterisk RTP stack has been changed in regards to RFC2833 reception
|
||||
and transmission. Packets will now be sent with proper duration instead of all
|
||||
at once. If you are receiving calls from a pre-1.4 Asterisk installation you
|
||||
will want to turn on the rfc2833compensate option. Without this option your
|
||||
DTMF reception may act poorly.
|
||||
|
||||
* The $SIPUSERAGENT dialplan variable is deprecated and will be removed
|
||||
in coming versions of Asterisk. Please use the dialplan function
|
||||
SIPCHANINFO(useragent) instead.
|
||||
|
||||
* The ALERT_INFO dialplan variable is deprecated and will be removed
|
||||
in coming versions of Asterisk. Please use the dialplan application
|
||||
sipaddheader() to add the "Alert-Info" header to the outbound invite.
|
||||
|
||||
* The "canreinvite" option has changed. canreinvite=yes used to disable
|
||||
re-invites if you had NAT=yes. In 1.4, you need to set canreinvite=nonat
|
||||
to disable re-invites when NAT=yes. This is propably what you want.
|
||||
The settings are now: "yes", "no", "nonat", "update". Please consult
|
||||
sip.conf.sample for detailed information.
|
||||
|
||||
The Zap channel:
|
||||
|
||||
* Support for MFC/R2 has been removed, as it has not been functional for some
|
||||
time and it has no maintainer.
|
||||
|
||||
The Agent channel:
|
||||
|
||||
* Callback mode (AgentCallbackLogin) is now deprecated, since the entire function
|
||||
it provided can be done using dialplan logic, without requiring additional
|
||||
channel and module locks (which frequently caused deadlocks). An example of
|
||||
how to do this using AEL dialplan is in doc/queues-with-callback-members.txt.
|
||||
|
||||
The G726-32 codec:
|
||||
|
||||
* It has been determined that previous versions of Asterisk used the wrong codeword
|
||||
packing order for G726-32 data. This version supports both available packing orders,
|
||||
and can transcode between them. It also now selects the proper order when
|
||||
negotiating with a SIP peer based on the codec name supplied in the SDP. However,
|
||||
there are existing devices that improperly request one order and then use another;
|
||||
Sipura and Grandstream ATAs are known to do this, and there may be others. To
|
||||
be able to continue to use these devices with this version of Asterisk and the
|
||||
G726-32 codec, a configuration parameter called 'g726nonstandard' has been added
|
||||
to sip.conf, so that Asterisk can use the packing order expected by the device (even
|
||||
though it requested a different order). In addition, the internal format number for
|
||||
G726-32 has been changed, and the old number is now assigned to AAL2-G726-32. The
|
||||
result of this is that this version of Asterisk will be able to interoperate over
|
||||
IAX2 with older versions of Asterisk, as long as this version is told to allow
|
||||
'g726aal2' instead of 'g726' as the codec for the call.
|
||||
|
||||
Installation:
|
||||
|
||||
* On BSD systems, the installation directories have changed to more "FreeBSDish"
|
||||
directories. On startup, Asterisk will look for the main configuration in
|
||||
/usr/local/etc/asterisk/asterisk.conf
|
||||
If you have an old installation, you might want to remove the binaries and
|
||||
move the configuration files to the new locations. The following directories
|
||||
are now default:
|
||||
ASTLIBDIR /usr/local/lib/asterisk
|
||||
ASTVARLIBDIR /usr/local/share/asterisk
|
||||
ASTETCDIR /usr/local/etc/asterisk
|
||||
ASTBINDIR /usr/local/bin/asterisk
|
||||
ASTSBINDIR /usr/local/sbin/asterisk
|
||||
|
||||
Music on Hold:
|
||||
|
||||
* The music on hold handling has been changed in some significant ways in hopes
|
||||
to make it work in a way that is much less confusing to users. Behavior will
|
||||
not change if the same configuration is used from older versions of Asterisk.
|
||||
However, there are some new configuration options that will make things work
|
||||
in a way that makes more sense.
|
||||
|
||||
Previously, many of the channel drivers had an option called "musicclass" or
|
||||
something similar. This option set what music on hold class this channel
|
||||
would *hear* when put on hold. Some people expected (with good reason) that
|
||||
this option was to configure what music on hold class to play when putting
|
||||
the bridged channel on hold. This option has now been deprecated.
|
||||
|
||||
Two new music on hold related configuration options for channel drivers have
|
||||
been introduced. Some channel drivers support both options, some just one,
|
||||
and some support neither of them. Check the sample configuration files to see
|
||||
which options apply to which channel driver.
|
||||
|
||||
The "mohsuggest" option specifies which music on hold class to suggest to the
|
||||
bridged channel when putting them on hold. The only way that this class can
|
||||
be overridden is if the bridged channel has a specific music class set that
|
||||
was done in the dialplan using Set(CHANNEL(musicclass)=something).
|
||||
|
||||
The "mohinterpret" option is similar to the old "musicclass" option. It
|
||||
specifies which music on hold class this channel would like to listen to when
|
||||
put on hold. This music class is only effective if this channel has no music
|
||||
class set on it from the dialplan and the bridged channel putting this one on
|
||||
hold had no "mohsuggest" setting.
|
||||
|
||||
The IAX2 and Zap channel drivers have an additional feature for the
|
||||
"mohinterpret" option. If this option is set to "passthrough", then these
|
||||
channel drivers will pass through the HOLD message in signalling instead of
|
||||
starting music on hold on the channel. An example for how this would be
|
||||
useful is in an enterprise network of Asterisk servers. When one phone on one
|
||||
server puts a phone on a different server on hold, the remote server will be
|
||||
responsible for playing the hold music to its local phone that was put on
|
||||
hold instead of the far end server across the network playing the music.
|
||||
|
||||
CDR Records:
|
||||
|
||||
* The behavior of the "clid" field of the CDR has always been that it will
|
||||
contain the callerid ANI if it is set, or the callerid number if ANI was not
|
||||
set. When using the "callerid" option for various channel drivers, some
|
||||
would set ANI and some would not. This has been cleared up so that all
|
||||
channel drivers set ANI. If you would like to change the callerid number
|
||||
on the channel from the dialplan and have that change also show up in the
|
||||
CDR, then you *must* set CALLERID(ANI) as well as CALLERID(num).
|
||||
|
||||
* cdr_pgsql now assumes the encoding of strings it is handed are in LATIN9,
|
||||
which should cover most uses of the extended ASCII set. If your strings
|
||||
use a different encoding in Asterisk, the "encoding" parameter may be set
|
||||
to specify the correct character set.
|
||||
|
||||
API:
|
||||
|
||||
* There are some API functions that were not previously prefixed with the 'ast_'
|
||||
prefix but now are; these include the ADSI, ODBC and AGI interfaces. If you
|
||||
have a module that uses the services provided by res_adsi, res_odbc, or
|
||||
res_agi, you will need to add ast_ prefixes to the functions that you call
|
||||
from those modules.
|
||||
|
||||
Formats:
|
||||
|
||||
* format_wav: The GAIN preprocessor definition has been changed from 2 to 0
|
||||
in Asterisk 1.4. This change was made in response to user complaints of
|
||||
choppiness or the clipping of loud signal peaks. The GAIN preprocessor
|
||||
definition will be retained in Asterisk 1.4, but will be removed in a
|
||||
future release. The use of GAIN for the increasing of voicemail message
|
||||
volume should use the 'volgain' option in voicemail.conf
|
||||
|
||||
iLBC Codec:
|
||||
|
||||
* Previously, the Asterisk source code distribution included the iLBC
|
||||
encoder/decoder source code, from Global IP Solutions
|
||||
(http://www.gipscorp.com). This code is not licensed for
|
||||
distribution, and thus has been removed from the Asterisk source
|
||||
code distribution. If you wish to use codec_ilbc to support iLBC
|
||||
channels in Asterisk, you can run the contrib/scripts/get_ilbc_source.sh
|
||||
script to download the source and put it in the proper place in
|
||||
the Asterisk build tree. Once that is done you can follow your normal
|
||||
steps of building Asterisk. You will need to run 'menuselect' and enable
|
||||
the iLBC codec in the 'Codec Translators' category.
|
||||
@@ -1,105 +0,0 @@
|
||||
=========================================================
|
||||
=== Information for upgrading from Zaptel to DAHDI ===
|
||||
=========================================================
|
||||
|
||||
As announced in early 2008, Digium is renaming the Zaptel telephony
|
||||
interface project to DAHDI (Digium Asterisk Hardware Device Interface)
|
||||
to accommodate the desires of the owner of the Zaptel trademark for
|
||||
telephony purposes.
|
||||
|
||||
This version of Asterisk can be built using either Zaptel or DAHDI,
|
||||
and has many changes to make the use of DAHDI as easy as possible for
|
||||
existing users with dialplans, CDR parsers, AMI applications, and
|
||||
others that expect Zaptel to be in use.
|
||||
|
||||
First, the modules that directly use services from Zaptel/DAHDI have been
|
||||
renamed; the new names are:
|
||||
|
||||
chan_zap.so -> chan_dahdi.so
|
||||
app_zapbarge.so -> app_dahdibarge.so
|
||||
app_zapras.so -> app_dahdiras.so
|
||||
app_zapscan.so -> app_dahdiscan.so
|
||||
codec_zap.so -> codec_dahdi.so
|
||||
|
||||
However, in spite of the file name changes, the channels and
|
||||
applications provided by these modules can still be used with 'Zap'
|
||||
style names; see below for more information.
|
||||
|
||||
Second, there are have been a number of efforts made to ensure that
|
||||
existing systems will not have to have any major configuration changes
|
||||
made solely because Asterisk was built against DAHDI instead of
|
||||
Zaptel. This includes:
|
||||
|
||||
chan_dahdi.so:
|
||||
|
||||
This module will determine which channel name ('Zap' or 'DAHDI')
|
||||
should be used for incoming and outgoing channels based on the
|
||||
build-time choice of telephony drivers. However, if you wish to
|
||||
continue using the 'Zap' channel name even though you built Asterisk
|
||||
against the DAHDI drivers, you can add the following line to the
|
||||
[options] section of your /etc/asterisk/asterisk.conf file:
|
||||
|
||||
dahdichanname = no
|
||||
|
||||
All CLI commands that begin with 'zap' are now available as 'dahdi'
|
||||
commands as well; the 'zap' variants will report that they are
|
||||
deprecated the first time you use each one in an Asterisk instance,
|
||||
but they will otherwise operate just as they did in previous versions.
|
||||
|
||||
All Asterisk Manager Interface (AMI) actions that begin with 'Zap'
|
||||
are also available with 'DAHDI' prefixes.
|
||||
|
||||
The ZapSendKeypadFacility dialplan application is now available as
|
||||
DAHDISendKeypadFacility as well; the Zap variant will report a deprecation
|
||||
warning but will otherwise operate as it did it in previous
|
||||
versions.
|
||||
|
||||
The configuration for the channel driver will be read from
|
||||
/etc/asterisk/chan_dahdi.conf unless 'dahdichanname' has been set to
|
||||
'no' in asterisk.conf; if that is done, then the configuration will
|
||||
be read from /etc/asterisk/zapata.conf, just as it was in previous
|
||||
versions.
|
||||
|
||||
app_dahdibarge.so:
|
||||
|
||||
The ZapBarge application is now available as DAHDIBarge as well; the
|
||||
ZapBarge variant will report a deprecation warning when used, but
|
||||
will otherwise operate as it did in previous versions. Regardless of
|
||||
which application name is used, the application will restrict itself
|
||||
to channels of the proper type, based on the 'dahdichanname' setting
|
||||
in asterisk.conf.
|
||||
|
||||
app_dahdiras.so:
|
||||
|
||||
The ZapRAS application is now available as DAHDIRAS as well; the
|
||||
ZapRAS variant will report a deprecation warning when used, but will
|
||||
otherwise operate as it did in previous versions. Regardless of
|
||||
which application name is used, the application will restrict itself
|
||||
to channels of the proper type, based on the 'dahdichanname' setting
|
||||
in asterisk.conf.
|
||||
|
||||
app_dahdiscan.so:
|
||||
|
||||
The ZapScan application is now available as DAHDIScan as well; the
|
||||
ZapScan variant will report a deprecation warning when used, but will
|
||||
otherwise operate as it did in previous versions. Regardless of
|
||||
which application name is used, the application will restrict itself
|
||||
to channels of the proper type, based on the 'dahdichanname' setting
|
||||
in asterisk.conf.
|
||||
|
||||
app_flash.so:
|
||||
|
||||
This application has not had any name changes, but will report its
|
||||
usage (via 'show application flash') as being for either DAHDI or
|
||||
Zaptel channels based on the 'dahdichanname' setting in
|
||||
asterisk.conf.
|
||||
|
||||
app_chanspy.so:
|
||||
|
||||
This application will transparently create 'DAHDI' or 'Zap' channels
|
||||
as needed, based on the 'dahdichanname' setting in asterisk.conf.
|
||||
|
||||
app_meetme.so:
|
||||
|
||||
This application will transparently create 'DAHDI' or 'Zap' channels
|
||||
as needed, based on the 'dahdichanname' setting in asterisk.conf.
|
||||
409
acl.c
Normal file
409
acl.c
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Various sorts of access control
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <asterisk/acl.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <sys/ioctl.h>
|
||||
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
|
||||
#include <fcntl.h>
|
||||
#include <net/route.h>
|
||||
|
||||
AST_MUTEX_DEFINE_STATIC(routeseq_lock);
|
||||
#endif
|
||||
|
||||
|
||||
struct ast_ha {
|
||||
/* Host access rule */
|
||||
struct in_addr netaddr;
|
||||
struct in_addr netmask;
|
||||
int sense;
|
||||
struct ast_ha *next;
|
||||
};
|
||||
|
||||
/* Default IP - if not otherwise set, don't breathe garbage */
|
||||
static struct in_addr __ourip = { 0x00000000 };
|
||||
|
||||
struct my_ifreq {
|
||||
char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "eth0", "ppp0", etc. */
|
||||
struct sockaddr_in ifru_addr;
|
||||
};
|
||||
|
||||
/* Free HA structure */
|
||||
void ast_free_ha(struct ast_ha *ha)
|
||||
{
|
||||
struct ast_ha *hal;
|
||||
while(ha) {
|
||||
hal = ha;
|
||||
ha = ha->next;
|
||||
free(hal);
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy HA structure */
|
||||
static void ast_copy_ha(struct ast_ha *from, struct ast_ha *to)
|
||||
{
|
||||
memcpy(&to->netaddr, &from->netaddr, sizeof(from->netaddr));
|
||||
memcpy(&to->netmask, &from->netmask, sizeof(from->netmask));
|
||||
to->sense = from->sense;
|
||||
}
|
||||
|
||||
/* Create duplicate of ha structure */
|
||||
static struct ast_ha *ast_duplicate_ha(struct ast_ha *original)
|
||||
{
|
||||
struct ast_ha *new_ha = malloc(sizeof(struct ast_ha));
|
||||
|
||||
/* Copy from original to new object */
|
||||
ast_copy_ha(original, new_ha);
|
||||
|
||||
return(new_ha);
|
||||
|
||||
}
|
||||
|
||||
/* Create duplicate HA link list */
|
||||
/* Used in chan_sip2 templates */
|
||||
struct ast_ha *ast_duplicate_ha_list(struct ast_ha *original)
|
||||
{
|
||||
struct ast_ha *start=original;
|
||||
struct ast_ha *ret = NULL;
|
||||
struct ast_ha *link,*prev=NULL;
|
||||
|
||||
while(start) {
|
||||
link = ast_duplicate_ha(start); /* Create copy of this object */
|
||||
if (prev)
|
||||
prev->next = link; /* Link previous to this object */
|
||||
|
||||
if (!ret)
|
||||
ret = link; /* Save starting point */
|
||||
|
||||
start = start->next; /* Go to next object */
|
||||
prev = link; /* Save pointer to this object */
|
||||
}
|
||||
return (ret); /* Return start of list */
|
||||
}
|
||||
|
||||
struct ast_ha *ast_append_ha(char *sense, char *stuff, struct ast_ha *path)
|
||||
{
|
||||
struct ast_ha *ha = malloc(sizeof(struct ast_ha));
|
||||
char *nm="255.255.255.255";
|
||||
char tmp[256] = "";
|
||||
struct ast_ha *prev = NULL;
|
||||
struct ast_ha *ret;
|
||||
int x,z;
|
||||
unsigned int y;
|
||||
ret = path;
|
||||
while(path) {
|
||||
prev = path;
|
||||
path = path->next;
|
||||
}
|
||||
if (ha) {
|
||||
strncpy(tmp, stuff, sizeof(tmp) - 1);
|
||||
nm = strchr(tmp, '/');
|
||||
if (!nm)
|
||||
nm = "255.255.255.255";
|
||||
else {
|
||||
*nm = '\0';
|
||||
nm++;
|
||||
}
|
||||
if (!strchr(nm, '.')) {
|
||||
if ((sscanf(nm, "%i", &x) == 1) && (x >= 0) && (x <= 32)) {
|
||||
y = 0;
|
||||
for (z=0;z<x;z++) {
|
||||
y >>= 1;
|
||||
y |= 0x80000000;
|
||||
}
|
||||
ha->netmask.s_addr = htonl(y);
|
||||
}
|
||||
} else if (!inet_aton(nm, &ha->netmask)) {
|
||||
ast_log(LOG_WARNING, "%s not a valid netmask\n", nm);
|
||||
free(ha);
|
||||
return path;
|
||||
}
|
||||
if (!inet_aton(tmp, &ha->netaddr)) {
|
||||
ast_log(LOG_WARNING, "%s not a valid IP\n", tmp);
|
||||
free(ha);
|
||||
return path;
|
||||
}
|
||||
ha->netaddr.s_addr &= ha->netmask.s_addr;
|
||||
if (!strncasecmp(sense, "p", 1)) {
|
||||
ha->sense = AST_SENSE_ALLOW;
|
||||
} else {
|
||||
ha->sense = AST_SENSE_DENY;
|
||||
}
|
||||
ha->next = NULL;
|
||||
if (prev)
|
||||
prev->next = ha;
|
||||
else
|
||||
ret = ha;
|
||||
}
|
||||
ast_log(LOG_DEBUG, "%s/%s appended to acl for peer\n",stuff, nm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin)
|
||||
{
|
||||
/* Start optimistic */
|
||||
int res = AST_SENSE_ALLOW;
|
||||
while(ha) {
|
||||
char iabuf[INET_ADDRSTRLEN];
|
||||
char iabuf2[INET_ADDRSTRLEN];
|
||||
/* DEBUG */
|
||||
ast_log(LOG_DEBUG,
|
||||
"##### Testing %s with %s\n",
|
||||
ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr),
|
||||
ast_inet_ntoa(iabuf2, sizeof(iabuf2), ha->netaddr));
|
||||
/* For each rule, if this address and the netmask = the net address
|
||||
apply the current rule */
|
||||
if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == (ha->netaddr.s_addr))
|
||||
res = ha->sense;
|
||||
ha = ha->next;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_get_ip(struct sockaddr_in *sin, char *value)
|
||||
{
|
||||
struct hostent *hp;
|
||||
struct ast_hostent ahp;
|
||||
hp = ast_gethostbyname(value, &ahp);
|
||||
if (hp) {
|
||||
memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Unable to lookup '%s'\n", value);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* iface is the interface (e.g. eth0); address is the return value */
|
||||
int ast_lookup_iface(char *iface, struct in_addr *address) {
|
||||
int mysock, res = 0;
|
||||
struct my_ifreq ifreq;
|
||||
|
||||
memset(&ifreq, 0, sizeof(ifreq));
|
||||
strncpy(ifreq.ifrn_name,iface,sizeof(ifreq.ifrn_name) - 1);
|
||||
|
||||
mysock = socket(PF_INET,SOCK_DGRAM,IPPROTO_IP);
|
||||
res = ioctl(mysock,SIOCGIFADDR,&ifreq);
|
||||
|
||||
close(mysock);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to get IP of %s: %s\n", iface, strerror(errno));
|
||||
memcpy((char *)address,(char *)&__ourip,sizeof(__ourip));
|
||||
return -1;
|
||||
} else {
|
||||
memcpy((char *)address,(char *)&ifreq.ifru_addr.sin_addr,sizeof(ifreq.ifru_addr.sin_addr));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ast_ouraddrfor(struct in_addr *them, struct in_addr *us)
|
||||
{
|
||||
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
|
||||
struct sockaddr_in *sin;
|
||||
struct sockaddr *sa;
|
||||
struct {
|
||||
struct rt_msghdr m_rtm;
|
||||
char m_space[512];
|
||||
} m_rtmsg;
|
||||
char iabuf[INET_ADDRSTRLEN];
|
||||
char *cp, *p;
|
||||
int i, l, s, seq, flags;
|
||||
pid_t pid = getpid();
|
||||
static int routeseq; /* Protected by "routeseq_lock" mutex */
|
||||
|
||||
p = ast_strdupa(ast_inet_ntoa(iabuf, sizeof(iabuf), *them));
|
||||
memset(us, 0, sizeof(struct in_addr));
|
||||
|
||||
memset(&m_rtmsg, 0, sizeof(m_rtmsg));
|
||||
m_rtmsg.m_rtm.rtm_type = RTM_GET;
|
||||
m_rtmsg.m_rtm.rtm_version = RTM_VERSION;
|
||||
ast_mutex_lock(&routeseq_lock);
|
||||
seq = ++routeseq;
|
||||
ast_mutex_unlock(&routeseq_lock);
|
||||
m_rtmsg.m_rtm.rtm_seq = seq;
|
||||
m_rtmsg.m_rtm.rtm_addrs = RTA_DST | RTA_IFA;
|
||||
m_rtmsg.m_rtm.rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
|
||||
sin = (struct sockaddr_in *)m_rtmsg.m_space;
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_len = sizeof(struct sockaddr_in);
|
||||
sin->sin_addr = *them;
|
||||
|
||||
if ((s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) {
|
||||
ast_log(LOG_ERROR, "Error opening routing socket\n");
|
||||
return -1;
|
||||
}
|
||||
flags = fcntl(s, F_GETFL);
|
||||
fcntl(s, F_SETFL, flags | O_NONBLOCK);
|
||||
if (write(s, (char *)&m_rtmsg, m_rtmsg.m_rtm.rtm_msglen) < 0) {
|
||||
ast_log(LOG_ERROR, "Error writing to routing socket: %s\n", strerror(errno));
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
do {
|
||||
l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
|
||||
} while (l > 0 && (m_rtmsg.m_rtm.rtm_seq != seq || m_rtmsg.m_rtm.rtm_pid != pid));
|
||||
if (l < 0) {
|
||||
if (errno != EAGAIN)
|
||||
ast_log(LOG_ERROR, "Error reading from routing socket\n");
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
close(s);
|
||||
|
||||
if (m_rtmsg.m_rtm.rtm_version != RTM_VERSION) {
|
||||
ast_log(LOG_ERROR, "Unsupported route socket protocol version\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (m_rtmsg.m_rtm.rtm_msglen != l)
|
||||
ast_log(LOG_WARNING, "Message length mismatch, in packet %d, returned %d\n",
|
||||
m_rtmsg.m_rtm.rtm_msglen, l);
|
||||
|
||||
if (m_rtmsg.m_rtm.rtm_errno) {
|
||||
ast_log(LOG_ERROR, "RTM_GET got %s (%d)\n",
|
||||
strerror(m_rtmsg.m_rtm.rtm_errno), m_rtmsg.m_rtm.rtm_errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cp = (char *)m_rtmsg.m_space;
|
||||
if (m_rtmsg.m_rtm.rtm_addrs)
|
||||
for (i = 1; i; i <<= 1)
|
||||
if (m_rtmsg.m_rtm.rtm_addrs & i) {
|
||||
sa = (struct sockaddr *)cp;
|
||||
if (i == RTA_IFA && sa->sa_family == AF_INET) {
|
||||
sin = (struct sockaddr_in *)sa;
|
||||
*us = sin->sin_addr;
|
||||
ast_log(LOG_DEBUG, "Found route to %s, output from our address %s.\n", p, ast_inet_ntoa(iabuf, sizeof(iabuf), *us));
|
||||
return 0;
|
||||
}
|
||||
cp += sa->sa_len > 0 ?
|
||||
(1 + ((sa->sa_len - 1) | (sizeof(long) - 1))) :
|
||||
sizeof(long);
|
||||
}
|
||||
|
||||
ast_log(LOG_DEBUG, "No route found for address %s!\n", p);
|
||||
return -1;
|
||||
#else
|
||||
FILE *PROC;
|
||||
unsigned int remote_ip;
|
||||
int res = 1;
|
||||
char line[256];
|
||||
remote_ip = them->s_addr;
|
||||
|
||||
PROC = fopen("/proc/net/route","r");
|
||||
if (!PROC) {
|
||||
bzero(us,sizeof(struct in_addr));
|
||||
return -1;
|
||||
}
|
||||
/* First line contains headers */
|
||||
fgets(line,sizeof(line),PROC);
|
||||
|
||||
while (!feof(PROC)) {
|
||||
char iface[256];
|
||||
unsigned int dest, gateway, mask;
|
||||
int i,fieldnum;
|
||||
char *fields[40];
|
||||
|
||||
fgets(line,sizeof(line),PROC);
|
||||
|
||||
fieldnum = 0;
|
||||
for (i=0;i<sizeof(line);i++) {
|
||||
char *offset;
|
||||
|
||||
fields[fieldnum++] = line + i;
|
||||
offset = strchr(line + i,'\t');
|
||||
if (offset == NULL) {
|
||||
/* Exit loop */
|
||||
break;
|
||||
} else if (fieldnum >= 9) {
|
||||
/* Short-circuit: can't break at 8, since the end of field 7 is figured when fieldnum=8 */
|
||||
break;
|
||||
} else {
|
||||
*offset = '\0';
|
||||
i = offset - line;
|
||||
}
|
||||
}
|
||||
if (fieldnum >= 8) {
|
||||
|
||||
sscanf(fields[0],"%s",iface);
|
||||
sscanf(fields[1],"%x",&dest);
|
||||
sscanf(fields[2],"%x",&gateway);
|
||||
sscanf(fields[7],"%x",&mask);
|
||||
#if 0
|
||||
{ char iabuf[INET_ADDRSTRLEN];
|
||||
printf("Addr: %s %08x Dest: %08x Mask: %08x\n", ast_inet_ntoa(iabuf, sizeof(iabuf), *them), remote_ip, dest, mask); }
|
||||
#endif
|
||||
/* Looks simple, but here is the magic */
|
||||
if (((remote_ip & mask) ^ dest) == 0) {
|
||||
res = ast_lookup_iface(iface,us);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(PROC);
|
||||
if (res == 1) {
|
||||
ast_log(LOG_WARNING, "Yikes! No default route?!!\n");
|
||||
bzero(us,sizeof(struct in_addr));
|
||||
return -2;
|
||||
} else if (res) {
|
||||
/* We've already warned in subroutine */
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ast_find_ourip(struct in_addr *ourip, struct sockaddr_in bindaddr)
|
||||
{
|
||||
char ourhost[MAXHOSTNAMELEN]="";
|
||||
struct ast_hostent ahp;
|
||||
struct hostent *hp;
|
||||
struct in_addr saddr;
|
||||
|
||||
/* just use the bind address if it is nonzero */
|
||||
if (ntohl(bindaddr.sin_addr.s_addr)) {
|
||||
memcpy(ourip, &bindaddr.sin_addr, sizeof(*ourip));
|
||||
return 0;
|
||||
}
|
||||
/* try to use our hostname */
|
||||
if (gethostname(ourhost, sizeof(ourhost)-1)) {
|
||||
ast_log(LOG_WARNING, "Unable to get hostname\n");
|
||||
} else {
|
||||
hp = ast_gethostbyname(ourhost, &ahp);
|
||||
if (hp) {
|
||||
memcpy(ourip, hp->h_addr, sizeof(*ourip));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* A.ROOT-SERVERS.NET. */
|
||||
if (inet_aton("198.41.0.4", &saddr) && !ast_ouraddrfor(&saddr, ourip))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -30,18 +30,12 @@
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 26/08/2003
|
||||
|
||||
This file contains the code for implementing encryption and decryption
|
||||
for AES (Rijndael) for block and key sizes of 16, 24 and 32 bytes. It
|
||||
can optionally be replaced by code written in assembler using NASM. For
|
||||
further details see the file aesopt.h
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief This file contains the code for implementing encryption and decryption
|
||||
* for AES (Rijndael) for block and key sizes of 16, 24 and 32 bytes. It
|
||||
* can optionally be replaced by code written in assembler using NASM. For
|
||||
* further details see the file aesopt.h
|
||||
*
|
||||
* \author Dr Brian Gladman <brg@gladman.me.uk>
|
||||
*/
|
||||
|
||||
#include "aesopt.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
@@ -30,17 +30,11 @@
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 26/08/2003
|
||||
|
||||
This file contains the code for implementing the key schedule for AES
|
||||
(Rijndael) for block and key sizes of 16, 24, and 32 bytes. See aesopt.h
|
||||
for further details including optimisation.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief This file contains the code for implementing the key schedule for AES
|
||||
* (Rijndael) for block and key sizes of 16, 24, and 32 bytes. See aesopt.h
|
||||
* for further details including optimisation.
|
||||
*
|
||||
* \author Dr Brian Gladman <brg@gladman.me.uk>
|
||||
*/
|
||||
|
||||
#include "aesopt.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
@@ -135,7 +135,7 @@
|
||||
#ifndef _AESOPT_H
|
||||
#define _AESOPT_H
|
||||
|
||||
#include "asterisk/aes.h"
|
||||
#include <asterisk/aes.h>
|
||||
#include "asterisk/endian.h"
|
||||
|
||||
/* CONFIGURATION - USE OF DEFINES
|
||||
@@ -147,6 +147,7 @@
|
||||
#if clauses.
|
||||
*/
|
||||
|
||||
|
||||
/* BYTE ORDER IN 32-BIT WORDS
|
||||
|
||||
To obtain the highest speed on processors with 32-bit words, this code
|
||||
50
agi/Makefile
50
agi/Makefile
@@ -1,49 +1,43 @@
|
||||
#
|
||||
# Asterisk -- A telephony toolkit for Linux.
|
||||
#
|
||||
# Makefile for AGI-related stuff
|
||||
# Makefile for PBX frontends (dynamically loaded)
|
||||
#
|
||||
# Copyright (C) 1999-2006, Digium
|
||||
# Copyright (C) 1999, Mark Spencer
|
||||
#
|
||||
# Mark Spencer <markster@digium.com>
|
||||
# Mark Spencer <markster@linux-support.net>
|
||||
#
|
||||
# This program is free software, distributed under the terms of
|
||||
# the GNU General Public License
|
||||
#
|
||||
|
||||
.PHONY: clean all uninstall
|
||||
AGIS=agi-test.agi eagi-test eagi-sphinx-test
|
||||
|
||||
AGIS=agi-test.agi eagi-test eagi-sphinx-test jukebox.agi
|
||||
CFLAGS+=
|
||||
|
||||
ifeq ($(OSARCH),SunOS)
|
||||
LIBS+=-lsocket -lnsl
|
||||
endif
|
||||
|
||||
include $(ASTTOPDIR)/Makefile.rules
|
||||
|
||||
_ASTCFLAGS+=-DSTANDALONE
|
||||
|
||||
all: $(AGIS)
|
||||
|
||||
strcompat.c: ../main/strcompat.c
|
||||
@cp $< $@
|
||||
|
||||
eagi-test: eagi-test.o strcompat.o
|
||||
|
||||
eagi-sphinx-test: eagi-sphinx-test.o
|
||||
all: depend $(AGIS)
|
||||
|
||||
install: all
|
||||
mkdir -p $(DESTDIR)$(AGI_DIR)
|
||||
for x in $(AGIS); do $(INSTALL) -m 755 $$x $(DESTDIR)$(AGI_DIR) ; done
|
||||
|
||||
uninstall:
|
||||
for x in $(AGIS); do rm -f $(DESTDIR)$(AGI_DIR)/$$x ; done
|
||||
eagi-test: eagi-test.o
|
||||
$(CC) $(CFLAGS) -o eagi-test eagi-test.o
|
||||
|
||||
eagi-sphinx-test: eagi-sphinx-test.o
|
||||
$(CC) $(CFLAGS) -o eagi-sphinx-test eagi-sphinx-test.o
|
||||
|
||||
clean:
|
||||
rm -f *.so *.o look eagi-test eagi-sphinx-test
|
||||
rm -f .*.d *.s *.i
|
||||
rm -f strcompat.c
|
||||
rm -f *.so *.o look .depend eagi-test eagi-sphinx-test
|
||||
|
||||
ifneq ($(wildcard .*.d),)
|
||||
include .*.d
|
||||
%.so : %.o
|
||||
$(CC) -shared -Xlinker -x -o $@ $<
|
||||
|
||||
ifneq ($(wildcard .depend),)
|
||||
include .depend
|
||||
endif
|
||||
|
||||
depend: .depend
|
||||
|
||||
.depend:
|
||||
../mkdep $(CFLAGS) `ls *.c`
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/compat.h"
|
||||
|
||||
#define AUDIO_FILENO (STDERR_FILENO + 1)
|
||||
|
||||
#define SPHINX_HOST "192.168.1.108"
|
||||
@@ -70,9 +66,7 @@ static int read_environment(void)
|
||||
char *val;
|
||||
/* Read environment */
|
||||
for(;;) {
|
||||
if (!fgets(buf, sizeof(buf), stdin)) {
|
||||
return -1;
|
||||
}
|
||||
fgets(buf, sizeof(buf), stdin);
|
||||
if (feof(stdin))
|
||||
return -1;
|
||||
buf[strlen(buf) - 1] = '\0';
|
||||
@@ -123,9 +117,7 @@ static char *wait_result(void)
|
||||
return NULL;
|
||||
}
|
||||
if (FD_ISSET(STDIN_FILENO, &fds)) {
|
||||
if (!fgets(astresp, sizeof(astresp), stdin)) {
|
||||
return NULL;
|
||||
}
|
||||
fgets(astresp, sizeof(astresp), stdin);
|
||||
if (feof(stdin)) {
|
||||
fprintf(stderr, "Got hungup on apparently\n");
|
||||
return NULL;
|
||||
@@ -136,10 +128,9 @@ static char *wait_result(void)
|
||||
}
|
||||
if (FD_ISSET(AUDIO_FILENO, &fds)) {
|
||||
res = read(AUDIO_FILENO, audiobuf, sizeof(audiobuf));
|
||||
if ((res > 0) && (sphinx_sock > -1)) {
|
||||
if (write(sphinx_sock, audiobuf, res) < 0) {
|
||||
fprintf(stderr, "write() failed: %s\n", strerror(errno));
|
||||
}
|
||||
if (res > 0) {
|
||||
if (sphinx_sock > -1)
|
||||
write(sphinx_sock, audiobuf, res);
|
||||
}
|
||||
}
|
||||
if ((sphinx_sock > -1) && FD_ISSET(sphinx_sock, &fds)) {
|
||||
@@ -214,7 +205,7 @@ int main(int argc, char *argv[])
|
||||
connect_sphinx();
|
||||
tmp = getenv("agi_enhanced");
|
||||
if (tmp) {
|
||||
if (sscanf(tmp, "%30d.%30d", &ver, &subver) != 2)
|
||||
if (sscanf(tmp, "%d.%d", &ver, &subver) != 2)
|
||||
ver = 0;
|
||||
}
|
||||
if (ver < 1) {
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/compat.h"
|
||||
|
||||
#define AUDIO_FILENO (STDERR_FILENO + 1)
|
||||
|
||||
static int read_environment(void)
|
||||
@@ -24,9 +20,7 @@ static int read_environment(void)
|
||||
char *val;
|
||||
/* Read environment */
|
||||
for(;;) {
|
||||
if (!fgets(buf, sizeof(buf), stdin)) {
|
||||
return -1;
|
||||
}
|
||||
fgets(buf, sizeof(buf), stdin);
|
||||
if (feof(stdin))
|
||||
return -1;
|
||||
buf[strlen(buf) - 1] = '\0';
|
||||
@@ -70,9 +64,7 @@ static char *wait_result(void)
|
||||
return NULL;
|
||||
}
|
||||
if (FD_ISSET(STDIN_FILENO, &fds)) {
|
||||
if (!fgets(astresp, sizeof(astresp), stdin)) {
|
||||
return NULL;
|
||||
}
|
||||
fgets(astresp, sizeof(astresp), stdin);
|
||||
if (feof(stdin)) {
|
||||
fprintf(stderr, "Got hungup on apparently\n");
|
||||
return NULL;
|
||||
@@ -156,7 +148,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
tmp = getenv("agi_enhanced");
|
||||
if (tmp) {
|
||||
if (sscanf(tmp, "%30d.%30d", &ver, &subver) != 2)
|
||||
if (sscanf(tmp, "%d.%d", &ver, &subver) != 2)
|
||||
ver = 0;
|
||||
}
|
||||
if (ver < 1) {
|
||||
|
||||
488
agi/jukebox.agi
488
agi/jukebox.agi
@@ -1,488 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Jukebox 0.2
|
||||
#
|
||||
# A music manager for Asterisk.
|
||||
#
|
||||
# Copyright (C) 2005-2006, Justin Tunney
|
||||
#
|
||||
# Justin Tunney <jesuscyborg@gmail.com>
|
||||
#
|
||||
# This program is free software, distributed under the terms of the
|
||||
# GNU General Public License v2.
|
||||
#
|
||||
# Keep it open source pigs
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
#
|
||||
# Uses festival to list off all your MP3 music files over a channel in
|
||||
# a hierarchical fashion. Put this file in your agi-bin folder which
|
||||
# is located at: /var/lib/asterisk/agi-bin Be sure to chmod +x it!
|
||||
#
|
||||
# Invocation Example:
|
||||
# exten => 68742,1,Answer()
|
||||
# exten => 68742,2,agi,jukebox.agi|/home/justin/Music
|
||||
# exten => 68742,3,Hangup()
|
||||
#
|
||||
# exten => 68742,1,Answer()
|
||||
# exten => 68742,2,agi,jukebox.agi|/home/justin/Music|pm
|
||||
# exten => 68742,3,Hangup()
|
||||
#
|
||||
# Options:
|
||||
# p - Precache text2wave outputs for every possible filename.
|
||||
# It is much better to set this option because if a caller
|
||||
# presses a key during a cache operation, it will be ignored.
|
||||
# m - Go back to menu after playing song
|
||||
# g - Do not play the greeting message
|
||||
#
|
||||
# Usage Instructions:
|
||||
# - Press '*' to go up a directory. If you are in the root music
|
||||
# folder you will be exitted from the script.
|
||||
# - If you have a really long list of files, you can filter the list
|
||||
# at any time by pressing '#' and spelling out a few letters you
|
||||
# expect the files to start with. For example, if you wanted to
|
||||
# know what extension 'Requiem For A Dream' was, you'd type:
|
||||
# '#737'. Note, phone keypads don't include Q and Z. Q is 7 and
|
||||
# Z is 9.
|
||||
#
|
||||
# Notes:
|
||||
# - This AGI script uses the MP3Player command which uses the
|
||||
# mpg123 Program. Grab yourself a copy of this program by
|
||||
# going to http://www.mpg123.de/cgi-bin/sitexplorer.cgi?/mpg123/
|
||||
# Be sure to download mpg123-0.59r.tar.gz because it is known to
|
||||
# work with Asterisk and hopefully isn't the release with that
|
||||
# awful security problem. If you're using Fedora Core 3 with
|
||||
# Alsa like me, make linux-alsa isn't going to work. Do make
|
||||
# linux-devel and you're peachy keen.
|
||||
#
|
||||
# - You won't get nifty STDERR debug messages if you're using a
|
||||
# remote asterisk shell.
|
||||
#
|
||||
# - For some reason, caching certain files will generate the
|
||||
# error: 'using default diphone ax-ax for y-pau'. Example:
|
||||
# # echo "Depeche Mode - CUW - 05 - The Meaning of Love" | text2wave -o /var/jukeboxcache/jukeboxcache/Depeche_Mode/Depeche_Mode_-_CUW_-_05_-_The_Meaning_of_Love.mp3.ul -otype ulaw -
|
||||
# The temporary work around is to just touch these files.
|
||||
#
|
||||
# - The background app doesn't like to get more than 2031 chars
|
||||
# of input.
|
||||
#
|
||||
|
||||
use strict;
|
||||
|
||||
$|=1;
|
||||
|
||||
# Setup some variables
|
||||
my %AGI; my $tests = 0; my $fail = 0; my $pass = 0;
|
||||
my @masterCacheList = ();
|
||||
my $maxNumber = 10;
|
||||
|
||||
while (<STDIN>) {
|
||||
chomp;
|
||||
last unless length($_);
|
||||
if (/^agi_(\w+)\:\s+(.*)$/) {
|
||||
$AGI{$1} = $2;
|
||||
}
|
||||
}
|
||||
|
||||
# setup options
|
||||
my $SHOWGREET = 1;
|
||||
my $PRECACHE = 0;
|
||||
my $MENUAFTERSONG = 0;
|
||||
|
||||
$PRECACHE = 1 if $ARGV[1] =~ /p/;
|
||||
$MENUAFTERSONG = 1 if $ARGV[1] =~ /m/;
|
||||
$SHOWGREET = 0 if $ARGV[1] =~ /g/;
|
||||
|
||||
# setup folders
|
||||
my $MUSIC = $ARGV[0];
|
||||
$MUSIC = &rmts($MUSIC);
|
||||
my $FESTIVALCACHE = "/var/jukeboxcache";
|
||||
if (! -e $FESTIVALCACHE) {
|
||||
`mkdir -p -m0776 $FESTIVALCACHE`;
|
||||
}
|
||||
|
||||
# make sure we have some essential files
|
||||
if (! -e "$FESTIVALCACHE/jukebox_greet.ul") {
|
||||
`echo "Welcome to the Asterisk Jukebox" | text2wave -o $FESTIVALCACHE/jukebox_greet.ul -otype ulaw -`;
|
||||
}
|
||||
if (! -e "$FESTIVALCACHE/jukebox_press.ul") {
|
||||
`echo "Press" | text2wave -o $FESTIVALCACHE/jukebox_press.ul -otype ulaw -`;
|
||||
}
|
||||
if (! -e "$FESTIVALCACHE/jukebox_for.ul") {
|
||||
`echo "For" | text2wave -o $FESTIVALCACHE/jukebox_for.ul -otype ulaw -`;
|
||||
}
|
||||
if (! -e "$FESTIVALCACHE/jukebox_toplay.ul") {
|
||||
`echo "To play" | text2wave -o $FESTIVALCACHE/jukebox_toplay.ul -otype ulaw -`;
|
||||
}
|
||||
if (! -e "$FESTIVALCACHE/jukebox_nonefound.ul") {
|
||||
`echo "There were no music files found in this folder" | text2wave -o $FESTIVALCACHE/jukebox_nonefound.ul -otype ulaw -`;
|
||||
}
|
||||
if (! -e "$FESTIVALCACHE/jukebox_percent.ul") {
|
||||
`echo "Percent" | text2wave -o $FESTIVALCACHE/jukebox_percent.ul -otype ulaw -`;
|
||||
}
|
||||
if (! -e "$FESTIVALCACHE/jukebox_generate.ul") {
|
||||
`echo "Please wait while Astrisk Jukebox cashes the files of your music collection" | text2wave -o $FESTIVALCACHE/jukebox_generate.ul -otype ulaw -`;
|
||||
}
|
||||
if (! -e "$FESTIVALCACHE/jukebox_invalid.ul") {
|
||||
`echo "You have entered an invalid selection" | text2wave -o $FESTIVALCACHE/jukebox_invalid.ul -otype ulaw -`;
|
||||
}
|
||||
if (! -e "$FESTIVALCACHE/jukebox_thankyou.ul") {
|
||||
`echo "Thank you for using Astrisk Jukebox, Goodbye" | text2wave -o $FESTIVALCACHE/jukebox_thankyou.ul -otype ulaw -`;
|
||||
}
|
||||
|
||||
# greet the user
|
||||
if ($SHOWGREET) {
|
||||
print "EXEC Playback \"$FESTIVALCACHE/jukebox_greet\"\n";
|
||||
my $result = <STDIN>; &check_result($result);
|
||||
}
|
||||
|
||||
# go through the directories
|
||||
music_dir_cache() if $PRECACHE;
|
||||
music_dir_menu('/');
|
||||
|
||||
exit 0;
|
||||
|
||||
##########################################################################
|
||||
|
||||
sub music_dir_menu {
|
||||
my $dir = shift;
|
||||
|
||||
# generate a list of mp3's and directories and assign each one it's
|
||||
# own selection number. Then make sure that we've got a sound clip
|
||||
# for the file name
|
||||
if (!opendir(THEDIR, rmts($MUSIC.$dir))) {
|
||||
print STDERR "Failed to open music directory: $dir\n";
|
||||
exit 1;
|
||||
}
|
||||
my @files = sort readdir THEDIR;
|
||||
my $cnt = 1;
|
||||
my @masterBgList = ();
|
||||
|
||||
foreach my $file (@files) {
|
||||
chomp($file);
|
||||
if ($file ne '.' && $file ne '..' && $file ne 'festivalcache') { # ignore special files
|
||||
my $real_version = &rmts($MUSIC.$dir).'/'.$file;
|
||||
my $cache_version = &rmts($FESTIVALCACHE.$dir).'/'.$file.'.ul';
|
||||
my $cache_version2 = &rmts($FESTIVALCACHE.$dir).'/'.$file;
|
||||
my $cache_version_esc = &clean_file($cache_version);
|
||||
my $cache_version2_esc = &clean_file($cache_version2);
|
||||
|
||||
if (-d $real_version) {
|
||||
# 0:id 1:type 2:text2wav-file 3:for-filtering 4:the-directory 5:text2wav echo
|
||||
push(@masterBgList, [$cnt++, 1, $cache_version2_esc, &remove_special_chars($file), $file, "for the $file folder"]);
|
||||
} elsif ($real_version =~ /\.mp3$/) {
|
||||
# 0:id 1:type 2:text2wav-file 3:for-filtering 4:the-mp3
|
||||
push(@masterBgList, [$cnt++, 2, $cache_version2_esc, &remove_special_chars($file), $real_version, "to play $file"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
close(THEDIR);
|
||||
|
||||
my @filterList = @masterBgList;
|
||||
|
||||
if (@filterList == 0) {
|
||||
print "EXEC Playback \"$FESTIVALCACHE/jukebox_nonefound\"\n";
|
||||
my $result = <STDIN>; &check_result($result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
MYCONTINUE:
|
||||
|
||||
# play bg selections and figure out their selection
|
||||
my $digit = '';
|
||||
my $digitstr = '';
|
||||
for (my $n=0; $n<@filterList; $n++) {
|
||||
&cache_speech(&remove_file_extension($filterList[$n][5]), "$filterList[$n][2].ul") if ! -e "$filterList[$n][2].ul";
|
||||
&cache_speech("Press $filterList[$n][0]", "$FESTIVALCACHE/jukebox_$filterList[$n][0].ul") if ! -e "$FESTIVALCACHE/jukebox_$filterList[$n][0].ul";
|
||||
print "EXEC Background \"$filterList[$n][2]&$FESTIVALCACHE/jukebox_$filterList[$n][0]\"\n";
|
||||
my $result = <STDIN>;
|
||||
$digit = &check_result($result);
|
||||
if ($digit > 0) {
|
||||
$digitstr .= chr($digit);
|
||||
last;
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
print "WAIT FOR DIGIT 3000\n";
|
||||
my $result = <STDIN>;
|
||||
$digit = &check_result($result);
|
||||
last if $digit <= 0;
|
||||
$digitstr .= chr($digit);
|
||||
}
|
||||
|
||||
# see if it's a valid selection
|
||||
print STDERR "Digits Entered: '$digitstr'\n";
|
||||
exit 0 if $digitstr eq '';
|
||||
my $found = 0;
|
||||
goto EXITSUB if $digitstr =~ /\*/;
|
||||
|
||||
# filter the list
|
||||
if ($digitstr =~ /^\#\d+/) {
|
||||
my $regexp = '';
|
||||
for (my $n=1; $n<length($digitstr); $n++) {
|
||||
my $d = substr($digitstr, $n, 1);
|
||||
if ($d == 2) {
|
||||
$regexp .= '[abc]';
|
||||
} elsif ($d == 3) {
|
||||
$regexp .= '[def]';
|
||||
} elsif ($d == 4) {
|
||||
$regexp .= '[ghi]';
|
||||
} elsif ($d == 5) {
|
||||
$regexp .= '[jkl]';
|
||||
} elsif ($d == 6) {
|
||||
$regexp .= '[mno]';
|
||||
} elsif ($d == 7) {
|
||||
$regexp .= '[pqrs]';
|
||||
} elsif ($d == 8) {
|
||||
$regexp .= '[tuv]';
|
||||
} elsif ($d == 9) {
|
||||
$regexp .= '[wxyz]';
|
||||
}
|
||||
}
|
||||
@filterList = ();
|
||||
for (my $n=1; $n<@masterBgList; $n++) {
|
||||
push(@filterList, $masterBgList[$n]) if $masterBgList[$n][3] =~ /^$regexp/i;
|
||||
}
|
||||
goto MYCONTINUE;
|
||||
}
|
||||
|
||||
for (my $n=0; $n<@masterBgList; $n++) {
|
||||
if ($digitstr == $masterBgList[$n][0]) {
|
||||
if ($masterBgList[$n][1] == 1) { # a folder
|
||||
&music_dir_menu(rmts($dir).'/'.$masterBgList[$n][4]);
|
||||
@filterList = @masterBgList;
|
||||
goto MYCONTINUE;
|
||||
} elsif ($masterBgList[$n][1] == 2) { # a file
|
||||
# because *'s scripting language is crunk and won't allow us to escape
|
||||
# funny filenames, we need to create a temporary symlink to the mp3
|
||||
# file
|
||||
my $mp3 = &escape_file($masterBgList[$n][4]);
|
||||
my $link = `mktemp`;
|
||||
chomp($link);
|
||||
$link .= '.mp3';
|
||||
print STDERR "ln -s $mp3 $link\n";
|
||||
my $cmdr = `ln -s $mp3 $link`;
|
||||
chomp($cmdr);
|
||||
print "Failed to create symlink to mp3: $cmdr\n" if $cmdr ne '';
|
||||
|
||||
print "EXEC MP3Player \"$link\"\n";
|
||||
my $result = <STDIN>; &check_result($result);
|
||||
|
||||
`rm $link`;
|
||||
|
||||
if (!$MENUAFTERSONG) {
|
||||
print "EXEC Playback \"$FESTIVALCACHE/jukebox_thankyou\"\n";
|
||||
my $result = <STDIN>; &check_result($result);
|
||||
exit 0;
|
||||
} else {
|
||||
goto MYCONTINUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
print "EXEC Playback \"$FESTIVALCACHE/jukebox_invalid\"\n";
|
||||
my $result = <STDIN>; &check_result($result);
|
||||
}
|
||||
EXITSUB:
|
||||
}
|
||||
|
||||
sub cache_speech {
|
||||
my $speech = shift;
|
||||
my $file = shift;
|
||||
|
||||
my $theDir = extract_file_dir($file);
|
||||
`mkdir -p -m0776 $theDir`;
|
||||
|
||||
print STDERR "echo \"$speech\" | text2wave -o $file -otype ulaw -\n";
|
||||
my $cmdr = `echo "$speech" | text2wave -o $file -otype ulaw -`;
|
||||
chomp($cmdr);
|
||||
if ($cmdr =~ /using default diphone/) {
|
||||
# temporary bug work around....
|
||||
`touch $file`;
|
||||
} elsif ($cmdr ne '') {
|
||||
print STDERR "Command Failed\n";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
sub music_dir_cache {
|
||||
# generate list of text2speech files to generate
|
||||
if (!music_dir_cache_genlist('/')) {
|
||||
print STDERR "Horrible Dreadful Error: No Music Found in $MUSIC!";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# add to list how many 'number' files we have to generate. We can't
|
||||
# use the SayNumber app in Asterisk because we want to chain all
|
||||
# talking in one Background command. We also want a consistent
|
||||
# voice...
|
||||
for (my $n=1; $n<=$maxNumber; $n++) {
|
||||
push(@masterCacheList, [3, "Press $n", "$FESTIVALCACHE/jukebox_$n.ul"]) if ! -e "$FESTIVALCACHE/jukebox_$n.ul";
|
||||
}
|
||||
|
||||
# now generate all these darn text2speech files
|
||||
if (@masterCacheList > 5) {
|
||||
print "EXEC Playback \"$FESTIVALCACHE/jukebox_generate\"\n";
|
||||
my $result = <STDIN>; &check_result($result);
|
||||
}
|
||||
my $theTime = time();
|
||||
for (my $n=0; $n < @masterCacheList; $n++) {
|
||||
my $cmdr = '';
|
||||
if ($masterCacheList[$n][0] == 1) { # directory
|
||||
&cache_speech("for folder $masterCacheList[$n][1]", $masterCacheList[$n][2]);
|
||||
} elsif ($masterCacheList[$n][0] == 2) { # file
|
||||
&cache_speech("to play $masterCacheList[$n][1]", $masterCacheList[$n][2]);
|
||||
} elsif ($masterCacheList[$n][0] == 3) { # number
|
||||
&cache_speech($masterCacheList[$n][1], $masterCacheList[$n][2]);
|
||||
}
|
||||
if (time() >= $theTime + 30) {
|
||||
my $percent = int($n / @masterCacheList * 100);
|
||||
print "SAY NUMBER $percent \"\"\n";
|
||||
my $result = <STDIN>; &check_result($result);
|
||||
print "EXEC Playback \"$FESTIVALCACHE/jukebox_percent\"\n";
|
||||
my $result = <STDIN>; &check_result($result);
|
||||
$theTime = time();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# this function will fill the @masterCacheList of all the files that
|
||||
# need to have text2speeced ulaw files of their names generated
|
||||
sub music_dir_cache_genlist {
|
||||
my $dir = shift;
|
||||
if (!opendir(THEDIR, rmts($MUSIC.$dir))) {
|
||||
print STDERR "Failed to open music directory: $dir\n";
|
||||
exit 1;
|
||||
}
|
||||
my @files = sort readdir THEDIR;
|
||||
my $foundFiles = 0;
|
||||
my $tmpMaxNum = 0;
|
||||
foreach my $file (@files) {
|
||||
chomp;
|
||||
if ($file ne '.' && $file ne '..' && $file ne 'festivalcache') { # ignore special files
|
||||
my $real_version = &rmts($MUSIC.$dir).'/'.$file;
|
||||
my $cache_version = &rmts($FESTIVALCACHE.$dir).'/'.$file.'.ul';
|
||||
my $cache_version2 = &rmts($FESTIVALCACHE.$dir).'/'.$file;
|
||||
my $cache_version_esc = &clean_file($cache_version);
|
||||
my $cache_version2_esc = &clean_file($cache_version2);
|
||||
|
||||
if (-d $real_version) {
|
||||
if (music_dir_cache_genlist(rmts($dir).'/'.$file)) {
|
||||
$tmpMaxNum++;
|
||||
$maxNumber = $tmpMaxNum if $tmpMaxNum > $maxNumber;
|
||||
push(@masterCacheList, [1, $file, $cache_version_esc]) if ! -e $cache_version_esc;
|
||||
$foundFiles = 1;
|
||||
}
|
||||
} elsif ($real_version =~ /\.mp3$/) {
|
||||
$tmpMaxNum++;
|
||||
$maxNumber = $tmpMaxNum if $tmpMaxNum > $maxNumber;
|
||||
push(@masterCacheList, [2, &remove_file_extension($file), $cache_version_esc]) if ! -e $cache_version_esc;
|
||||
$foundFiles = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
close(THEDIR);
|
||||
return $foundFiles;
|
||||
}
|
||||
|
||||
sub rmts { # remove trailing slash
|
||||
my $hog = shift;
|
||||
$hog =~ s/\/$//;
|
||||
return $hog;
|
||||
}
|
||||
|
||||
sub extract_file_name {
|
||||
my $hog = shift;
|
||||
$hog =~ /\/?([^\/]+)$/;
|
||||
return $1;
|
||||
}
|
||||
|
||||
sub extract_file_dir {
|
||||
my $hog = shift;
|
||||
return $hog if ! ($hog =~ /\//);
|
||||
$hog =~ /(.*)\/[^\/]*$/;
|
||||
return $1;
|
||||
}
|
||||
|
||||
sub remove_file_extension {
|
||||
my $hog = shift;
|
||||
return $hog if ! ($hog =~ /\./);
|
||||
$hog =~ /(.*)\.[^.]*$/;
|
||||
return $1;
|
||||
}
|
||||
|
||||
sub clean_file {
|
||||
my $hog = shift;
|
||||
$hog =~ s/\\/_/g;
|
||||
$hog =~ s/ /_/g;
|
||||
$hog =~ s/\t/_/g;
|
||||
$hog =~ s/\'/_/g;
|
||||
$hog =~ s/\"/_/g;
|
||||
$hog =~ s/\(/_/g;
|
||||
$hog =~ s/\)/_/g;
|
||||
$hog =~ s/&/_/g;
|
||||
$hog =~ s/\[/_/g;
|
||||
$hog =~ s/\]/_/g;
|
||||
$hog =~ s/\$/_/g;
|
||||
$hog =~ s/\|/_/g;
|
||||
$hog =~ s/\^/_/g;
|
||||
return $hog;
|
||||
}
|
||||
|
||||
sub remove_special_chars {
|
||||
my $hog = shift;
|
||||
$hog =~ s/\\//g;
|
||||
$hog =~ s/ //g;
|
||||
$hog =~ s/\t//g;
|
||||
$hog =~ s/\'//g;
|
||||
$hog =~ s/\"//g;
|
||||
$hog =~ s/\(//g;
|
||||
$hog =~ s/\)//g;
|
||||
$hog =~ s/&//g;
|
||||
$hog =~ s/\[//g;
|
||||
$hog =~ s/\]//g;
|
||||
$hog =~ s/\$//g;
|
||||
$hog =~ s/\|//g;
|
||||
$hog =~ s/\^//g;
|
||||
return $hog;
|
||||
}
|
||||
|
||||
sub escape_file {
|
||||
my $hog = shift;
|
||||
$hog =~ s/\\/\\\\/g;
|
||||
$hog =~ s/ /\\ /g;
|
||||
$hog =~ s/\t/\\\t/g;
|
||||
$hog =~ s/\'/\\\'/g;
|
||||
$hog =~ s/\"/\\\"/g;
|
||||
$hog =~ s/\(/\\\(/g;
|
||||
$hog =~ s/\)/\\\)/g;
|
||||
$hog =~ s/&/\\&/g;
|
||||
$hog =~ s/\[/\\\[/g;
|
||||
$hog =~ s/\]/\\\]/g;
|
||||
$hog =~ s/\$/\\\$/g;
|
||||
$hog =~ s/\|/\\\|/g;
|
||||
$hog =~ s/\^/\\\^/g;
|
||||
return $hog;
|
||||
}
|
||||
|
||||
sub check_result {
|
||||
my ($res) = @_;
|
||||
my $retval;
|
||||
$tests++;
|
||||
chomp $res;
|
||||
if ($res =~ /^200/) {
|
||||
$res =~ /result=(-?\d+)/;
|
||||
if (!length($1)) {
|
||||
print STDERR "FAIL ($res)\n";
|
||||
$fail++;
|
||||
exit 1;
|
||||
} else {
|
||||
print STDERR "PASS ($1)\n";
|
||||
return $1;
|
||||
}
|
||||
} else {
|
||||
print STDERR "FAIL (unexpected result '$res')\n";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,17 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* u-Law to Signed linear conversion
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief u-Law to Signed linear conversion
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/alaw.h"
|
||||
#include <asterisk/alaw.h>
|
||||
|
||||
#define AMI_MASK 0x55
|
||||
|
||||
@@ -71,7 +55,7 @@ static inline short int alaw2linear (unsigned char alaw)
|
||||
int seg;
|
||||
|
||||
alaw ^= AMI_MASK;
|
||||
i = ((alaw & 0x0F) << 4) + 8 /* rounding error */;
|
||||
i = ((alaw & 0x0F) << 4);
|
||||
seg = (((int) alaw & 0x70) >> 4);
|
||||
if (seg)
|
||||
i = (i + 0x100) << (seg - 1);
|
||||
114
apps/Makefile
114
apps/Makefile
@@ -1,42 +1,104 @@
|
||||
#
|
||||
# Asterisk -- A telephony toolkit for Linux.
|
||||
#
|
||||
# Makefile for PBX applications
|
||||
# Makefile for PBX frontends (dynamically loaded)
|
||||
#
|
||||
# Copyright (C) 1999-2006, Digium, Inc.
|
||||
# Copyright (C) 1999, Mark Spencer
|
||||
#
|
||||
# Mark Spencer <markster@linux-support.net>
|
||||
#
|
||||
# This program is free software, distributed under the terms of
|
||||
# the GNU General Public License
|
||||
#
|
||||
|
||||
-include ../menuselect.makeopts ../menuselect.makedeps
|
||||
USE_MYSQL_VM_INTERFACE=0
|
||||
USE_POSTGRES_VM_INTERFACE=0
|
||||
|
||||
MENUSELECT_CATEGORY=APPS
|
||||
MENUSELECT_DESCRIPTION=Applications
|
||||
#APPS=app_dial.so app_playback.so app_directory.so app_intercom.so app_mp3.so
|
||||
APPS=app_dial.so app_playback.so app_voicemail.so app_directory.so app_mp3.so\
|
||||
app_system.so app_echo.so app_record.so app_image.so app_url.so app_disa.so \
|
||||
app_qcall.so app_adsiprog.so app_getcpeid.so app_milliwatt.so \
|
||||
app_zapateller.so app_setcallerid.so app_festival.so \
|
||||
app_queue.so app_senddtmf.so app_parkandannounce.so app_striplsd.so \
|
||||
app_setcidname.so app_lookupcidname.so app_substring.so app_macro.so \
|
||||
app_authenticate.so app_softhangup.so app_lookupblacklist.so \
|
||||
app_waitforring.so app_privacy.so app_db.so app_chanisavail.so \
|
||||
app_enumlookup.so app_transfer.so app_setcidnum.so app_cdr.so \
|
||||
app_hasnewvoicemail.so app_sayunixtime.so app_cut.so app_read.so \
|
||||
app_setcdruserfield.so app_random.so app_ices.so app_eval.so \
|
||||
app_nbscat.so app_sendtext.so app_exec.so app_sms.so \
|
||||
app_groupcount.so app_txtcidname.so app_controlplayback.so \
|
||||
app_talkdetect.so app_alarmreceiver.so app_userevent.so app_verbose.so \
|
||||
app_test.so app_forkcdr.so
|
||||
|
||||
ALL_C_MODS:=$(patsubst %.c,%,$(wildcard app_*.c))
|
||||
ALL_CC_MODS:=$(patsubst %.cc,%,$(wildcard app_*.cc))
|
||||
|
||||
C_MODS:=$(filter-out $(MENUSELECT_APPS),$(ALL_C_MODS))
|
||||
CC_MODS:=$(filter-out $(MENUSELECT_APPS),$(ALL_CC_MODS))
|
||||
|
||||
LOADABLE_MODS:=$(C_MODS) $(CC_MODS)
|
||||
|
||||
ifneq ($(findstring apps,$(MENUSELECT_EMBED)),)
|
||||
EMBEDDED_MODS:=$(LOADABLE_MODS)
|
||||
LOADABLE_MODS:=
|
||||
ifneq (${OSARCH},Darwin)
|
||||
APPS+=app_intercom.so
|
||||
endif
|
||||
|
||||
MENUSELECT_OPTS_app_directory:=$(MENUSELECT_OPTS_app_voicemail)
|
||||
ifneq ($(findstring ODBC_STORAGE,$(MENUSELECT_OPTS_app_voicemail)),)
|
||||
MENUSELECT_DEPENDS_app_voicemail+=$(MENUSELECT_DEPENDS_ODBC_STORAGE)
|
||||
MENUSELECT_DEPENDS_app_directory+=$(MENUSELECT_DEPENDS_ODBC_STORAGE)
|
||||
endif
|
||||
ifneq ($(findstring IMAP_STORAGE,$(MENUSELECT_OPTS_app_voicemail)),)
|
||||
MENUSELECT_DEPENDS_app_voicemail+=$(MENUSELECT_DEPENDS_IMAP_STORAGE)
|
||||
MENUSELECT_DEPENDS_app_directory+=$(MENUSELECT_DEPENDS_IMAP_STORAGE)
|
||||
#APPS+=app_sql_postgres.so
|
||||
#APPS+=app_sql_odbc.so
|
||||
#APPS+=app_rpt.so
|
||||
|
||||
APPS+=$(shell if [ -f /usr/include/linux/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi)
|
||||
APPS+=$(shell if [ -f /usr/local/include/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi)
|
||||
APPS+=$(shell if [ -f /usr/include/osp/osp.h ]; then echo "app_osplookup.so" ; fi)
|
||||
|
||||
CFLAGS+=-fPIC
|
||||
|
||||
ifeq ($(USE_POSTGRES_VM_INTERFACE),1)
|
||||
CFLAGS+=-DUSEPOSTGRESVM
|
||||
endif
|
||||
|
||||
all: _all
|
||||
ifeq ($(USE_MYSQL_VM_INTERFACE),1)
|
||||
CFLAGS+=-DUSEMYSQLVM
|
||||
endif
|
||||
|
||||
include $(ASTTOPDIR)/Makefile.moddir_rules
|
||||
all: $(APPS)
|
||||
|
||||
clean:
|
||||
rm -f *.so *.o look .depend
|
||||
|
||||
%.so : %.o
|
||||
$(CC) $(SOLINK) -o $@ $<
|
||||
|
||||
app_rpt.so : app_rpt.o
|
||||
$(CC) $(SOLINK) -o $@ $< -ltonezone
|
||||
|
||||
install: all
|
||||
for x in $(APPS); do $(INSTALL) -m 755 $$x $(DESTDIR)$(MODULES_DIR) ; done
|
||||
rm -f $(DESTDIR)$(MODULES_DIR)/app_datetime.so
|
||||
|
||||
app_voicemail.so : app_voicemail.o
|
||||
ifeq ($(USE_MYSQL_VM_INTERFACE),1)
|
||||
$(CC) $(SOLINK) -o $@ $(MLFLAGS) $< -L/usr/lib/mysql -lmysqlclient -lz
|
||||
else
|
||||
ifeq ($(USE_POSTGRES_VM_INTERFACE),1)
|
||||
$(CC) $(SOLINK) -o $@ $(MLFLAGS) $< -lpq
|
||||
else
|
||||
$(CC) $(SOLINK) -o $@ $(MLFLAGS) $<
|
||||
endif
|
||||
endif
|
||||
|
||||
app_sql_postgres.o: app_sql_postgres.c
|
||||
$(CC) -pipe -I/usr/local/pgsql/include $(CFLAGS) -c -o app_sql_postgres.o app_sql_postgres.c
|
||||
|
||||
app_sql_postgres.so: app_sql_postgres.o
|
||||
$(CC) $(SOLINK) -o $@ $< -L/usr/local/pgsql/lib -lpq
|
||||
|
||||
app_sql_odbc.so: app_sql_odbc.o
|
||||
$(CC) $(SOLINK) -o $@ $< -lodbc
|
||||
|
||||
look: look.c
|
||||
$(CC) -pipe -O6 -g look.c -o look -lncurses
|
||||
|
||||
ifneq ($(wildcard .depend),)
|
||||
include .depend
|
||||
endif
|
||||
|
||||
depend: .depend
|
||||
|
||||
.depend:
|
||||
../mkdep $(CFLAGS) `ls *.c`
|
||||
|
||||
env:
|
||||
env
|
||||
|
||||
@@ -1,39 +1,26 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Program Asterisk ADSI Scripts into phone
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Program Asterisk ADSI Scripts into phone
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>res_adsi</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/adsi.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
@@ -43,15 +30,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/adsi.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "../asterisk.h"
|
||||
#include "../astconf.h"
|
||||
|
||||
static char *tdesc = "Asterisk ADSI Programming Application";
|
||||
|
||||
static char *app = "ADSIProg";
|
||||
|
||||
@@ -60,8 +42,13 @@ static char *synopsis = "Load Asterisk ADSI Scripts into phone";
|
||||
/* #define DUMP_MESSAGES */
|
||||
|
||||
static char *descrip =
|
||||
" ADSIProg(script): This application programs an ADSI Phone with the given\n"
|
||||
"script. If nothing is specified, the default script (asterisk.adsi) is used.\n";
|
||||
" ADSIProg(script): Programs an ADSI Phone with the given script.\n"
|
||||
"If none is specified, the default is used. Returns 0 unless CPE\n"
|
||||
"is hungup.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
struct adsi_event {
|
||||
int id;
|
||||
@@ -172,9 +159,9 @@ struct adsi_script {
|
||||
struct adsi_flag flags[7];
|
||||
|
||||
/* Stuff from adsi script */
|
||||
unsigned char sec[5];
|
||||
char sec[5];
|
||||
char desc[19];
|
||||
unsigned char fdn[5];
|
||||
char fdn[5];
|
||||
int ver;
|
||||
};
|
||||
|
||||
@@ -195,7 +182,7 @@ static int process_token(void *out, char *src, int maxlen, int argtype)
|
||||
if (!(argtype & ARG_NUMBER))
|
||||
return -1;
|
||||
/* Octal value */
|
||||
if (sscanf(src, "%30o", (int *)out) != 1)
|
||||
if (sscanf(src, "%o", (int *)out) != 1)
|
||||
return -1;
|
||||
if (argtype & ARG_STRING) {
|
||||
/* Convert */
|
||||
@@ -205,7 +192,7 @@ static int process_token(void *out, char *src, int maxlen, int argtype)
|
||||
if (!(argtype & ARG_NUMBER))
|
||||
return -1;
|
||||
/* Hex value */
|
||||
if (sscanf(src + 2, "%30x", (unsigned int *)out) != 1)
|
||||
if (sscanf(src + 2, "%x", (unsigned int *)out) != 1)
|
||||
return -1;
|
||||
if (argtype & ARG_STRING) {
|
||||
/* Convert */
|
||||
@@ -215,7 +202,7 @@ static int process_token(void *out, char *src, int maxlen, int argtype)
|
||||
if (!(argtype & ARG_NUMBER))
|
||||
return -1;
|
||||
/* Hex value */
|
||||
if (sscanf(src, "%30d", (int *)out) != 1)
|
||||
if (sscanf(src, "%d", (int *)out) != 1)
|
||||
return -1;
|
||||
if (argtype & ARG_STRING) {
|
||||
/* Convert */
|
||||
@@ -413,7 +400,7 @@ static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, ch
|
||||
ast_log(LOG_WARNING, "No more flag space at line %d of %s\n", lineno, script);
|
||||
return NULL;
|
||||
}
|
||||
ast_copy_string(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname));
|
||||
strncpy(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname) - 1);
|
||||
state->flags[state->numflags].id = state->numflags + 1;
|
||||
state->numflags++;
|
||||
return &state->flags[state->numflags-1];
|
||||
@@ -516,7 +503,7 @@ static struct adsi_soft_key *getkeybyname(struct adsi_script *state, char *name,
|
||||
ast_log(LOG_WARNING, "No more key space at line %d of %s\n", lineno, script);
|
||||
return NULL;
|
||||
}
|
||||
ast_copy_string(state->keys[state->numkeys].vname, name, sizeof(state->keys[state->numkeys].vname));
|
||||
strncpy(state->keys[state->numkeys].vname, name, sizeof(state->keys[state->numkeys].vname) - 1);
|
||||
state->keys[state->numkeys].id = state->numkeys + 2;
|
||||
state->numkeys++;
|
||||
return &state->keys[state->numkeys-1];
|
||||
@@ -532,7 +519,7 @@ static struct adsi_subscript *getsubbyname(struct adsi_script *state, char *name
|
||||
ast_log(LOG_WARNING, "No more subscript space at line %d of %s\n", lineno, script);
|
||||
return NULL;
|
||||
}
|
||||
ast_copy_string(state->subs[state->numsubs].vname, name, sizeof(state->subs[state->numsubs].vname));
|
||||
strncpy(state->subs[state->numsubs].vname, name, sizeof(state->subs[state->numsubs].vname) - 1);
|
||||
state->subs[state->numsubs].id = state->numsubs;
|
||||
state->numsubs++;
|
||||
return &state->subs[state->numsubs-1];
|
||||
@@ -551,7 +538,7 @@ static struct adsi_state *getstatebyname(struct adsi_script *state, char *name,
|
||||
ast_log(LOG_WARNING, "No more state space at line %d of %s\n", lineno, script);
|
||||
return NULL;
|
||||
}
|
||||
ast_copy_string(state->states[state->numstates].vname, name, sizeof(state->states[state->numstates].vname));
|
||||
strncpy(state->states[state->numstates].vname, name, sizeof(state->states[state->numstates].vname) - 1);
|
||||
state->states[state->numstates].id = state->numstates + 1;
|
||||
state->numstates++;
|
||||
return &state->states[state->numstates-1];
|
||||
@@ -570,7 +557,7 @@ static struct adsi_display *getdisplaybyname(struct adsi_script *state, char *na
|
||||
ast_log(LOG_WARNING, "No more display space at line %d of %s\n", lineno, script);
|
||||
return NULL;
|
||||
}
|
||||
ast_copy_string(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname));
|
||||
strncpy(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname) - 1);
|
||||
state->displays[state->numdisplays].id = state->numdisplays + 1;
|
||||
state->numdisplays++;
|
||||
return &state->displays[state->numdisplays-1];
|
||||
@@ -1056,7 +1043,7 @@ static int adsi_process(struct adsi_script *state, char *buf, char *script, int
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ast_copy_string(tmp2, tmp, sizeof(tmp2));
|
||||
strncpy(tmp2, tmp, sizeof(tmp2) - 1);
|
||||
}
|
||||
if (strlen(tmp2) > 18) {
|
||||
ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script);
|
||||
@@ -1349,7 +1336,7 @@ static struct adsi_script *compile_script(char *script)
|
||||
int x, err;
|
||||
struct adsi_script *scr;
|
||||
if (script[0] == '/')
|
||||
ast_copy_string(fn, script, sizeof(fn));
|
||||
strncpy(fn, script, sizeof(fn) - 1);
|
||||
else
|
||||
snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, script);
|
||||
f = fopen(fn, "r");
|
||||
@@ -1357,16 +1344,17 @@ static struct adsi_script *compile_script(char *script)
|
||||
ast_log(LOG_WARNING, "Can't open file '%s'\n", fn);
|
||||
return NULL;
|
||||
}
|
||||
if (!(scr = ast_calloc(1, sizeof(*scr)))) {
|
||||
scr = malloc(sizeof(struct adsi_script));
|
||||
if (!scr) {
|
||||
fclose(f);
|
||||
ast_log(LOG_WARNING, "Out of memory loading script '%s'\n", fn);
|
||||
return NULL;
|
||||
}
|
||||
memset(scr, 0, sizeof(struct adsi_script));
|
||||
/* Create "main" as first subroutine */
|
||||
getsubbyname(scr, "main", NULL, 0);
|
||||
while(!feof(f)) {
|
||||
if (!fgets(buf, sizeof(buf), f)) {
|
||||
continue;
|
||||
}
|
||||
fgets(buf, sizeof(buf), f);
|
||||
if (!feof(f)) {
|
||||
lineno++;
|
||||
/* Trim off trailing return */
|
||||
@@ -1437,18 +1425,18 @@ static int adsi_prog(struct ast_channel *chan, char *script)
|
||||
{
|
||||
struct adsi_script *scr;
|
||||
int x;
|
||||
unsigned char buf[1024];
|
||||
char buf[1024];
|
||||
int bytes;
|
||||
scr = compile_script(script);
|
||||
if (!scr)
|
||||
return -1;
|
||||
|
||||
/* Start an empty ADSI Session */
|
||||
if (ast_adsi_load_session(chan, NULL, 0, 1) < 1)
|
||||
if (adsi_load_session(chan, NULL, 0, 1) < 1)
|
||||
return -1;
|
||||
|
||||
/* Now begin the download attempt */
|
||||
if (ast_adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
|
||||
if (adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
|
||||
/* User rejected us for some reason */
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "User rejected download attempt\n");
|
||||
@@ -1462,7 +1450,7 @@ static int adsi_prog(struct ast_channel *chan, char *script)
|
||||
for (x=0;x<scr->numkeys;x++) {
|
||||
if (bytes + scr->keys[x].retstrlen > 253) {
|
||||
/* Send what we've collected so far */
|
||||
if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
|
||||
return -1;
|
||||
}
|
||||
@@ -1475,7 +1463,7 @@ static int adsi_prog(struct ast_channel *chan, char *script)
|
||||
#endif
|
||||
}
|
||||
if (bytes) {
|
||||
if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
|
||||
return -1;
|
||||
}
|
||||
@@ -1486,7 +1474,7 @@ static int adsi_prog(struct ast_channel *chan, char *script)
|
||||
for (x=0;x<scr->numdisplays;x++) {
|
||||
if (bytes + scr->displays[x].datalen > 253) {
|
||||
/* Send what we've collected so far */
|
||||
if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
|
||||
return -1;
|
||||
}
|
||||
@@ -1499,7 +1487,7 @@ static int adsi_prog(struct ast_channel *chan, char *script)
|
||||
#endif
|
||||
}
|
||||
if (bytes) {
|
||||
if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
|
||||
return -1;
|
||||
}
|
||||
@@ -1510,7 +1498,7 @@ static int adsi_prog(struct ast_channel *chan, char *script)
|
||||
for (x=0;x<scr->numsubs;x++) {
|
||||
if (bytes + scr->subs[x].datalen > 253) {
|
||||
/* Send what we've collected so far */
|
||||
if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
|
||||
return -1;
|
||||
}
|
||||
@@ -1523,7 +1511,7 @@ static int adsi_prog(struct ast_channel *chan, char *script)
|
||||
#endif
|
||||
}
|
||||
if (bytes) {
|
||||
if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
|
||||
ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
|
||||
return -1;
|
||||
}
|
||||
@@ -1531,11 +1519,11 @@ static int adsi_prog(struct ast_channel *chan, char *script)
|
||||
|
||||
|
||||
bytes = 0;
|
||||
bytes += ast_adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
|
||||
bytes += ast_adsi_set_line(buf, ADSI_INFO_PAGE, 1);
|
||||
if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
|
||||
bytes += adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
|
||||
bytes += adsi_set_line(buf, ADSI_INFO_PAGE, 1);
|
||||
if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
|
||||
return -1;
|
||||
if (ast_adsi_end_download(chan)) {
|
||||
if (adsi_end_download(chan)) {
|
||||
/* Download failed for some reason */
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Download attempt failed\n");
|
||||
@@ -1544,21 +1532,18 @@ static int adsi_prog(struct ast_channel *chan, char *script)
|
||||
return -1;
|
||||
}
|
||||
free(scr);
|
||||
ast_adsi_unload_session(chan);
|
||||
adsi_unload_session(chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adsi_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
struct ast_module_user *u;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (ast_strlen_zero(data))
|
||||
struct localuser *u;
|
||||
if (!data || ast_strlen_zero(data))
|
||||
data = "asterisk.adsi";
|
||||
|
||||
if (!ast_adsi_available(chan)) {
|
||||
LOCAL_USER_ADD(u);
|
||||
if (!adsi_available(chan)) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "ADSI Unavailable on CPE. Not bothering to try.\n");
|
||||
} else {
|
||||
@@ -1566,27 +1551,34 @@ static int adsi_exec(struct ast_channel *chan, void *data)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "ADSI Available on CPE. Attempting Upload.\n");
|
||||
res = adsi_prog(chan, data);
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, adsi_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Asterisk ADSI Programming Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 2004 - 2005 Steve Rodgers
|
||||
* Central Station Alarm receiver for Ademco Contact ID
|
||||
*
|
||||
* Copyright (C) 2004 Steve Rodgers
|
||||
*
|
||||
* Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* \brief Central Station Alarm receiver for Ademco Contact ID
|
||||
* \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
|
||||
*
|
||||
* the GNU General Public License
|
||||
*
|
||||
* *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
|
||||
*
|
||||
* Use at your own risk. Please consult the GNU GPL license document included with Asterisk. *
|
||||
* Use at your own risk. Please consult the GNU GPL license document included with Asterisk details. *
|
||||
*
|
||||
* *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/ulaw.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/app.h>
|
||||
#include <asterisk/dsp.h>
|
||||
#include <asterisk/config.h>
|
||||
#include <asterisk/localtime.h>
|
||||
#include <asterisk/callerid.h>
|
||||
#include <asterisk/astdb.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -41,23 +41,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/ulaw.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/dsp.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/localtime.h"
|
||||
#include "asterisk/callerid.h"
|
||||
#include "asterisk/astdb.h"
|
||||
#include "asterisk/utils.h"
|
||||
|
||||
#define ALMRCV_CONFIG "alarmreceiver.conf"
|
||||
#define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
|
||||
|
||||
@@ -68,19 +51,19 @@ struct event_node{
|
||||
|
||||
typedef struct event_node event_node_t;
|
||||
|
||||
static char *tdesc = "Alarm Receiver for Asterisk";
|
||||
|
||||
static char *app = "AlarmReceiver";
|
||||
|
||||
static char *synopsis = "Provide support for receiving alarm reports from a burglar or fire alarm panel";
|
||||
static char *synopsis = "Provide support for receving alarm reports from a burglar or fire alarm panel";
|
||||
static char *descrip =
|
||||
" AlarmReceiver(): Only 1 signalling format is supported at this time: Ademco\n"
|
||||
"Contact ID. This application should be called whenever there is an alarm\n"
|
||||
"panel calling in to dump its events. The application will handshake with the\n"
|
||||
"alarm panel, and receive events, validate them, handshake them, and store them\n"
|
||||
"until the panel hangs up. Once the panel hangs up, the application will run the\n"
|
||||
"system command specified by the eventcmd setting in alarmreceiver.conf and pipe\n"
|
||||
"the events to the standard input of the application. The configuration file also\n"
|
||||
"contains settings for DTMF timing, and for the loudness of the acknowledgement\n"
|
||||
"tones.\n";
|
||||
"Alarm receiver application for Asterisk. Only 1 signalling format is supported at this time:\n"
|
||||
"Ademco Contact ID. This application should be called whenever there is an alarm panel calling in\n"
|
||||
"to dump its events. The application will handshake with the alarm panel, and receive events,\n"
|
||||
"validate them, handshake them, and store them until the panel hangs up. Once the panel hangs up,\n"
|
||||
"the application will run the command line specified by the eventcmd setting in alarmreceiver.conf\n"
|
||||
"and pipe the events to the standard input of the application. Alarmreceiver.conf also contains settings\n"
|
||||
"for DTMF timing, and for the loudness of the acknowledgement tones.\n";
|
||||
|
||||
/* Config Variables */
|
||||
|
||||
@@ -93,10 +76,18 @@ static char event_app[128] = {'\0'};
|
||||
static char db_family[128] = {'\0'};
|
||||
static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
|
||||
|
||||
|
||||
/* Misc variables */
|
||||
|
||||
|
||||
static char event_file[14] = "/event-XXXXXX";
|
||||
|
||||
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
/*
|
||||
* Attempt to access a database variable and increment it,
|
||||
* provided that the user defined db-family in alarmreceiver.conf
|
||||
@@ -112,7 +103,7 @@ static void database_increment( char *key )
|
||||
char value[16];
|
||||
|
||||
|
||||
if (ast_strlen_zero(db_family))
|
||||
if(!strlen(db_family))
|
||||
return; /* If not defined, don't do anything */
|
||||
|
||||
res = ast_db_get(db_family, key, value, sizeof(value) - 1);
|
||||
@@ -125,7 +116,7 @@ static void database_increment( char *key )
|
||||
return;
|
||||
}
|
||||
|
||||
sscanf(value, "%30u", &v);
|
||||
sscanf(value, "%u", &v);
|
||||
v++;
|
||||
|
||||
if(option_verbose >= 4)
|
||||
@@ -136,7 +127,7 @@ static void database_increment( char *key )
|
||||
res = ast_db_put(db_family, key, value);
|
||||
|
||||
if((res)&&(option_verbose >= 4))
|
||||
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: database_increment write error\n");
|
||||
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: database_increment write error");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -206,7 +197,6 @@ static int send_tone_burst(struct ast_channel *chan, float freq, int duration, i
|
||||
|
||||
i += wf.datalen / 8;
|
||||
if (i > duration) {
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
if (ast_write(chan, &wf)){
|
||||
@@ -214,7 +204,6 @@ static int send_tone_burst(struct ast_channel *chan, float freq, int duration, i
|
||||
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Failed to write frame on %s\n", chan->name);
|
||||
ast_log(LOG_WARNING, "AlarmReceiver Failed to write frame on %s\n",chan->name);
|
||||
res = -1;
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -224,6 +213,20 @@ static int send_tone_burst(struct ast_channel *chan, float freq, int duration, i
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the difference in milliseconds between two timeval structs
|
||||
*/
|
||||
|
||||
static int ms_diff(struct timeval *tv1, struct timeval *tv2){
|
||||
|
||||
int ms;
|
||||
|
||||
ms = (tv1->tv_sec - tv2->tv_sec) * 1000;
|
||||
ms += (tv1->tv_usec - tv2->tv_usec) / 1000;
|
||||
|
||||
return(ms);
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive a string of DTMF digits where the length of the digit string is known in advance. Do not give preferential
|
||||
* treatment to any digit value, and allow separate time out values to be specified for the first digit and all subsequent
|
||||
@@ -241,12 +244,14 @@ static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int
|
||||
int i = 0;
|
||||
int r;
|
||||
struct ast_frame *f;
|
||||
struct timeval lastdigittime;
|
||||
struct timeval now, lastdigittime;
|
||||
|
||||
lastdigittime = ast_tvnow();
|
||||
gettimeofday(&lastdigittime,NULL);
|
||||
for(;;){
|
||||
gettimeofday(&now,NULL);
|
||||
|
||||
/* if outa time, leave */
|
||||
if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) >
|
||||
if (ms_diff(&now,&lastdigittime) >
|
||||
((i > 0) ? sdto : fdto)){
|
||||
if(option_verbose >= 4)
|
||||
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: DTMF Digit Timeout on %s\n", chan->name);
|
||||
@@ -291,7 +296,7 @@ static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int
|
||||
if(i >= length)
|
||||
break;
|
||||
|
||||
lastdigittime = ast_tvnow();
|
||||
gettimeofday(&lastdigittime,NULL);
|
||||
}
|
||||
|
||||
digit_string[i] = '\0'; /* Nul terminate the end of the digit string */
|
||||
@@ -313,8 +318,8 @@ static int write_metadata( FILE *logfile, char *signalling_type, struct ast_chan
|
||||
char timestamp[80];
|
||||
|
||||
/* Extract the caller ID location */
|
||||
if (chan->cid.cid_num)
|
||||
ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring));
|
||||
|
||||
strncpy(workstring, chan->callerid, sizeof(workstring) - 1);
|
||||
workstring[sizeof(workstring) - 1] = '\0';
|
||||
|
||||
ast_callerid_parse(workstring, &cn, &cl);
|
||||
@@ -389,11 +394,11 @@ static int log_events(struct ast_channel *chan, char *signalling_type, event_no
|
||||
FILE *logfile;
|
||||
event_node_t *elp = event;
|
||||
|
||||
if (!ast_strlen_zero(event_spool_dir)) {
|
||||
if(strlen(event_spool_dir)){
|
||||
|
||||
/* Make a template */
|
||||
|
||||
ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
|
||||
strncpy(workstring, event_spool_dir, sizeof(workstring) - 1);
|
||||
strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
|
||||
|
||||
/* Make the temporary file */
|
||||
@@ -513,7 +518,7 @@ static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int
|
||||
ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
|
||||
|
||||
if(!got_some_digits){
|
||||
got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
|
||||
got_some_digits = (strlen(event)) ? 1 : 0;
|
||||
ack_retries++;
|
||||
}
|
||||
continue;
|
||||
@@ -549,12 +554,13 @@ static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int
|
||||
|
||||
checksum = checksum % 15;
|
||||
|
||||
if (checksum) {
|
||||
if(checksum){
|
||||
database_increment("checksum-errors");
|
||||
if (option_verbose >= 2)
|
||||
if(option_verbose >= 2){
|
||||
ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Nonzero checksum\n");
|
||||
ast_log(LOG_DEBUG, "AlarmReceiver: Nonzero checksum\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the message type for correctness */
|
||||
@@ -571,14 +577,20 @@ static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int
|
||||
|
||||
events_received++;
|
||||
|
||||
/* Queue the Event */
|
||||
if (!(enew = ast_calloc(1, sizeof(*enew)))) {
|
||||
/* Queue the Event */
|
||||
|
||||
if((enew = malloc(sizeof(event_node_t))) == NULL){
|
||||
if(option_verbose >= 1)
|
||||
ast_verbose(VERBOSE_PREFIX_1 "AlarmReceiver: Failed to allocate memory\n");
|
||||
ast_log(LOG_WARNING, "AlarmReceiver Failed to allocate memory\n");
|
||||
res = -1;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
memset(enew, 0, sizeof(event_node_t));
|
||||
|
||||
enew->next = NULL;
|
||||
ast_copy_string(enew->data, event, sizeof(enew->data));
|
||||
strncpy(enew->data, event, sizeof(enew->data) - 1);
|
||||
|
||||
/*
|
||||
* Insert event onto end of list
|
||||
@@ -626,13 +638,13 @@ static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int
|
||||
static int alarmreceiver_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_module_user *u;
|
||||
struct localuser *u;
|
||||
event_node_t *elp, *efree;
|
||||
char signalling_type[64] = "";
|
||||
|
||||
event_node_t *event_head = NULL;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
/* Set write and read formats to ULAW */
|
||||
|
||||
@@ -641,19 +653,17 @@ static int alarmreceiver_exec(struct ast_channel *chan, void *data)
|
||||
|
||||
if (ast_set_write_format(chan,AST_FORMAT_ULAW)){
|
||||
ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_set_read_format(chan,AST_FORMAT_ULAW)){
|
||||
ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set default values for this invokation of the application */
|
||||
|
||||
ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
|
||||
strncpy(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type) - 1);
|
||||
|
||||
|
||||
/* Answer the channel if it is not already */
|
||||
@@ -666,7 +676,7 @@ static int alarmreceiver_exec(struct ast_channel *chan, void *data)
|
||||
res = ast_answer(chan);
|
||||
|
||||
if (res) {
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -705,7 +715,7 @@ static int alarmreceiver_exec(struct ast_channel *chan, void *data)
|
||||
* Do we exec a command line at the end?
|
||||
*/
|
||||
|
||||
if((!res) && (!ast_strlen_zero(event_app)) && (event_head)){
|
||||
if((!res) && (strlen(event_app)) && (event_head)){
|
||||
ast_log(LOG_DEBUG,"Alarmreceiver: executing: %s\n", event_app);
|
||||
ast_safe_system(event_app);
|
||||
}
|
||||
@@ -721,7 +731,7 @@ static int alarmreceiver_exec(struct ast_channel *chan, void *data)
|
||||
}
|
||||
|
||||
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -733,17 +743,16 @@ static int alarmreceiver_exec(struct ast_channel *chan, void *data)
|
||||
static int load_config(void)
|
||||
{
|
||||
struct ast_config *cfg;
|
||||
const char *p;
|
||||
char *p;
|
||||
|
||||
/* Read in the config file */
|
||||
|
||||
cfg = ast_config_load(ALMRCV_CONFIG);
|
||||
cfg = ast_load(ALMRCV_CONFIG);
|
||||
|
||||
if(!cfg){
|
||||
|
||||
if(option_verbose >= 4)
|
||||
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: No config file\n");
|
||||
return 0;
|
||||
}
|
||||
else{
|
||||
|
||||
@@ -751,7 +760,7 @@ static int load_config(void)
|
||||
p = ast_variable_retrieve(cfg, "general", "eventcmd");
|
||||
|
||||
if(p){
|
||||
ast_copy_string(event_app, p, sizeof(event_app));
|
||||
strncpy(event_app, p, sizeof(event_app) - 1);
|
||||
event_app[sizeof(event_app) - 1] = '\0';
|
||||
}
|
||||
|
||||
@@ -791,26 +800,26 @@ static int load_config(void)
|
||||
p = ast_variable_retrieve(cfg, "general", "eventspooldir");
|
||||
|
||||
if(p){
|
||||
ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
|
||||
strncpy(event_spool_dir, p, sizeof(event_spool_dir) - 1);
|
||||
event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
|
||||
}
|
||||
|
||||
p = ast_variable_retrieve(cfg, "general", "timestampformat");
|
||||
|
||||
if(p){
|
||||
ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
|
||||
strncpy(time_stamp_format, p, sizeof(time_stamp_format) - 1);
|
||||
time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
|
||||
}
|
||||
|
||||
p = ast_variable_retrieve(cfg, "general", "db-family");
|
||||
|
||||
if(p){
|
||||
ast_copy_string(db_family, p, sizeof(db_family));
|
||||
strncpy(db_family, p, sizeof(db_family) - 1);
|
||||
db_family[sizeof(db_family) - 1] = '\0';
|
||||
}
|
||||
ast_config_destroy(cfg);
|
||||
ast_destroy(cfg);
|
||||
}
|
||||
return 1;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
@@ -819,23 +828,31 @@ static int load_config(void)
|
||||
*/
|
||||
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
load_config();
|
||||
return ast_register_application(app, alarmreceiver_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
char *key()
|
||||
{
|
||||
if(load_config())
|
||||
return ast_register_application(app, alarmreceiver_exec, synopsis, descrip);
|
||||
else
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");
|
||||
|
||||
432
apps/app_amd.c
432
apps/app_amd.c
@@ -1,432 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2003 - 2006, Aheeva Technology.
|
||||
*
|
||||
* Claude Klimos (claude.klimos@aheeva.com)
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* A license has been granted to Digium (via disclaimer) for the use of
|
||||
* this code.
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/dsp.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/app.h"
|
||||
|
||||
|
||||
static char *app = "AMD";
|
||||
static char *synopsis = "Attempts to detect answering machines";
|
||||
static char *descrip =
|
||||
" AMD([initialSilence][|greeting][|afterGreetingSilence][|totalAnalysisTime]\n"
|
||||
" [|minimumWordLength][|betweenWordsSilence][|maximumNumberOfWords]\n"
|
||||
" [|silenceThreshold])\n"
|
||||
" This application attempts to detect answering machines at the beginning\n"
|
||||
" of outbound calls. Simply call this application after the call\n"
|
||||
" has been answered (outbound only, of course).\n"
|
||||
" When loaded, AMD reads amd.conf and uses the parameters specified as\n"
|
||||
" default values. Those default values get overwritten when calling AMD\n"
|
||||
" with parameters.\n"
|
||||
"- 'initialSilence' is the maximum silence duration before the greeting. If\n"
|
||||
" exceeded then MACHINE.\n"
|
||||
"- 'greeting' is the maximum length of a greeting. If exceeded then MACHINE.\n"
|
||||
"- 'afterGreetingSilence' is the silence after detecting a greeting.\n"
|
||||
" If exceeded then HUMAN.\n"
|
||||
"- 'totalAnalysisTime' is the maximum time allowed for the algorithm to decide\n"
|
||||
" on a HUMAN or MACHINE.\n"
|
||||
"- 'minimumWordLength'is the minimum duration of Voice to considered as a word.\n"
|
||||
"- 'betweenWordsSilence' is the minimum duration of silence after a word to \n"
|
||||
" consider the audio that follows as a new word.\n"
|
||||
"- 'maximumNumberOfWords'is the maximum number of words in the greeting. \n"
|
||||
" If exceeded then MACHINE.\n"
|
||||
"- 'silenceThreshold' is the silence threshold.\n"
|
||||
"This application sets the following channel variable upon completion:\n"
|
||||
" AMDSTATUS - This is the status of the answering machine detection.\n"
|
||||
" Possible values are:\n"
|
||||
" MACHINE | HUMAN | NOTSURE | HANGUP\n"
|
||||
" AMDCAUSE - Indicates the cause that led to the conclusion.\n"
|
||||
" Possible values are:\n"
|
||||
" TOOLONG-<%d total_time>\n"
|
||||
" INITIALSILENCE-<%d silenceDuration>-<%d initialSilence>\n"
|
||||
" HUMAN-<%d silenceDuration>-<%d afterGreetingSilence>\n"
|
||||
" MAXWORDS-<%d wordsCount>-<%d maximumNumberOfWords>\n"
|
||||
" LONGGREETING-<%d voiceDuration>-<%d greeting>\n";
|
||||
|
||||
#define STATE_IN_WORD 1
|
||||
#define STATE_IN_SILENCE 2
|
||||
|
||||
/* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */
|
||||
static int dfltInitialSilence = 2500;
|
||||
static int dfltGreeting = 1500;
|
||||
static int dfltAfterGreetingSilence = 800;
|
||||
static int dfltTotalAnalysisTime = 5000;
|
||||
static int dfltMinimumWordLength = 100;
|
||||
static int dfltBetweenWordsSilence = 50;
|
||||
static int dfltMaximumNumberOfWords = 3;
|
||||
static int dfltSilenceThreshold = 256;
|
||||
|
||||
/* Set to the lowest ms value provided in amd.conf or application parameters */
|
||||
static int dfltMaxWaitTimeForFrame = 50;
|
||||
|
||||
static void isAnsweringMachine(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_frame *f = NULL;
|
||||
struct ast_dsp *silenceDetector = NULL;
|
||||
int dspsilence = 0, readFormat, framelength = 0;
|
||||
int inInitialSilence = 1;
|
||||
int inGreeting = 0;
|
||||
int voiceDuration = 0;
|
||||
int silenceDuration = 0;
|
||||
int iTotalTime = 0;
|
||||
int iWordsCount = 0;
|
||||
int currentState = STATE_IN_WORD;
|
||||
int previousState = STATE_IN_SILENCE;
|
||||
int consecutiveVoiceDuration = 0;
|
||||
char amdCause[256] = "", amdStatus[256] = "";
|
||||
char *parse = ast_strdupa(data);
|
||||
|
||||
/* Lets 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. */
|
||||
int initialSilence = dfltInitialSilence;
|
||||
int greeting = dfltGreeting;
|
||||
int afterGreetingSilence = dfltAfterGreetingSilence;
|
||||
int totalAnalysisTime = dfltTotalAnalysisTime;
|
||||
int minimumWordLength = dfltMinimumWordLength;
|
||||
int betweenWordsSilence = dfltBetweenWordsSilence;
|
||||
int maximumNumberOfWords = dfltMaximumNumberOfWords;
|
||||
int silenceThreshold = dfltSilenceThreshold;
|
||||
int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(argInitialSilence);
|
||||
AST_APP_ARG(argGreeting);
|
||||
AST_APP_ARG(argAfterGreetingSilence);
|
||||
AST_APP_ARG(argTotalAnalysisTime);
|
||||
AST_APP_ARG(argMinimumWordLength);
|
||||
AST_APP_ARG(argBetweenWordsSilence);
|
||||
AST_APP_ARG(argMaximumNumberOfWords);
|
||||
AST_APP_ARG(argSilenceThreshold);
|
||||
);
|
||||
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD: %s %s %s (Fmt: %d)\n", chan->name ,chan->cid.cid_ani, chan->cid.cid_rdnis, chan->readformat);
|
||||
|
||||
/* Lets parse the arguments. */
|
||||
if (!ast_strlen_zero(parse)) {
|
||||
/* Some arguments have been passed. Lets parse them and overwrite the defaults. */
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
if (!ast_strlen_zero(args.argInitialSilence))
|
||||
initialSilence = atoi(args.argInitialSilence);
|
||||
if (!ast_strlen_zero(args.argGreeting))
|
||||
greeting = atoi(args.argGreeting);
|
||||
if (!ast_strlen_zero(args.argAfterGreetingSilence))
|
||||
afterGreetingSilence = atoi(args.argAfterGreetingSilence);
|
||||
if (!ast_strlen_zero(args.argTotalAnalysisTime))
|
||||
totalAnalysisTime = atoi(args.argTotalAnalysisTime);
|
||||
if (!ast_strlen_zero(args.argMinimumWordLength))
|
||||
minimumWordLength = atoi(args.argMinimumWordLength);
|
||||
if (!ast_strlen_zero(args.argBetweenWordsSilence))
|
||||
betweenWordsSilence = atoi(args.argBetweenWordsSilence);
|
||||
if (!ast_strlen_zero(args.argMaximumNumberOfWords))
|
||||
maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
|
||||
if (!ast_strlen_zero(args.argSilenceThreshold))
|
||||
silenceThreshold = atoi(args.argSilenceThreshold);
|
||||
} else if (option_debug)
|
||||
ast_log(LOG_DEBUG, "AMD using the default parameters.\n");
|
||||
|
||||
/* Find lowest ms value, that will be max wait time for a frame */
|
||||
if (maxWaitTimeForFrame > initialSilence)
|
||||
maxWaitTimeForFrame = initialSilence;
|
||||
if (maxWaitTimeForFrame > greeting)
|
||||
maxWaitTimeForFrame = greeting;
|
||||
if (maxWaitTimeForFrame > afterGreetingSilence)
|
||||
maxWaitTimeForFrame = afterGreetingSilence;
|
||||
if (maxWaitTimeForFrame > totalAnalysisTime)
|
||||
maxWaitTimeForFrame = totalAnalysisTime;
|
||||
if (maxWaitTimeForFrame > minimumWordLength)
|
||||
maxWaitTimeForFrame = minimumWordLength;
|
||||
if (maxWaitTimeForFrame > betweenWordsSilence)
|
||||
maxWaitTimeForFrame = betweenWordsSilence;
|
||||
|
||||
/* Now we're ready to roll! */
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
|
||||
"totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n",
|
||||
initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
|
||||
minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold );
|
||||
|
||||
/* Set read format to signed linear so we get signed linear frames in */
|
||||
readFormat = chan->readformat;
|
||||
if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0 ) {
|
||||
ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", chan->name );
|
||||
pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
|
||||
pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create a new DSP that will detect the silence */
|
||||
if (!(silenceDetector = ast_dsp_new())) {
|
||||
ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", chan->name );
|
||||
pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
|
||||
pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set silence threshold to specified value */
|
||||
ast_dsp_set_threshold(silenceDetector, silenceThreshold);
|
||||
|
||||
/* Now we go into a loop waiting for frames from the channel */
|
||||
while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
|
||||
|
||||
/* If we fail to read in a frame, that means they hung up */
|
||||
if (!(f = ast_read(chan))) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD: HANGUP\n");
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Got hangup\n");
|
||||
strcpy(amdStatus, "HANGUP");
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) {
|
||||
/* If the total time exceeds the analysis time then give up as we are not too sure */
|
||||
if (f->frametype == AST_FRAME_VOICE) {
|
||||
framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
|
||||
} else {
|
||||
framelength = 2 * maxWaitTimeForFrame;
|
||||
}
|
||||
|
||||
iTotalTime += framelength;
|
||||
if (iTotalTime >= totalAnalysisTime) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name );
|
||||
ast_frfree(f);
|
||||
strcpy(amdStatus , "NOTSURE");
|
||||
sprintf(amdCause , "TOOLONG-%d", iTotalTime);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Feed the frame of audio into the silence detector and see if we get a result */
|
||||
if (f->frametype != AST_FRAME_VOICE)
|
||||
dspsilence += 2 * maxWaitTimeForFrame;
|
||||
else {
|
||||
dspsilence = 0;
|
||||
ast_dsp_silence(silenceDetector, f, &dspsilence);
|
||||
}
|
||||
|
||||
if (dspsilence > 0) {
|
||||
silenceDuration = dspsilence;
|
||||
|
||||
if (silenceDuration >= betweenWordsSilence) {
|
||||
if (currentState != STATE_IN_SILENCE ) {
|
||||
previousState = currentState;
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD: Changed state to STATE_IN_SILENCE\n");
|
||||
}
|
||||
currentState = STATE_IN_SILENCE;
|
||||
consecutiveVoiceDuration = 0;
|
||||
}
|
||||
|
||||
if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
|
||||
silenceDuration, initialSilence);
|
||||
ast_frfree(f);
|
||||
strcpy(amdStatus , "MACHINE");
|
||||
sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD: HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
|
||||
silenceDuration, afterGreetingSilence);
|
||||
ast_frfree(f);
|
||||
strcpy(amdStatus , "HUMAN");
|
||||
sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
consecutiveVoiceDuration += framelength;
|
||||
voiceDuration += framelength;
|
||||
|
||||
/* If I have enough consecutive voice to say that I am in a Word, I can only increment the
|
||||
number of words if my previous state was Silence, which means that I moved into a word. */
|
||||
if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
|
||||
iWordsCount++;
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD: Word detected. iWordsCount:%d\n", iWordsCount);
|
||||
previousState = currentState;
|
||||
currentState = STATE_IN_WORD;
|
||||
}
|
||||
|
||||
if (iWordsCount >= maximumNumberOfWords) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: iWordsCount:%d\n", iWordsCount);
|
||||
ast_frfree(f);
|
||||
strcpy(amdStatus , "MACHINE");
|
||||
sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (inGreeting == 1 && voiceDuration >= greeting) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", voiceDuration, greeting);
|
||||
ast_frfree(f);
|
||||
strcpy(amdStatus , "MACHINE");
|
||||
sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (voiceDuration >= minimumWordLength ) {
|
||||
silenceDuration = 0;
|
||||
inInitialSilence = 0;
|
||||
inGreeting = 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
/* It took too long to get a frame back. Giving up. */
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name);
|
||||
strcpy(amdStatus , "NOTSURE");
|
||||
sprintf(amdCause , "TOOLONG-%d", iTotalTime);
|
||||
}
|
||||
|
||||
/* Set the status and cause on the channel */
|
||||
pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
|
||||
pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
|
||||
|
||||
/* Restore channel read format */
|
||||
if (readFormat && ast_set_read_format(chan, readFormat))
|
||||
ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name);
|
||||
|
||||
/* Free the DSP used to detect silence */
|
||||
ast_dsp_free(silenceDetector);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static int amd_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_module_user *u = NULL;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
isAnsweringMachine(chan, data);
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void load_config(void)
|
||||
{
|
||||
struct ast_config *cfg = NULL;
|
||||
char *cat = NULL;
|
||||
struct ast_variable *var = NULL;
|
||||
|
||||
if (!(cfg = ast_config_load("amd.conf"))) {
|
||||
ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cat = ast_category_browse(cfg, NULL);
|
||||
|
||||
while (cat) {
|
||||
if (!strcasecmp(cat, "general") ) {
|
||||
var = ast_variable_browse(cfg, cat);
|
||||
while (var) {
|
||||
if (!strcasecmp(var->name, "initial_silence")) {
|
||||
dfltInitialSilence = atoi(var->value);
|
||||
} else if (!strcasecmp(var->name, "greeting")) {
|
||||
dfltGreeting = atoi(var->value);
|
||||
} else if (!strcasecmp(var->name, "after_greeting_silence")) {
|
||||
dfltAfterGreetingSilence = atoi(var->value);
|
||||
} else if (!strcasecmp(var->name, "silence_threshold")) {
|
||||
dfltSilenceThreshold = atoi(var->value);
|
||||
} else if (!strcasecmp(var->name, "total_analysis_time")) {
|
||||
dfltTotalAnalysisTime = atoi(var->value);
|
||||
} else if (!strcasecmp(var->name, "min_word_length")) {
|
||||
dfltMinimumWordLength = atoi(var->value);
|
||||
} else if (!strcasecmp(var->name, "between_words_silence")) {
|
||||
dfltBetweenWordsSilence = atoi(var->value);
|
||||
} else if (!strcasecmp(var->name, "maximum_number_of_words")) {
|
||||
dfltMaximumNumberOfWords = atoi(var->value);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
|
||||
app, cat, var->name, var->lineno);
|
||||
}
|
||||
var = var->next;
|
||||
}
|
||||
}
|
||||
cat = ast_category_browse(cfg, cat);
|
||||
}
|
||||
|
||||
ast_config_destroy(cfg);
|
||||
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
|
||||
"totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n",
|
||||
dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
|
||||
dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_module_user_hangup_all();
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
load_config();
|
||||
return ast_register_application(app, amd_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
static int reload(void)
|
||||
{
|
||||
load_config();
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.reload = reload,
|
||||
);
|
||||
@@ -1,254 +1,172 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Execute arbitrary authenticate commands
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Execute arbitrary authenticate commands
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/app.h>
|
||||
#include <asterisk/astdb.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/astdb.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/options.h"
|
||||
|
||||
enum {
|
||||
OPT_ACCOUNT = (1 << 0),
|
||||
OPT_DATABASE = (1 << 1),
|
||||
OPT_JUMP = (1 << 2),
|
||||
OPT_MULTIPLE = (1 << 3),
|
||||
OPT_REMOVE = (1 << 4),
|
||||
} auth_option_flags;
|
||||
|
||||
AST_APP_OPTIONS(auth_app_options, {
|
||||
AST_APP_OPTION('a', OPT_ACCOUNT),
|
||||
AST_APP_OPTION('d', OPT_DATABASE),
|
||||
AST_APP_OPTION('j', OPT_JUMP),
|
||||
AST_APP_OPTION('m', OPT_MULTIPLE),
|
||||
AST_APP_OPTION('r', OPT_REMOVE),
|
||||
});
|
||||
|
||||
static char *tdesc = "Authentication Application";
|
||||
|
||||
static char *app = "Authenticate";
|
||||
|
||||
static char *synopsis = "Authenticate a user";
|
||||
|
||||
static char *descrip =
|
||||
" Authenticate(password[|options[|maxdigits]]): This application asks the caller\n"
|
||||
"to enter a given password in order to continue dialplan execution. If the password\n"
|
||||
"begins with the '/' character, it is interpreted as a file which contains a list of\n"
|
||||
"valid passwords, listed 1 password per line in the file.\n"
|
||||
" When using a database key, the value associated with the key can be anything.\n"
|
||||
"Users have three attempts to authenticate before the channel is hung up. If the\n"
|
||||
"passsword is invalid, the 'j' option is specified, and priority n+101 exists,\n"
|
||||
"dialplan execution will continnue at this location.\n"
|
||||
" Options:\n"
|
||||
" a - Set the channels' account code to the password that is entered\n"
|
||||
" d - Interpret the given path as database key, not a literal file\n"
|
||||
" j - Support jumping to n+101 if authentication fails\n"
|
||||
" m - Interpret the given path as a file which contains a list of account\n"
|
||||
" codes and password hashes delimited with ':', listed one per line in\n"
|
||||
" the file. When one of the passwords is matched, the channel will have\n"
|
||||
" its account code set to the corresponding account code in the file.\n"
|
||||
" r - Remove the database key upon successful entry (valid with 'd' only)\n"
|
||||
" maxdigits - maximum acceptable number of digits. Stops reading after\n"
|
||||
" maxdigits have been entered (without requiring the user to\n"
|
||||
" press the '#' key).\n"
|
||||
" Defaults to 0 - no limit - wait for the user press the '#' key.\n"
|
||||
;
|
||||
" Authenticate(password[|options]): Requires a user to enter a"
|
||||
"given password in order to continue execution. If the\n"
|
||||
"password begins with the '/' character, it is interpreted as\n"
|
||||
"a file which contains a list of valid passwords (1 per line).\n"
|
||||
"an optional set of opions may be provided by concatenating any\n"
|
||||
"of the following letters:\n"
|
||||
" a - Set account code to the password that is entered\n"
|
||||
" d - Interpret path as database key, not literal file\n"
|
||||
" r - Remove database key upon successful entry (valid with 'd' only)\n"
|
||||
"\n"
|
||||
"When using a database key, the value associated with the key can be\n"
|
||||
"anything.\n"
|
||||
"Returns 0 if the user enters a valid password within three\n"
|
||||
"tries, or -1 otherwise (or on hangup).\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int auth_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
int retries;
|
||||
struct ast_module_user *u;
|
||||
struct localuser *u;
|
||||
char password[256]="";
|
||||
char passwd[256];
|
||||
char *opts;
|
||||
char *prompt;
|
||||
int maxdigits;
|
||||
char *argcopy =NULL;
|
||||
struct ast_flags flags = {0};
|
||||
|
||||
AST_DECLARE_APP_ARGS(arglist,
|
||||
AST_APP_ARG(password);
|
||||
AST_APP_ARG(options);
|
||||
AST_APP_ARG(maxdigits);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
if (!data || ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
res = ast_answer(chan);
|
||||
if (res) {
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
argcopy = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(arglist,argcopy);
|
||||
|
||||
if (!ast_strlen_zero(arglist.options)) {
|
||||
ast_app_parse_options(auth_app_options, &flags, NULL, arglist.options);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(arglist.maxdigits)) {
|
||||
maxdigits = atoi(arglist.maxdigits);
|
||||
if ((maxdigits<1) || (maxdigits>sizeof(passwd)-2))
|
||||
maxdigits = sizeof(passwd) - 2;
|
||||
} else {
|
||||
maxdigits = sizeof(passwd) - 2;
|
||||
}
|
||||
|
||||
strncpy(password, data, sizeof(password) - 1);
|
||||
opts=strchr(password, '|');
|
||||
if (opts) {
|
||||
*opts = 0;
|
||||
opts++;
|
||||
} else
|
||||
opts = "";
|
||||
/* Start asking for password */
|
||||
prompt = "agent-pass";
|
||||
for (retries = 0; retries < 3; retries++) {
|
||||
res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0);
|
||||
res = ast_app_getdata(chan, prompt, passwd, sizeof(passwd) - 2, 0);
|
||||
if (res < 0)
|
||||
break;
|
||||
res = 0;
|
||||
if (arglist.password[0] == '/') {
|
||||
if (ast_test_flag(&flags,OPT_DATABASE)) {
|
||||
if (password[0] == '/') {
|
||||
if (strchr(opts, 'd')) {
|
||||
char tmp[256];
|
||||
/* Compare against a database key */
|
||||
if (!ast_db_get(arglist.password + 1, passwd, tmp, sizeof(tmp))) {
|
||||
if (!ast_db_get(password + 1, passwd, tmp, sizeof(tmp))) {
|
||||
/* It's a good password */
|
||||
if (ast_test_flag(&flags,OPT_REMOVE)) {
|
||||
ast_db_del(arglist.password + 1, passwd);
|
||||
if (strchr(opts, 'r')) {
|
||||
ast_db_del(password + 1, passwd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Compare against a file */
|
||||
FILE *f;
|
||||
f = fopen(arglist.password, "r");
|
||||
f = fopen(password, "r");
|
||||
if (f) {
|
||||
char buf[256] = "";
|
||||
char md5passwd[33] = "";
|
||||
char *md5secret = NULL;
|
||||
|
||||
while (!feof(f)) {
|
||||
if (!fgets(buf, sizeof(buf), f)) {
|
||||
continue;
|
||||
}
|
||||
if (!ast_strlen_zero(buf)) {
|
||||
size_t len = strlen(buf);
|
||||
if (buf[len - 1] == '\n')
|
||||
buf[len - 1] = '\0';
|
||||
if (ast_test_flag(&flags,OPT_MULTIPLE)) {
|
||||
md5secret = strchr(buf, ':');
|
||||
if (md5secret == NULL)
|
||||
continue;
|
||||
*md5secret = '\0';
|
||||
md5secret++;
|
||||
ast_md5_hash(md5passwd, passwd);
|
||||
if (!strcmp(md5passwd, md5secret)) {
|
||||
if (ast_test_flag(&flags,OPT_ACCOUNT))
|
||||
ast_cdr_setaccount(chan, buf);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!strcmp(passwd, buf)) {
|
||||
if (ast_test_flag(&flags,OPT_ACCOUNT))
|
||||
ast_cdr_setaccount(chan, buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(!feof(f)) {
|
||||
fgets(buf, sizeof(buf), f);
|
||||
if (!feof(f) && !ast_strlen_zero(buf)) {
|
||||
buf[strlen(buf) - 1] = '\0';
|
||||
if (!ast_strlen_zero(buf) && !strcmp(passwd, buf))
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
if (!ast_strlen_zero(buf)) {
|
||||
if (ast_test_flag(&flags,OPT_MULTIPLE)) {
|
||||
if (md5secret && !strcmp(md5passwd, md5secret))
|
||||
break;
|
||||
} else {
|
||||
if (!strcmp(passwd, buf))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ast_strlen_zero(buf) && !strcmp(passwd, buf))
|
||||
break;
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
|
||||
ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", password, strerror(errno));
|
||||
}
|
||||
} else {
|
||||
/* Compare against a fixed password */
|
||||
if (!strcmp(passwd, arglist.password))
|
||||
if (!strcmp(passwd, password))
|
||||
break;
|
||||
}
|
||||
prompt="auth-incorrect";
|
||||
}
|
||||
if ((retries < 3) && !res) {
|
||||
if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE))
|
||||
if (strchr(opts, 'a'))
|
||||
ast_cdr_setaccount(chan, passwd);
|
||||
res = ast_streamfile(chan, "auth-thankyou", chan->language);
|
||||
if (!res)
|
||||
res = ast_waitstream(chan, "");
|
||||
} else {
|
||||
if (ast_test_flag(&flags,OPT_JUMP) && ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101) == 0) {
|
||||
res = 0;
|
||||
} else {
|
||||
if (!ast_streamfile(chan, "vm-goodbye", chan->language))
|
||||
res = ast_waitstream(chan, "");
|
||||
res = -1;
|
||||
}
|
||||
if (!res)
|
||||
res = ast_streamfile(chan, "vm-goodbye", chan->language);
|
||||
if (!res)
|
||||
res = ast_waitstream(chan, "");
|
||||
res = -1;
|
||||
}
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, auth_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,78 +1,66 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Applictions connected with CDR engine
|
||||
*
|
||||
* Copyright (C) 2003, Digium
|
||||
*
|
||||
* Martin Pycko <martinp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Applications connected with CDR engine
|
||||
*
|
||||
* Martin Pycko <martinp@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/pbx.h"
|
||||
|
||||
static char *nocdr_descrip =
|
||||
" NoCDR(): This application will tell Asterisk not to maintain a CDR for the\n"
|
||||
"current call.\n";
|
||||
static char *tdesc = "Make sure asterisk doesn't save CDR for a certain call";
|
||||
|
||||
static char *nocdr_descrip = "NoCDR(): makes sure there won't be any CDR written for a certain call";
|
||||
static char *nocdr_app = "NoCDR";
|
||||
static char *nocdr_synopsis = "Tell Asterisk to not maintain a CDR for the current call";
|
||||
static char *nocdr_synopsis = "Make sure asterisk doesn't save CDR for a certain call";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int nocdr_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_module_user *u;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (chan->cdr) {
|
||||
ast_set_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED);
|
||||
ast_cdr_free(chan->cdr);
|
||||
chan->cdr = NULL;
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(nocdr_app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(nocdr_app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(nocdr_app, nocdr_exec, nocdr_synopsis, nocdr_descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Tell Asterisk to not maintain a CDR for the current call");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,104 +1,69 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
* James Golovich <james@gnuinter.net>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Check if Channel is Available
|
||||
*
|
||||
* \brief Check if Channel is Available
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
* \author James Golovich <james@gnuinter.net>
|
||||
|
||||
* \ingroup applications
|
||||
* Copyright (C) 2003, Digium
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
* James Golovich <james@gnuinter.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License
|
||||
*
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/app.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/devicestate.h"
|
||||
#include "asterisk/options.h"
|
||||
static char *tdesc = "Check if channel is available";
|
||||
|
||||
static char *app = "ChanIsAvail";
|
||||
|
||||
static char *synopsis = "Check channel availability";
|
||||
static char *synopsis = "Check if channel is available";
|
||||
|
||||
static char *descrip =
|
||||
" ChanIsAvail(Technology/resource[&Technology2/resource2...][|options]): \n"
|
||||
"This application will check to see if any of the specified channels are\n"
|
||||
"available. Note that the AVAILSTATUS variable is used for both device state\n"
|
||||
"and cause code. It is therefore possible for it to give a value that may\n"
|
||||
"indicate a device is available when it is not. It is suggested that the\n"
|
||||
"AVAILORIGCHAN variable is used instead to see whether a device is available\n"
|
||||
"or not.\n"
|
||||
"The following variables will be set by this application:\n"
|
||||
" ${AVAILCHAN} - the name of the available channel, if one exists\n"
|
||||
" ${AVAILORIGCHAN} - the canonical channel name that was used to create the channel\n"
|
||||
" ${AVAILSTATUS} - the status code for the available channel\n"
|
||||
" Options:\n"
|
||||
" s - Consider the channel unavailable if the channel is in use at all\n"
|
||||
" j - Support jumping to priority n+101 if no channel is available\n";
|
||||
" ChanIsAvail(Technology/resource[&Technology2/resource2...]): \n"
|
||||
"Checks is any of the requested channels are available. If none\n"
|
||||
"of the requested channels are available the new priority will be\n"
|
||||
"n+101 (unless such a priority does not exist or on error, in which\n"
|
||||
"case ChanIsAvail will return -1).\n"
|
||||
"If any of the requested channels are available, the next priority will be n+1,\n"
|
||||
"the channel variable ${AVAILCHAN} will be set to the name of the available channel\n"
|
||||
"and the ChanIsAvail app will return 0.\n"
|
||||
"${AVAILORIGCHAN} is the canonical channel name that was used to create the channel.\n"
|
||||
"${AVAILSTATUS} is the status code for the channel.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int chanavail_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=-1, inuse=-1, option_state=0, priority_jump=0;
|
||||
int status;
|
||||
struct ast_module_user *u;
|
||||
char *info, tmp[512], trychan[512], *peers, *tech, *number, *rest, *cur;
|
||||
int res=-1;
|
||||
struct localuser *u;
|
||||
char info[512], tmp[512], *peers, *tech, *number, *rest, *cur;
|
||||
struct ast_channel *tempchan;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(reqchans);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
if (!data) {
|
||||
ast_log(LOG_WARNING, "ChanIsAvail requires an argument (Zap/1&Zap/2)\n");
|
||||
return -1;
|
||||
}
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
info = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, info);
|
||||
|
||||
if (args.options) {
|
||||
if (strchr(args.options, 's'))
|
||||
option_state = 1;
|
||||
if (strchr(args.options, 'j'))
|
||||
priority_jump = 1;
|
||||
}
|
||||
peers = args.reqchans;
|
||||
strncpy(info, (char *)data, sizeof(info)-1);
|
||||
peers = info;
|
||||
if (peers) {
|
||||
cur = peers;
|
||||
do {
|
||||
@@ -112,67 +77,61 @@ static int chanavail_exec(struct ast_channel *chan, void *data)
|
||||
number = strchr(tech, '/');
|
||||
if (!number) {
|
||||
ast_log(LOG_WARNING, "ChanIsAvail argument takes format ([technology]/[device])\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
*number = '\0';
|
||||
number++;
|
||||
|
||||
if (option_state) {
|
||||
/* If the pbx says in use then don't bother trying further.
|
||||
This is to permit testing if someone's on a call, even if the
|
||||
channel can permit more calls (ie callwaiting, sip calls, etc). */
|
||||
|
||||
snprintf(trychan, sizeof(trychan), "%s/%s",cur,number);
|
||||
status = inuse = ast_device_state(trychan);
|
||||
}
|
||||
if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, number, &status))) {
|
||||
if ((tempchan = ast_request(tech, chan->nativeformats, number))) {
|
||||
pbx_builtin_setvar_helper(chan, "AVAILCHAN", tempchan->name);
|
||||
/* Store the originally used channel too */
|
||||
snprintf(tmp, sizeof(tmp), "%s/%s", tech, number);
|
||||
pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", tmp);
|
||||
snprintf(tmp, sizeof(tmp), "%d", status);
|
||||
pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
|
||||
ast_hangup(tempchan);
|
||||
tempchan = NULL;
|
||||
res = 1;
|
||||
break;
|
||||
} else {
|
||||
snprintf(tmp, sizeof(tmp), "%d", status);
|
||||
pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
|
||||
}
|
||||
cur = rest;
|
||||
} while (cur);
|
||||
}
|
||||
|
||||
if (res < 1) {
|
||||
pbx_builtin_setvar_helper(chan, "AVAILCHAN", "");
|
||||
pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", "");
|
||||
if (priority_jump || ast_opt_priority_jumping) {
|
||||
if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
|
||||
chan->priority+=100;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, chanavail_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Check channel availability");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2006, Sergey Basmanov
|
||||
*
|
||||
* 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 ChannelRedirect application
|
||||
*
|
||||
* \author Sergey Basmanov <sergey_basmanov@mail.ru>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/features.h"
|
||||
#include "asterisk/options.h"
|
||||
|
||||
static char *app = "ChannelRedirect";
|
||||
static char *synopsis = "Redirects given channel to a dialplan target.";
|
||||
static char *descrip =
|
||||
"ChannelRedirect(channel|[[context|]extension|]priority):\n"
|
||||
" Sends the specified channel to the specified extension priority\n";
|
||||
|
||||
|
||||
static int asyncgoto_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = -1;
|
||||
struct ast_module_user *u;
|
||||
char *info, *context, *exten, *priority;
|
||||
int prio = 1;
|
||||
struct ast_channel *chan2 = NULL;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(channel);
|
||||
AST_APP_ARG(label);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "%s requires an argument (channel|[[context|]exten|]priority)\n", app);
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
info = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, info);
|
||||
|
||||
if (ast_strlen_zero(args.channel) || ast_strlen_zero(args.label)) {
|
||||
ast_log(LOG_WARNING, "%s requires an argument (channel|[[context|]exten|]priority)\n", app);
|
||||
goto quit;
|
||||
}
|
||||
|
||||
chan2 = ast_get_channel_by_name_locked(args.channel);
|
||||
if (!chan2) {
|
||||
ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);
|
||||
goto quit;
|
||||
}
|
||||
|
||||
/* Parsed right to left, so standard parsing won't work */
|
||||
context = strsep(&args.label, "|");
|
||||
exten = strsep(&args.label, "|");
|
||||
if (exten) {
|
||||
priority = strsep(&args.label, "|");
|
||||
if (!priority) {
|
||||
priority = exten;
|
||||
exten = context;
|
||||
context = NULL;
|
||||
}
|
||||
} else {
|
||||
priority = context;
|
||||
context = NULL;
|
||||
}
|
||||
|
||||
/* ast_findlabel_extension does not convert numeric priorities; it only does a lookup */
|
||||
if (!(prio = atoi(priority)) && !(prio = ast_findlabel_extension(chan2, S_OR(context, chan2->context),
|
||||
S_OR(exten, chan2->exten), priority, chan2->cid.cid_num))) {
|
||||
ast_log(LOG_WARNING, "'%s' is not a known priority or label\n", priority);
|
||||
goto chanquit;
|
||||
}
|
||||
|
||||
if (option_debug > 1)
|
||||
ast_log(LOG_DEBUG, "Attempting async goto (%s) to %s|%s|%d\n", args.channel, S_OR(context, chan2->context), S_OR(exten, chan2->exten), prio);
|
||||
|
||||
if (chan2->pbx) {
|
||||
ast_channel_lock(chan2);
|
||||
ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
|
||||
ast_channel_unlock(chan2);
|
||||
}
|
||||
if (ast_async_goto_if_exists(chan2, S_OR(context, chan2->context), S_OR(exten, chan2->exten), prio))
|
||||
ast_log(LOG_WARNING, "%s failed for %s\n", app, args.channel);
|
||||
else
|
||||
res = 0;
|
||||
|
||||
chanquit:
|
||||
ast_mutex_unlock(&chan2->lock);
|
||||
quit:
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, asyncgoto_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel Redirect");
|
||||
@@ -1,887 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
|
||||
* Copyright (C) 2005 - 2008, Digium, Inc.
|
||||
*
|
||||
* A license has been granted to Digium (via disclaimer) for the use of
|
||||
* this code.
|
||||
*
|
||||
* 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 ChanSpy: Listen in on any channel.
|
||||
*
|
||||
* \author Anthony Minessale II <anthmct@yahoo.com>
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
* \author Russell Bryant <russell@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/audiohook.h"
|
||||
#include "asterisk/features.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/say.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
|
||||
#define AST_NAME_STRLEN 256
|
||||
|
||||
/* "Zap/pseudo" is ten characters.
|
||||
* "DAHDI/pseudo" is twelve characters.
|
||||
*/
|
||||
|
||||
static const char *tdesc = "Listen to a channel, and optionally whisper into it";
|
||||
static const char *app_chan = "ChanSpy";
|
||||
static const char *desc_chan =
|
||||
" ChanSpy([chanprefix][|options]): This application is used to listen to the\n"
|
||||
"audio from an Asterisk channel. This includes the audio coming in and\n"
|
||||
"out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
|
||||
"only channels beginning with this string will be spied upon.\n"
|
||||
" While spying, the following actions may be performed:\n"
|
||||
" - Dialing # cycles the volume level.\n"
|
||||
" - Dialing * will stop spying and look for another channel to spy on.\n"
|
||||
" - Dialing a series of digits followed by # builds a channel name to append\n"
|
||||
" to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
|
||||
" the digits '1234#' while spying will begin spying on the channel\n"
|
||||
" 'Agent/1234'.\n"
|
||||
" Options:\n"
|
||||
" b - Only spy on channels involved in a bridged call.\n"
|
||||
" g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
|
||||
" contain 'grp'.\n"
|
||||
" q - Don't play a beep when beginning to spy on a channel, or speak the\n"
|
||||
" selected channel name.\n"
|
||||
" r[(basename)] - Record the session to the monitor spool directory. An\n"
|
||||
" optional base for the filename may be specified. The\n"
|
||||
" default is 'chanspy'.\n"
|
||||
" v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
|
||||
" negative value refers to a quieter setting.\n"
|
||||
" w - Enable 'whisper' mode, so the spying channel can talk to\n"
|
||||
" the spied-on channel.\n"
|
||||
" W - Enable 'private whisper' mode, so the spying channel can\n"
|
||||
" talk to the spied-on channel but cannot listen to that\n"
|
||||
" channel.\n"
|
||||
;
|
||||
|
||||
static const char *app_ext = "ExtenSpy";
|
||||
static const char *desc_ext =
|
||||
" ExtenSpy(exten[@context][|options]): This application is used to listen to the\n"
|
||||
"audio from an Asterisk channel. This includes the audio coming in and\n"
|
||||
"out of the channel being spied on. Only channels created by outgoing calls for the\n"
|
||||
"specified extension will be selected for spying. If the optional context is not\n"
|
||||
"supplied, the current channel's context will be used.\n"
|
||||
" While spying, the following actions may be performed:\n"
|
||||
" - Dialing # cycles the volume level.\n"
|
||||
" - Dialing * will stop spying and look for another channel to spy on.\n"
|
||||
" Options:\n"
|
||||
" b - Only spy on channels involved in a bridged call.\n"
|
||||
" g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
|
||||
" contain 'grp'.\n"
|
||||
" q - Don't play a beep when beginning to spy on a channel, or speak the\n"
|
||||
" selected channel name.\n"
|
||||
" r[(basename)] - Record the session to the monitor spool directory. An\n"
|
||||
" optional base for the filename may be specified. The\n"
|
||||
" default is 'chanspy'.\n"
|
||||
" v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
|
||||
" negative value refers to a quieter setting.\n"
|
||||
" w - Enable 'whisper' mode, so the spying channel can talk to\n"
|
||||
" the spied-on channel.\n"
|
||||
" W - Enable 'private whisper' mode, so the spying channel can\n"
|
||||
" talk to the spied-on channel but cannot listen to that\n"
|
||||
" channel.\n"
|
||||
;
|
||||
|
||||
enum {
|
||||
OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
|
||||
OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
|
||||
OPTION_VOLUME = (1 << 2), /* Specify initial volume */
|
||||
OPTION_GROUP = (1 << 3), /* Only look at channels in group */
|
||||
OPTION_RECORD = (1 << 4),
|
||||
OPTION_WHISPER = (1 << 5),
|
||||
OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
|
||||
} chanspy_opt_flags;
|
||||
|
||||
enum {
|
||||
OPT_ARG_VOLUME = 0,
|
||||
OPT_ARG_GROUP,
|
||||
OPT_ARG_RECORD,
|
||||
OPT_ARG_ARRAY_SIZE,
|
||||
} chanspy_opt_args;
|
||||
|
||||
AST_APP_OPTIONS(spy_opts, {
|
||||
AST_APP_OPTION('q', OPTION_QUIET),
|
||||
AST_APP_OPTION('b', OPTION_BRIDGED),
|
||||
AST_APP_OPTION('w', OPTION_WHISPER),
|
||||
AST_APP_OPTION('W', OPTION_PRIVATE),
|
||||
AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
|
||||
AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
|
||||
AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
|
||||
});
|
||||
|
||||
static int next_unique_id_to_use = 0;
|
||||
|
||||
struct chanspy_translation_helper {
|
||||
/* spy data */
|
||||
struct ast_audiohook spy_audiohook;
|
||||
struct ast_audiohook whisper_audiohook;
|
||||
int fd;
|
||||
int volfactor;
|
||||
};
|
||||
|
||||
static void *spy_alloc(struct ast_channel *chan, void *data)
|
||||
{
|
||||
/* just store the data pointer in the channel structure */
|
||||
return data;
|
||||
}
|
||||
|
||||
static void spy_release(struct ast_channel *chan, void *data)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
|
||||
{
|
||||
struct chanspy_translation_helper *csth = data;
|
||||
struct ast_frame *f, *cur;
|
||||
|
||||
ast_audiohook_lock(&csth->spy_audiohook);
|
||||
if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_audiohook_unlock(&csth->spy_audiohook);
|
||||
return -1;
|
||||
}
|
||||
|
||||
f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
|
||||
|
||||
ast_audiohook_unlock(&csth->spy_audiohook);
|
||||
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
|
||||
if (ast_write(chan, cur)) {
|
||||
ast_frfree(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (csth->fd) {
|
||||
if (write(csth->fd, cur->data, cur->datalen) < 0) {
|
||||
ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ast_frfree(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ast_generator spygen = {
|
||||
.alloc = spy_alloc,
|
||||
.release = spy_release,
|
||||
.generate = spy_generate,
|
||||
};
|
||||
|
||||
static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
|
||||
{
|
||||
int res;
|
||||
struct ast_channel *peer;
|
||||
|
||||
ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
|
||||
|
||||
ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
|
||||
|
||||
res = ast_audiohook_attach(chan, audiohook);
|
||||
|
||||
if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
|
||||
ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
struct chanspy_ds {
|
||||
struct ast_channel *chan;
|
||||
char unique_id[20];
|
||||
ast_mutex_t lock;
|
||||
};
|
||||
|
||||
static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
|
||||
int *volfactor, int fd, const struct ast_flags *flags)
|
||||
{
|
||||
struct chanspy_translation_helper csth;
|
||||
int running = 0, res, x = 0;
|
||||
char inp[24] = {0};
|
||||
char *name;
|
||||
struct ast_frame *f;
|
||||
struct ast_silence_generator *silgen = NULL;
|
||||
struct ast_channel *spyee = NULL;
|
||||
const char *spyer_name;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
spyer_name = ast_strdupa(chan->name);
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
ast_mutex_lock(&spyee_chanspy_ds->lock);
|
||||
while ((spyee = spyee_chanspy_ds->chan) && ast_channel_trylock(spyee)) {
|
||||
/* avoid a deadlock here, just in case spyee is masqueraded and
|
||||
* chanspy_ds_chan_fixup() is called with the channel locked */
|
||||
DEADLOCK_AVOIDANCE(&spyee_chanspy_ds->lock);
|
||||
}
|
||||
ast_mutex_unlock(&spyee_chanspy_ds->lock);
|
||||
|
||||
if (!spyee)
|
||||
return 0;
|
||||
|
||||
/* We now hold the channel lock on spyee */
|
||||
|
||||
if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
|
||||
ast_channel_unlock(spyee);
|
||||
return 0;
|
||||
}
|
||||
|
||||
name = ast_strdupa(spyee->name);
|
||||
if (option_verbose >= 2)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
|
||||
|
||||
memset(&csth, 0, sizeof(csth));
|
||||
|
||||
ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
|
||||
|
||||
if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
|
||||
ast_audiohook_destroy(&csth.spy_audiohook);
|
||||
ast_channel_unlock(spyee);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_WHISPER)) {
|
||||
ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
|
||||
start_spying(spyee, spyer_name, &csth.whisper_audiohook);
|
||||
}
|
||||
|
||||
ast_channel_unlock(spyee);
|
||||
spyee = NULL;
|
||||
|
||||
csth.volfactor = *volfactor;
|
||||
|
||||
if (csth.volfactor) {
|
||||
csth.spy_audiohook.options.read_volume = csth.volfactor;
|
||||
csth.spy_audiohook.options.write_volume = csth.volfactor;
|
||||
}
|
||||
|
||||
csth.fd = fd;
|
||||
|
||||
if (ast_test_flag(flags, OPTION_PRIVATE))
|
||||
silgen = ast_channel_start_silence_generator(chan);
|
||||
else
|
||||
ast_activate_generator(chan, &spygen, &csth);
|
||||
|
||||
/* We can no longer rely on 'spyee' being an actual channel;
|
||||
it can be hung up and freed out from under us. However, the
|
||||
channel destructor will put NULL into our csth.spy.chan
|
||||
field when that happens, so that is our signal that the spyee
|
||||
channel has gone away.
|
||||
*/
|
||||
|
||||
/* Note: it is very important that the ast_waitfor() be the first
|
||||
condition in this expression, so that if we wait for some period
|
||||
of time before receiving a frame from our spying channel, we check
|
||||
for hangup on the spied-on channel _after_ knowing that a frame
|
||||
has arrived, since the spied-on channel could have gone away while
|
||||
we were waiting
|
||||
*/
|
||||
while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
|
||||
running = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_WHISPER) && (f->frametype == AST_FRAME_VOICE)) {
|
||||
ast_audiohook_lock(&csth.whisper_audiohook);
|
||||
ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
|
||||
ast_audiohook_unlock(&csth.whisper_audiohook);
|
||||
ast_frfree(f);
|
||||
continue;
|
||||
}
|
||||
|
||||
res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
|
||||
ast_frfree(f);
|
||||
if (!res)
|
||||
continue;
|
||||
|
||||
if (x == sizeof(inp))
|
||||
x = 0;
|
||||
|
||||
if (res < 0) {
|
||||
running = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (res == '*') {
|
||||
running = 0;
|
||||
break;
|
||||
} else if (res == '#') {
|
||||
if (!ast_strlen_zero(inp)) {
|
||||
running = atoi(inp);
|
||||
break;
|
||||
}
|
||||
|
||||
(*volfactor)++;
|
||||
if (*volfactor > 4)
|
||||
*volfactor = -4;
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
|
||||
csth.volfactor = *volfactor;
|
||||
csth.spy_audiohook.options.read_volume = csth.volfactor;
|
||||
csth.spy_audiohook.options.write_volume = csth.volfactor;
|
||||
} else if (res >= '0' && res <= '9') {
|
||||
inp[x++] = res;
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_PRIVATE))
|
||||
ast_channel_stop_silence_generator(chan, silgen);
|
||||
else
|
||||
ast_deactivate_generator(chan);
|
||||
|
||||
if (ast_test_flag(flags, OPTION_WHISPER)) {
|
||||
ast_audiohook_lock(&csth.whisper_audiohook);
|
||||
ast_audiohook_detach(&csth.whisper_audiohook);
|
||||
ast_audiohook_unlock(&csth.whisper_audiohook);
|
||||
ast_audiohook_destroy(&csth.whisper_audiohook);
|
||||
}
|
||||
|
||||
ast_audiohook_lock(&csth.spy_audiohook);
|
||||
ast_audiohook_detach(&csth.spy_audiohook);
|
||||
ast_audiohook_unlock(&csth.spy_audiohook);
|
||||
ast_audiohook_destroy(&csth.spy_audiohook);
|
||||
|
||||
if (option_verbose >= 2)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
|
||||
|
||||
return running;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \note This relies on the embedded lock to be recursive, as it may be called
|
||||
* due to a call to chanspy_ds_free with the lock held there.
|
||||
*/
|
||||
static void chanspy_ds_destroy(void *data)
|
||||
{
|
||||
struct chanspy_ds *chanspy_ds = data;
|
||||
|
||||
/* Setting chan to be NULL is an atomic operation, but we don't want this
|
||||
* value to change while this lock is held. The lock is held elsewhere
|
||||
* while it performs non-atomic operations with this channel pointer */
|
||||
|
||||
ast_mutex_lock(&chanspy_ds->lock);
|
||||
chanspy_ds->chan = NULL;
|
||||
ast_mutex_unlock(&chanspy_ds->lock);
|
||||
}
|
||||
|
||||
static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
|
||||
{
|
||||
struct chanspy_ds *chanspy_ds = data;
|
||||
|
||||
ast_mutex_lock(&chanspy_ds->lock);
|
||||
chanspy_ds->chan = new_chan;
|
||||
ast_mutex_unlock(&chanspy_ds->lock);
|
||||
}
|
||||
|
||||
static const struct ast_datastore_info chanspy_ds_info = {
|
||||
.type = "chanspy",
|
||||
.destroy = chanspy_ds_destroy,
|
||||
.chan_fixup = chanspy_ds_chan_fixup,
|
||||
};
|
||||
|
||||
static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
|
||||
{
|
||||
struct ast_channel *chan;
|
||||
|
||||
if (!chanspy_ds) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_mutex_lock(&chanspy_ds->lock);
|
||||
while ((chan = chanspy_ds->chan)) {
|
||||
struct ast_datastore *datastore;
|
||||
|
||||
if (ast_channel_trylock(chan)) {
|
||||
DEADLOCK_AVOIDANCE(&chanspy_ds->lock);
|
||||
continue;
|
||||
}
|
||||
if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
|
||||
ast_channel_datastore_remove(chan, datastore);
|
||||
/* chanspy_ds->chan is NULL after this call */
|
||||
chanspy_ds_destroy(datastore->data);
|
||||
datastore->data = NULL;
|
||||
ast_channel_datastore_free(datastore);
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
break;
|
||||
}
|
||||
ast_mutex_unlock(&chanspy_ds->lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
|
||||
static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
|
||||
{
|
||||
struct ast_datastore *datastore = NULL;
|
||||
|
||||
ast_mutex_lock(&chanspy_ds->lock);
|
||||
|
||||
if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
|
||||
ast_mutex_unlock(&chanspy_ds->lock);
|
||||
chanspy_ds = chanspy_ds_free(chanspy_ds);
|
||||
ast_channel_unlock(chan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chanspy_ds->chan = chan;
|
||||
datastore->data = chanspy_ds;
|
||||
ast_channel_datastore_add(chan, datastore);
|
||||
|
||||
return chanspy_ds;
|
||||
}
|
||||
|
||||
static struct chanspy_ds *next_channel(struct ast_channel *chan,
|
||||
const struct ast_channel *last, const char *spec,
|
||||
const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
|
||||
{
|
||||
struct ast_channel *this;
|
||||
char channel_name[AST_CHANNEL_NAME];
|
||||
static size_t PSEUDO_CHAN_LEN = 0;
|
||||
|
||||
if (!PSEUDO_CHAN_LEN) {
|
||||
PSEUDO_CHAN_LEN = *dahdi_chan_name_len + strlen("/pseudo");
|
||||
}
|
||||
|
||||
redo:
|
||||
if (spec)
|
||||
this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
|
||||
else if (exten)
|
||||
this = ast_walk_channel_by_exten_locked(last, exten, context);
|
||||
else
|
||||
this = ast_channel_walk_locked(last);
|
||||
|
||||
if (!this)
|
||||
return NULL;
|
||||
|
||||
snprintf(channel_name, AST_CHANNEL_NAME, "%s/pseudo", dahdi_chan_name);
|
||||
if (!strncmp(this->name, channel_name, PSEUDO_CHAN_LEN)) {
|
||||
last = this;
|
||||
ast_channel_unlock(this);
|
||||
goto redo;
|
||||
} else if (this == chan) {
|
||||
last = this;
|
||||
ast_channel_unlock(this);
|
||||
goto redo;
|
||||
}
|
||||
|
||||
return setup_chanspy_ds(this, chanspy_ds);
|
||||
}
|
||||
|
||||
static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
|
||||
int volfactor, const int fd, const char *mygroup, const char *spec,
|
||||
const char *exten, const char *context)
|
||||
{
|
||||
char nameprefix[AST_NAME_STRLEN];
|
||||
char peer_name[AST_NAME_STRLEN + 5];
|
||||
signed char zero_volume = 0;
|
||||
int waitms;
|
||||
int res;
|
||||
char *ptr;
|
||||
int num;
|
||||
int num_spyed_upon = 1;
|
||||
struct chanspy_ds chanspy_ds = { 0, };
|
||||
|
||||
ast_mutex_init(&chanspy_ds.lock);
|
||||
|
||||
snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
|
||||
|
||||
if (chan->_state != AST_STATE_UP)
|
||||
ast_answer(chan);
|
||||
|
||||
ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
|
||||
|
||||
waitms = 100;
|
||||
|
||||
for (;;) {
|
||||
struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
|
||||
struct ast_channel *prev = NULL, *peer = NULL;
|
||||
|
||||
if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
|
||||
res = ast_streamfile(chan, "beep", chan->language);
|
||||
if (!res)
|
||||
res = ast_waitstream(chan, "");
|
||||
else if (res < 0) {
|
||||
ast_clear_flag(chan, AST_FLAG_SPYING);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
res = ast_waitfordigit(chan, waitms);
|
||||
if (res < 0) {
|
||||
ast_clear_flag(chan, AST_FLAG_SPYING);
|
||||
break;
|
||||
}
|
||||
|
||||
/* reset for the next loop around, unless overridden later */
|
||||
waitms = 100;
|
||||
num_spyed_upon = 0;
|
||||
|
||||
for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
|
||||
peer_chanspy_ds;
|
||||
chanspy_ds_free(peer_chanspy_ds), prev = peer,
|
||||
peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
|
||||
next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
|
||||
const char *group;
|
||||
int igrp = !mygroup;
|
||||
char *groups[25];
|
||||
int num_groups = 0;
|
||||
char dup_group[512];
|
||||
int x;
|
||||
char *s;
|
||||
|
||||
peer = peer_chanspy_ds->chan;
|
||||
|
||||
ast_mutex_unlock(&peer_chanspy_ds->lock);
|
||||
|
||||
if (peer == prev) {
|
||||
ast_channel_unlock(peer);
|
||||
chanspy_ds_free(peer_chanspy_ds);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ast_check_hangup(chan)) {
|
||||
ast_channel_unlock(peer);
|
||||
chanspy_ds_free(peer_chanspy_ds);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
|
||||
ast_channel_unlock(peer);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
|
||||
ast_channel_unlock(peer);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mygroup) {
|
||||
if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
|
||||
ast_copy_string(dup_group, group, sizeof(dup_group));
|
||||
num_groups = ast_app_separate_args(dup_group, ':', groups,
|
||||
sizeof(groups) / sizeof(groups[0]));
|
||||
}
|
||||
|
||||
for (x = 0; x < num_groups; x++) {
|
||||
if (!strcmp(mygroup, groups[x])) {
|
||||
igrp = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!igrp) {
|
||||
ast_channel_unlock(peer);
|
||||
continue;
|
||||
}
|
||||
|
||||
strcpy(peer_name, "spy-");
|
||||
strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
|
||||
ptr = strchr(peer_name, '/');
|
||||
*ptr++ = '\0';
|
||||
|
||||
for (s = peer_name; s < ptr; s++)
|
||||
*s = tolower(*s);
|
||||
|
||||
/* We have to unlock the peer channel here to avoid a deadlock.
|
||||
* So, when we need to dereference it again, we have to lock the
|
||||
* datastore and get the pointer from there to see if the channel
|
||||
* is still valid. */
|
||||
ast_channel_unlock(peer);
|
||||
|
||||
if (!ast_test_flag(flags, OPTION_QUIET)) {
|
||||
if (ast_fileexists(peer_name, NULL, NULL) > 0) {
|
||||
res = ast_streamfile(chan, peer_name, chan->language);
|
||||
if (!res)
|
||||
res = ast_waitstream(chan, "");
|
||||
if (res) {
|
||||
chanspy_ds_free(peer_chanspy_ds);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
res = ast_say_character_str(chan, peer_name, "", chan->language);
|
||||
if ((num = atoi(ptr)))
|
||||
ast_say_digits(chan, atoi(ptr), "", chan->language);
|
||||
}
|
||||
|
||||
res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags);
|
||||
num_spyed_upon++;
|
||||
|
||||
if (res == -1) {
|
||||
chanspy_ds_free(peer_chanspy_ds);
|
||||
break;
|
||||
} else if (res > 1 && spec) {
|
||||
struct ast_channel *next;
|
||||
|
||||
snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
|
||||
|
||||
if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
|
||||
peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
|
||||
next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
|
||||
} else {
|
||||
/* stay on this channel, if it is still valid */
|
||||
|
||||
ast_mutex_lock(&peer_chanspy_ds->lock);
|
||||
if (peer_chanspy_ds->chan) {
|
||||
ast_channel_lock(peer_chanspy_ds->chan);
|
||||
next_chanspy_ds = peer_chanspy_ds;
|
||||
peer_chanspy_ds = NULL;
|
||||
} else {
|
||||
/* the channel is gone */
|
||||
ast_mutex_unlock(&peer_chanspy_ds->lock);
|
||||
next_chanspy_ds = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
peer = NULL;
|
||||
}
|
||||
}
|
||||
if (res == -1 || ast_check_hangup(chan))
|
||||
break;
|
||||
}
|
||||
|
||||
ast_clear_flag(chan, AST_FLAG_SPYING);
|
||||
|
||||
ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
|
||||
|
||||
ast_mutex_lock(&chanspy_ds.lock);
|
||||
ast_mutex_unlock(&chanspy_ds.lock);
|
||||
ast_mutex_destroy(&chanspy_ds.lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int chanspy_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_module_user *u;
|
||||
char *options = NULL;
|
||||
char *spec = NULL;
|
||||
char *argv[2];
|
||||
char *mygroup = NULL;
|
||||
char *recbase = NULL;
|
||||
int fd = 0;
|
||||
struct ast_flags flags;
|
||||
int oldwf = 0;
|
||||
int argc = 0;
|
||||
int volfactor = 0;
|
||||
int res;
|
||||
|
||||
data = ast_strdupa(data);
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
|
||||
spec = argv[0];
|
||||
if (argc > 1)
|
||||
options = argv[1];
|
||||
|
||||
if (ast_strlen_zero(spec) || !strcmp(spec, "all"))
|
||||
spec = NULL;
|
||||
}
|
||||
|
||||
if (options) {
|
||||
char *opts[OPT_ARG_ARRAY_SIZE];
|
||||
|
||||
ast_app_parse_options(spy_opts, &flags, opts, options);
|
||||
if (ast_test_flag(&flags, OPTION_GROUP))
|
||||
mygroup = opts[OPT_ARG_GROUP];
|
||||
|
||||
if (ast_test_flag(&flags, OPTION_RECORD) &&
|
||||
!(recbase = opts[OPT_ARG_RECORD]))
|
||||
recbase = "chanspy";
|
||||
|
||||
if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
|
||||
int vol;
|
||||
|
||||
if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
|
||||
ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
|
||||
else
|
||||
volfactor = vol;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, OPTION_PRIVATE))
|
||||
ast_set_flag(&flags, OPTION_WHISPER);
|
||||
} else
|
||||
ast_clear_flag(&flags, AST_FLAGS_ALL);
|
||||
|
||||
oldwf = chan->writeformat;
|
||||
if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
|
||||
ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (recbase) {
|
||||
char filename[PATH_MAX];
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
|
||||
if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
|
||||
ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
|
||||
fd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL);
|
||||
|
||||
if (fd)
|
||||
close(fd);
|
||||
|
||||
if (oldwf && ast_set_write_format(chan, oldwf) < 0)
|
||||
ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int extenspy_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_module_user *u;
|
||||
char *options = NULL;
|
||||
char *exten = NULL;
|
||||
char *context = NULL;
|
||||
char *argv[2];
|
||||
char *mygroup = NULL;
|
||||
char *recbase = NULL;
|
||||
int fd = 0;
|
||||
struct ast_flags flags;
|
||||
int oldwf = 0;
|
||||
int argc = 0;
|
||||
int volfactor = 0;
|
||||
int res;
|
||||
|
||||
data = ast_strdupa(data);
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
|
||||
context = argv[0];
|
||||
if (!ast_strlen_zero(argv[0]))
|
||||
exten = strsep(&context, "@");
|
||||
if (ast_strlen_zero(context))
|
||||
context = ast_strdupa(chan->context);
|
||||
if (argc > 1)
|
||||
options = argv[1];
|
||||
}
|
||||
|
||||
if (options) {
|
||||
char *opts[OPT_ARG_ARRAY_SIZE];
|
||||
|
||||
ast_app_parse_options(spy_opts, &flags, opts, options);
|
||||
if (ast_test_flag(&flags, OPTION_GROUP))
|
||||
mygroup = opts[OPT_ARG_GROUP];
|
||||
|
||||
if (ast_test_flag(&flags, OPTION_RECORD) &&
|
||||
!(recbase = opts[OPT_ARG_RECORD]))
|
||||
recbase = "chanspy";
|
||||
|
||||
if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
|
||||
int vol;
|
||||
|
||||
if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
|
||||
ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
|
||||
else
|
||||
volfactor = vol;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, OPTION_PRIVATE))
|
||||
ast_set_flag(&flags, OPTION_WHISPER);
|
||||
} else
|
||||
ast_clear_flag(&flags, AST_FLAGS_ALL);
|
||||
|
||||
oldwf = chan->writeformat;
|
||||
if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
|
||||
ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (recbase) {
|
||||
char filename[PATH_MAX];
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
|
||||
if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
|
||||
ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
|
||||
fd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context);
|
||||
|
||||
if (fd)
|
||||
close(fd);
|
||||
|
||||
if (oldwf && ast_set_write_format(chan, oldwf) < 0)
|
||||
ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
res |= ast_unregister_application(app_chan);
|
||||
res |= ast_unregister_application(app_ext);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
|
||||
res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");
|
||||
@@ -1,168 +1,138 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Trivial application to control playback a sound file
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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 Trivial application to control playback of a sound file
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/app.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/options.h"
|
||||
static char *tdesc = "Control Playback Application";
|
||||
|
||||
static const char *app = "ControlPlayback";
|
||||
static char *app = "ControlPlayback";
|
||||
|
||||
static const char *synopsis = "Play a file with fast forward and rewind";
|
||||
static char *synopsis = "Play a file with fast forward and rewind";
|
||||
|
||||
static const char *descrip =
|
||||
" ControlPlayback(file[|skipms[|ff[|rew[|stop[|pause[|restart|options]]]]]]]):\n"
|
||||
"This application will play back the given filename. By default, the '*' key\n"
|
||||
"can be used to rewind, and the '#' key can be used to fast-forward.\n"
|
||||
"Parameters:\n"
|
||||
" skipms - This is number of milliseconds to skip when rewinding or\n"
|
||||
" fast-forwarding.\n"
|
||||
" ff - Fast-forward when this DTMF digit is received.\n"
|
||||
" rew - Rewind when this DTMF digit is received.\n"
|
||||
" stop - Stop playback when this DTMF digit is received.\n"
|
||||
" pause - Pause playback when this DTMF digit is received.\n"
|
||||
" restart - Restart playback when this DTMF digit is received.\n"
|
||||
"Options:\n"
|
||||
" j - Jump to priority n+101 if the requested file is not found.\n"
|
||||
"This application sets the following channel variable upon completion:\n"
|
||||
" CPLAYBACKSTATUS - This variable contains the status of the attempt as a text\n"
|
||||
" string, one of: SUCCESS | USERSTOPPED | ERROR\n";
|
||||
static char *descrip =
|
||||
"ControlPlayback(filename[|skipms[|ffchar[|rewchar[|stopchar[|pausechr]]]]]):\n"
|
||||
" Plays back a given filename (do not put extension). Options may also\n"
|
||||
" be included following a pipe symbol. You can use * and # to rewind and\n"
|
||||
" fast forward the playback specified. If 'stopchar' is added the file will\n"
|
||||
" terminate playback when 'stopchar' is pressed. Returns -1 if the channel\n"
|
||||
" was hung up, or if the file does not exist. Returns 0 otherwise.\n\n"
|
||||
" Example: exten => 1234,1,ControlPlayback(file|4000|*|#|1|0)\n\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int is_on_phonepad(char key)
|
||||
{
|
||||
return key == 35 || key == 42 || (key >= 48 && key <= 57);
|
||||
return (key == 35 || key == 42 || (key >= 48 && key <= 57)) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int controlplayback_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0, priority_jump = 0;
|
||||
int res = 0;
|
||||
int skipms = 0;
|
||||
struct ast_module_user *u;
|
||||
char *tmp;
|
||||
int argc;
|
||||
char *argv[8];
|
||||
enum arg_ids {
|
||||
arg_file = 0,
|
||||
arg_skip = 1,
|
||||
arg_fwd = 2,
|
||||
arg_rev = 3,
|
||||
arg_stop = 4,
|
||||
arg_pause = 5,
|
||||
arg_restart = 6,
|
||||
options = 7,
|
||||
};
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
struct localuser *u;
|
||||
char tmp[256];
|
||||
char *skip = NULL, *fwd = NULL, *rev = NULL, *stop = NULL, *pause = NULL, *file = NULL;
|
||||
|
||||
if (!data || ast_strlen_zero((char *)data)) {
|
||||
ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
tmp = ast_strdupa(data);
|
||||
memset(argv, 0, sizeof(argv));
|
||||
strncpy(tmp, (char *)data, sizeof(tmp)-1);
|
||||
file = tmp;
|
||||
|
||||
argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
|
||||
|
||||
if (argc < 1) {
|
||||
ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
if ((skip=strchr(tmp,'|'))) {
|
||||
*skip++ = '\0';
|
||||
fwd=strchr(skip,'|');
|
||||
if (fwd) {
|
||||
*fwd++ = '\0';
|
||||
rev = strchr(fwd,'|');
|
||||
if (rev) {
|
||||
*rev++ = '\0';
|
||||
stop = strchr(rev,'|');
|
||||
if (stop) {
|
||||
*stop++ = '\0';
|
||||
pause = strchr(stop,'|');
|
||||
if (pause) {
|
||||
*pause++ = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skipms = argv[arg_skip] ? atoi(argv[arg_skip]) : 3000;
|
||||
skipms = skip ? atoi(skip) : 3000;
|
||||
if (!skipms)
|
||||
skipms = 3000;
|
||||
|
||||
if (!argv[arg_fwd] || !is_on_phonepad(*argv[arg_fwd]))
|
||||
argv[arg_fwd] = "#";
|
||||
if (!argv[arg_rev] || !is_on_phonepad(*argv[arg_rev]))
|
||||
argv[arg_rev] = "*";
|
||||
if (argv[arg_stop] && !is_on_phonepad(*argv[arg_stop]))
|
||||
argv[arg_stop] = NULL;
|
||||
if (argv[arg_pause] && !is_on_phonepad(*argv[arg_pause]))
|
||||
argv[arg_pause] = NULL;
|
||||
if (argv[arg_restart] && !is_on_phonepad(*argv[arg_restart]))
|
||||
argv[arg_restart] = NULL;
|
||||
if (!fwd || !is_on_phonepad(*fwd))
|
||||
fwd = "#";
|
||||
if (!rev || !is_on_phonepad(*rev))
|
||||
rev = "*";
|
||||
if (stop && !is_on_phonepad(*stop))
|
||||
stop = NULL;
|
||||
if (pause && !is_on_phonepad(*pause))
|
||||
pause = NULL;
|
||||
|
||||
if (argv[options]) {
|
||||
if (strchr(argv[options], 'j'))
|
||||
priority_jump = 1;
|
||||
}
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
res = ast_control_streamfile(chan, argv[arg_file], argv[arg_fwd], argv[arg_rev], argv[arg_stop], argv[arg_pause], argv[arg_restart], skipms);
|
||||
res = ast_control_streamfile(chan, file, fwd, rev, stop, pause, skipms);
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
|
||||
/* If we stopped on one of our stop keys, return 0 */
|
||||
if (res > 0 && argv[arg_stop] && strchr(argv[arg_stop], res)) {
|
||||
if(stop && strchr(stop, res))
|
||||
res = 0;
|
||||
pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "USERSTOPPED");
|
||||
} else {
|
||||
if (res < 0) {
|
||||
if (priority_jump || ast_opt_priority_jumping) {
|
||||
if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
|
||||
ast_log(LOG_WARNING, "ControlPlayback tried to jump to priority n+101 as requested, but priority didn't exist\n");
|
||||
}
|
||||
}
|
||||
res = 0;
|
||||
pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "ERROR");
|
||||
} else
|
||||
pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "SUCCESS");
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
res = ast_unregister_application(app);
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, controlplayback_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Control Playback Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
198
apps/app_cut.c
Normal file
198
apps/app_cut.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Cut application
|
||||
*
|
||||
* Copyright (c) 2003 Tilghman Lesher. All rights reserved.
|
||||
*
|
||||
* Tilghman Lesher <app_cut__v003@the-tilghman.com>
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* This code is released by the author with no restrictions on usage.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
|
||||
/* Maximum length of any variable */
|
||||
#define MAXRESULT 1024
|
||||
|
||||
static char *tdesc = "Cuts up variables";
|
||||
|
||||
static char *app_cut = "Cut";
|
||||
|
||||
static char *cut_synopsis = "Splits a variable's content using the specified delimiter";
|
||||
|
||||
static char *cut_descrip =
|
||||
"Usage: Cut(newvar=varname,delimiter,fieldspec)\n"
|
||||
" newvar - new variable created from result string\n"
|
||||
" varname - variable you want cut\n"
|
||||
" delimiter - defaults to '-'\n"
|
||||
" fieldspec - number of the field you want (1-based offset)\n"
|
||||
" may also be specified as a range (with -)\n"
|
||||
" or group of ranges and fields (with &)\n"
|
||||
" Returns 0 or -1 on hangup or error.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int cut_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
struct localuser *u;
|
||||
char *s, *newvar=NULL, *varname=NULL, *delimiter=NULL, *field=NULL;
|
||||
int args_okay = 0;
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
/* Check and parse arguments */
|
||||
if (data) {
|
||||
s = ast_strdupa((char *)data);
|
||||
if (s) {
|
||||
newvar = strsep(&s, "=");
|
||||
if (newvar && (newvar[0] != '\0')) {
|
||||
varname = strsep(&s, "|");
|
||||
if (varname && (varname[0] != '\0')) {
|
||||
delimiter = strsep(&s, "|");
|
||||
if (delimiter) {
|
||||
field = strsep(&s, "|");
|
||||
if (field) {
|
||||
args_okay = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Out of memory\n");
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (args_okay) {
|
||||
char d, ds[2];
|
||||
char *tmp = alloca(strlen(varname) + 4);
|
||||
char varvalue[MAXRESULT], *tmp2=varvalue;
|
||||
char retstring[MAXRESULT];
|
||||
|
||||
memset(retstring, 0, MAXRESULT);
|
||||
|
||||
if (tmp) {
|
||||
snprintf(tmp, strlen(varname) + 4, "${%s}", varname);
|
||||
memset(varvalue, 0, sizeof(varvalue));
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Out of memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (delimiter[0])
|
||||
d = delimiter[0];
|
||||
else
|
||||
d = '-';
|
||||
|
||||
/* String form of the delimiter, for use with strsep(3) */
|
||||
snprintf(ds, sizeof(ds), "%c", d);
|
||||
|
||||
pbx_substitute_variables_helper(chan, tmp, tmp2, MAXRESULT - 1);
|
||||
|
||||
if (tmp2) {
|
||||
int curfieldnum = 1;
|
||||
while ((tmp2 != NULL) && (field != NULL)) {
|
||||
char *nextgroup = strsep(&field, "&");
|
||||
int num1 = 0, num2 = MAXRESULT;
|
||||
char trashchar;
|
||||
|
||||
if (sscanf(nextgroup, "%d-%d", &num1, &num2) == 2) {
|
||||
/* range with both start and end */
|
||||
} else if (sscanf(nextgroup, "-%d", &num2) == 1) {
|
||||
/* range with end */
|
||||
num1 = 0;
|
||||
} else if ((sscanf(nextgroup, "%d%c", &num1, &trashchar) == 2) && (trashchar == '-')) {
|
||||
/* range with start */
|
||||
num2 = MAXRESULT;
|
||||
} else if (sscanf(nextgroup, "%d", &num1) == 1) {
|
||||
/* single number */
|
||||
num2 = num1;
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Cut(): Illegal range '%s'\n", nextgroup);
|
||||
ast_log(LOG_ERROR, "Usage: %s\n", cut_synopsis);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get to start, if any */
|
||||
if (num1 > 0) {
|
||||
while ((tmp2 != (char *)NULL + 1) && (curfieldnum < num1)) {
|
||||
tmp2 = index(tmp2, d) + 1;
|
||||
curfieldnum++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Most frequent problem is the expectation of reordering fields */
|
||||
if ((num1 > 0) && (curfieldnum > num1)) {
|
||||
ast_log(LOG_WARNING, "Cut(): we're already past the field you wanted?\n");
|
||||
}
|
||||
|
||||
/* Re-null tmp2 if we added 1 to NULL */
|
||||
if (tmp2 == (char *)NULL + 1)
|
||||
tmp2 = NULL;
|
||||
|
||||
/* Output fields until we either run out of fields or num2 is reached */
|
||||
while ((tmp2 != NULL) && (curfieldnum <= num2)) {
|
||||
char *tmp3 = strsep(&tmp2, ds);
|
||||
int curlen = strlen(retstring);
|
||||
|
||||
if (strlen(retstring)) {
|
||||
snprintf(retstring + curlen, MAXRESULT - curlen, "%c%s", d, tmp3);
|
||||
} else {
|
||||
snprintf(retstring, MAXRESULT, "%s", tmp3);
|
||||
}
|
||||
|
||||
curfieldnum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pbx_builtin_setvar_helper(chan, newvar, retstring);
|
||||
}
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app_cut);
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app_cut, cut_exec, cut_synopsis, cut_descrip);
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
@@ -1,306 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Execute an ISDN RAS
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>dahdi</depend>
|
||||
<depend>working_fork</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/signal.h>
|
||||
#else
|
||||
#include <signal.h>
|
||||
#endif /* __linux__ */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "asterisk/dahdi_compat.h"
|
||||
|
||||
#ifdef HAVE_CAP
|
||||
#include <sys/capability.h>
|
||||
#endif /* HAVE_CAP */
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/options.h"
|
||||
|
||||
static char *dahdi_app = "DAHDIRAS";
|
||||
static char *zap_app = "ZapRAS";
|
||||
|
||||
static char *dahdi_synopsis = "Executes DAHDI ISDN RAS application";
|
||||
static char *zap_synopsis = "Executes Zaptel ISDN RAS application";
|
||||
|
||||
static char *dahdi_descrip =
|
||||
" DAHDIRAS(args): Executes a RAS server using pppd on the given channel.\n"
|
||||
"The channel must be a clear channel (i.e. PRI source) and a DAHDI\n"
|
||||
"channel to be able to use this function (no modem emulation is included).\n"
|
||||
"Your pppd must have the DAHDI plugin available. Arguments should be\n"
|
||||
"separated by | characters.\n";
|
||||
|
||||
static char *zap_descrip =
|
||||
" ZapRAS(args): Executes a RAS server using pppd on the given channel.\n"
|
||||
"The channel must be a clear channel (i.e. PRI source) and a Zaptel\n"
|
||||
"channel to be able to use this function (no modem emulation is included).\n"
|
||||
"Your pppd must have the Zaptel plugin available. Arguments should be\n"
|
||||
"separated by | characters.\n";
|
||||
|
||||
#define PPP_MAX_ARGS 32
|
||||
#define PPP_EXEC "/usr/sbin/pppd"
|
||||
|
||||
static pid_t spawn_ras(struct ast_channel *chan, char *args)
|
||||
{
|
||||
pid_t pid;
|
||||
int x;
|
||||
char *c;
|
||||
|
||||
char *argv[PPP_MAX_ARGS];
|
||||
int argc = 0;
|
||||
char *stringp=NULL;
|
||||
sigset_t fullset, oldset;
|
||||
#ifdef HAVE_CAP
|
||||
cap_t cap;
|
||||
#endif
|
||||
|
||||
sigfillset(&fullset);
|
||||
pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
|
||||
|
||||
/* Start by forking */
|
||||
pid = fork();
|
||||
if (pid) {
|
||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||
return pid;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CAP
|
||||
cap = cap_from_text("cap_net_admin-eip");
|
||||
|
||||
if (cap_set_proc(cap)) {
|
||||
/* Careful with order! Logging cannot happen after we close FDs */
|
||||
ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
|
||||
}
|
||||
cap_free(cap);
|
||||
#endif
|
||||
|
||||
/* Restore original signal handlers */
|
||||
for (x=0;x<NSIG;x++)
|
||||
signal(x, SIG_DFL);
|
||||
|
||||
pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
|
||||
|
||||
/* Execute RAS on File handles */
|
||||
dup2(chan->fds[0], STDIN_FILENO);
|
||||
|
||||
/* Drop high priority */
|
||||
if (ast_opt_high_priority)
|
||||
ast_set_priority(0);
|
||||
|
||||
/* Close other file descriptors */
|
||||
for (x=STDERR_FILENO + 1;x<1024;x++)
|
||||
close(x);
|
||||
|
||||
/* Reset all arguments */
|
||||
memset(argv, 0, sizeof(argv));
|
||||
|
||||
/* First argument is executable, followed by standard
|
||||
arguments for DAHDI PPP */
|
||||
argv[argc++] = PPP_EXEC;
|
||||
argv[argc++] = "nodetach";
|
||||
|
||||
/* And all the other arguments */
|
||||
stringp=args;
|
||||
c = strsep(&stringp, "|");
|
||||
while(c && strlen(c) && (argc < (PPP_MAX_ARGS - 4))) {
|
||||
argv[argc++] = c;
|
||||
c = strsep(&stringp, "|");
|
||||
}
|
||||
|
||||
argv[argc++] = "plugin";
|
||||
#ifdef HAVE_ZAPTEL
|
||||
argv[argc++] = "zaptel.so";
|
||||
#else
|
||||
argv[argc++] = "dahdi.so";
|
||||
#endif
|
||||
argv[argc++] = "stdin";
|
||||
|
||||
/* Finally launch PPP */
|
||||
execv(PPP_EXEC, argv);
|
||||
fprintf(stderr, "Failed to exec PPPD!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void run_ras(struct ast_channel *chan, char *args)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
int res;
|
||||
int signalled = 0;
|
||||
struct dahdi_bufferinfo savebi;
|
||||
int x;
|
||||
|
||||
res = ioctl(chan->fds[0], DAHDI_GET_BUFINFO, &savebi);
|
||||
if(res) {
|
||||
ast_log(LOG_WARNING, "Unable to check buffer policy on channel %s\n", chan->name);
|
||||
return;
|
||||
}
|
||||
|
||||
pid = spawn_ras(chan, args);
|
||||
if (pid < 0) {
|
||||
ast_log(LOG_WARNING, "Failed to spawn RAS\n");
|
||||
} else {
|
||||
for (;;) {
|
||||
res = wait4(pid, &status, WNOHANG, NULL);
|
||||
if (!res) {
|
||||
/* Check for hangup */
|
||||
if (chan->_softhangup && !signalled) {
|
||||
ast_log(LOG_DEBUG, "Channel '%s' hungup. Signalling RAS at %d to die...\n", chan->name, pid);
|
||||
kill(pid, SIGTERM);
|
||||
signalled=1;
|
||||
}
|
||||
/* Try again */
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "wait4 returned %d: %s\n", res, strerror(errno));
|
||||
}
|
||||
if (option_verbose > 2) {
|
||||
if (WIFEXITED(status)) {
|
||||
ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with status %d\n", chan->name, WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with signal %d\n",
|
||||
chan->name, WTERMSIG(status));
|
||||
} else {
|
||||
ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated weirdly.\n", chan->name);
|
||||
}
|
||||
}
|
||||
/* Throw back into audio mode */
|
||||
x = 1;
|
||||
ioctl(chan->fds[0], DAHDI_AUDIOMODE, &x);
|
||||
|
||||
/* Restore saved values */
|
||||
res = ioctl(chan->fds[0], DAHDI_SET_BUFINFO, &savebi);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set buffer policy on channel %s\n", chan->name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=-1;
|
||||
char *args;
|
||||
struct ast_module_user *u;
|
||||
struct dahdi_params ztp;
|
||||
|
||||
if (!data)
|
||||
data = "";
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
args = ast_strdupa(data);
|
||||
|
||||
/* Answer the channel if it's not up */
|
||||
if (chan->_state != AST_STATE_UP)
|
||||
ast_answer(chan);
|
||||
if (strcasecmp(chan->tech->type, dahdi_chan_name)) {
|
||||
if (option_verbose > 1)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Channel %s is not a %s channel\n", chan->name, dahdi_chan_name);
|
||||
sleep(2);
|
||||
} else {
|
||||
memset(&ztp, 0, sizeof(ztp));
|
||||
if (ioctl(chan->fds[0], DAHDI_GET_PARAMS, &ztp)) {
|
||||
ast_log(LOG_WARNING, "Unable to get parameters\n");
|
||||
} else if (ztp.sigtype != DAHDI_SIG_CLEAR) {
|
||||
if (option_verbose > 1)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Channel %s is not a clear channel\n", chan->name);
|
||||
} else {
|
||||
/* Everything should be okay. Run PPP. */
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Starting RAS on %s\n", chan->name);
|
||||
/* Execute RAS */
|
||||
run_ras(chan, args);
|
||||
}
|
||||
}
|
||||
ast_module_user_remove(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int exec_warn(struct ast_channel *chan, void *data)
|
||||
{
|
||||
ast_log(LOG_WARNING, "Use of the command %s is deprecated, please use %s instead.\n", zap_app, dahdi_app);
|
||||
|
||||
return exec(chan, data);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) {
|
||||
res |= ast_unregister_application(dahdi_app);
|
||||
}
|
||||
|
||||
res |= ast_unregister_application(zap_app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) {
|
||||
res |= ast_register_application(dahdi_app, exec, dahdi_synopsis, dahdi_descrip);
|
||||
}
|
||||
|
||||
res |= ast_register_application(zap_app, exec_warn, zap_synopsis, zap_descrip);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DAHDI RAS Application");
|
||||
|
||||
@@ -1,389 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* Modified from app_zapbarge by David Troy <dave@toad.net>
|
||||
*
|
||||
* Special thanks to comphealth.com for sponsoring this
|
||||
* GPL application.
|
||||
*
|
||||
* 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 Zap Scanner
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>dahdi</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/say.h"
|
||||
|
||||
#include "asterisk/dahdi_compat.h"
|
||||
|
||||
static char *app = "DAHDIScan";
|
||||
static char *deprecated_app = "ZapScan";
|
||||
|
||||
static char *synopsis = "Scan Zap channels to monitor calls";
|
||||
|
||||
static char *descrip =
|
||||
" ZapScan([group]) allows a call center manager to monitor Zap channels in\n"
|
||||
"a convenient way. Use '#' to select the next channel and use '*' to exit\n"
|
||||
"Limit scanning to a channel GROUP by setting the option group argument.\n";
|
||||
|
||||
|
||||
#define CONF_SIZE 160
|
||||
|
||||
static struct ast_channel *get_zap_channel_locked(int num) {
|
||||
char name[80];
|
||||
|
||||
snprintf(name,sizeof(name),"%s/%d-1", dahdi_chan_name, num);
|
||||
return ast_get_channel_by_name_locked(name);
|
||||
}
|
||||
|
||||
static int careful_write(int fd, unsigned char *data, int len)
|
||||
{
|
||||
int res;
|
||||
while(len) {
|
||||
res = write(fd, data, len);
|
||||
if (res < 1) {
|
||||
if (errno != EAGAIN) {
|
||||
ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
|
||||
return -1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
len -= res;
|
||||
data += res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int conf_run(struct ast_channel *chan, int confno, int confflags)
|
||||
{
|
||||
int fd;
|
||||
struct dahdi_confinfo ztc;
|
||||
struct ast_frame *f;
|
||||
struct ast_channel *c;
|
||||
struct ast_frame fr;
|
||||
int outfd;
|
||||
int ms;
|
||||
int nfds;
|
||||
int res;
|
||||
int flags;
|
||||
int retryzap;
|
||||
int origfd;
|
||||
int ret = -1;
|
||||
char input[4];
|
||||
int ic=0;
|
||||
struct dahdi_bufferinfo bi;
|
||||
char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
|
||||
char *buf = __buf + AST_FRIENDLY_OFFSET;
|
||||
|
||||
/* Set it into U-law mode (write) */
|
||||
if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
|
||||
goto outrun;
|
||||
}
|
||||
|
||||
/* Set it into U-law mode (read) */
|
||||
if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
|
||||
goto outrun;
|
||||
}
|
||||
ast_indicate(chan, -1);
|
||||
retryzap = strcasecmp(chan->tech->type, "Zap");
|
||||
zapretry:
|
||||
origfd = chan->fds[0];
|
||||
if (retryzap) {
|
||||
fd = open(DAHDI_FILE_PSEUDO, O_RDWR);
|
||||
if (fd < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
|
||||
goto outrun;
|
||||
}
|
||||
/* Make non-blocking */
|
||||
flags = fcntl(fd, F_GETFL);
|
||||
if (flags < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
goto outrun;
|
||||
}
|
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
|
||||
ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
goto outrun;
|
||||
}
|
||||
/* Setup buffering information */
|
||||
memset(&bi, 0, sizeof(bi));
|
||||
bi.bufsize = CONF_SIZE;
|
||||
bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
|
||||
bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
|
||||
bi.numbufs = 4;
|
||||
if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
|
||||
ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
goto outrun;
|
||||
}
|
||||
nfds = 1;
|
||||
} else {
|
||||
/* XXX Make sure we're not running on a pseudo channel XXX */
|
||||
fd = chan->fds[0];
|
||||
nfds = 0;
|
||||
}
|
||||
memset(&ztc, 0, sizeof(ztc));
|
||||
/* Check to see if we're in a conference... */
|
||||
ztc.chan = 0;
|
||||
if (ioctl(fd, DAHDI_GETCONF, &ztc)) {
|
||||
ast_log(LOG_WARNING, "Error getting conference\n");
|
||||
close(fd);
|
||||
goto outrun;
|
||||
}
|
||||
if (ztc.confmode) {
|
||||
/* Whoa, already in a conference... Retry... */
|
||||
if (!retryzap) {
|
||||
ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
|
||||
retryzap = 1;
|
||||
goto zapretry;
|
||||
}
|
||||
}
|
||||
memset(&ztc, 0, sizeof(ztc));
|
||||
/* Add us to the conference */
|
||||
ztc.chan = 0;
|
||||
ztc.confno = confno;
|
||||
ztc.confmode = DAHDI_CONF_MONITORBOTH;
|
||||
|
||||
if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
|
||||
ast_log(LOG_WARNING, "Error setting conference\n");
|
||||
close(fd);
|
||||
goto outrun;
|
||||
}
|
||||
ast_log(LOG_DEBUG, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno);
|
||||
|
||||
for(;;) {
|
||||
outfd = -1;
|
||||
ms = -1;
|
||||
c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
|
||||
if (c) {
|
||||
if (c->fds[0] != origfd) {
|
||||
if (retryzap) {
|
||||
/* Kill old pseudo */
|
||||
close(fd);
|
||||
}
|
||||
ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
|
||||
retryzap = 0;
|
||||
goto zapretry;
|
||||
}
|
||||
f = ast_read(c);
|
||||
if (!f)
|
||||
break;
|
||||
if(f->frametype == AST_FRAME_DTMF) {
|
||||
if(f->subclass == '#') {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
else if (f->subclass == '*') {
|
||||
ret = -1;
|
||||
break;
|
||||
|
||||
}
|
||||
else {
|
||||
input[ic++] = f->subclass;
|
||||
}
|
||||
if(ic == 3) {
|
||||
input[ic++] = '\0';
|
||||
ic=0;
|
||||
ret = atoi(input);
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Zapscan: change channel to %d\n",ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd != chan->fds[0]) {
|
||||
if (f->frametype == AST_FRAME_VOICE) {
|
||||
if (f->subclass == AST_FORMAT_ULAW) {
|
||||
/* Carefully write */
|
||||
careful_write(fd, f->data, f->datalen);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass);
|
||||
}
|
||||
}
|
||||
ast_frfree(f);
|
||||
} else if (outfd > -1) {
|
||||
res = read(outfd, buf, CONF_SIZE);
|
||||
if (res > 0) {
|
||||
memset(&fr, 0, sizeof(fr));
|
||||
fr.frametype = AST_FRAME_VOICE;
|
||||
fr.subclass = AST_FORMAT_ULAW;
|
||||
fr.datalen = res;
|
||||
fr.samples = res;
|
||||
fr.data = buf;
|
||||
fr.offset = AST_FRIENDLY_OFFSET;
|
||||
if (ast_write(chan, &fr) < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
|
||||
/* break; */
|
||||
}
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
if (f)
|
||||
ast_frfree(f);
|
||||
if (fd != chan->fds[0])
|
||||
close(fd);
|
||||
else {
|
||||
/* Take out of conference */
|
||||
/* Add us to the conference */
|
||||
ztc.chan = 0;
|
||||
ztc.confno = 0;
|
||||
ztc.confmode = 0;
|
||||
if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
|
||||
ast_log(LOG_WARNING, "Error setting conference\n");
|
||||
}
|
||||
}
|
||||
|
||||
outrun:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int conf_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=-1;
|
||||
struct ast_module_user *u;
|
||||
int confflags = 0;
|
||||
int confno = 0;
|
||||
char confstr[80] = "", *tmp = NULL;
|
||||
struct ast_channel *tempchan = NULL, *lastchan = NULL,*ichan = NULL;
|
||||
struct ast_frame *f;
|
||||
char *desired_group;
|
||||
int input=0,search_group=0;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (chan->_state != AST_STATE_UP)
|
||||
ast_answer(chan);
|
||||
|
||||
desired_group = ast_strdupa(data);
|
||||
if(!ast_strlen_zero(desired_group)) {
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Scanning for group %s\n", desired_group);
|
||||
search_group = 1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (ast_waitfor(chan, 100) < 0)
|
||||
break;
|
||||
|
||||
f = ast_read(chan);
|
||||
if (!f)
|
||||
break;
|
||||
if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) {
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
ast_frfree(f);
|
||||
ichan = NULL;
|
||||
if(input) {
|
||||
ichan = get_zap_channel_locked(input);
|
||||
input = 0;
|
||||
}
|
||||
|
||||
tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan);
|
||||
|
||||
if ( !tempchan && !lastchan )
|
||||
break;
|
||||
|
||||
if (tempchan && search_group) {
|
||||
const char *mygroup;
|
||||
if((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) {
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
|
||||
} else {
|
||||
ast_mutex_unlock(&tempchan->lock);
|
||||
lastchan = tempchan;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (tempchan && (!strcmp(tempchan->tech->type, "Zap")) && (tempchan != chan) ) {
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Zap channel %s is in-use, monitoring...\n", tempchan->name);
|
||||
ast_copy_string(confstr, tempchan->name, sizeof(confstr));
|
||||
ast_mutex_unlock(&tempchan->lock);
|
||||
if ((tmp = strchr(confstr,'-'))) {
|
||||
*tmp = '\0';
|
||||
}
|
||||
confno = atoi(strchr(confstr,'/') + 1);
|
||||
ast_stopstream(chan);
|
||||
ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL);
|
||||
res = conf_run(chan, confno, confflags);
|
||||
if (res<0) break;
|
||||
input = res;
|
||||
} else if (tempchan)
|
||||
ast_mutex_unlock(&tempchan->lock);
|
||||
lastchan = tempchan;
|
||||
}
|
||||
ast_module_user_remove(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int conf_exec_warn(struct ast_channel *chan, void *data)
|
||||
{
|
||||
ast_log(LOG_WARNING, "Use of the command %s is deprecated, please use %s instead.\n", deprecated_app, app);
|
||||
return conf_exec(chan, data);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
ast_register_application(deprecated_app, conf_exec_warn, synopsis, descrip);
|
||||
return ast_register_application(app, conf_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan Zap channels application");
|
||||
|
||||
81
apps/app_datetime.c
Normal file
81
apps/app_datetime.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Time of day - Report the time of day
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/say.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static char *tdesc = "Date and Time";
|
||||
|
||||
static char *app = "DateTime";
|
||||
|
||||
static char *synopsis = "Say the date and time";
|
||||
|
||||
static char *descrip =
|
||||
" DateTime(): Says the current date and time. Returns -1 on hangup or 0\n"
|
||||
"otherwise.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int datetime_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
time_t t;
|
||||
struct localuser *u;
|
||||
LOCAL_USER_ADD(u);
|
||||
time(&t);
|
||||
if (chan->_state != AST_STATE_UP)
|
||||
res = ast_answer(chan);
|
||||
if (!res)
|
||||
res = ast_say_datetime(chan, t, "", chan->language);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, datetime_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
270
apps/app_db.c
270
apps/app_db.c
@@ -1,88 +1,86 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Database access functions
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
* Copyright (C) 2003, Jefferson Noxon
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
* Jefferson Noxon <jeff@debian.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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Database access functions
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
* \author Jefferson Noxon <jeff@debian.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/astdb.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/astdb.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/options.h"
|
||||
static char *tdesc = "Database access functions for Asterisk extension logic";
|
||||
|
||||
static char *g_descrip =
|
||||
" DBget(varname=family/key): Retrieves a value from the Asterisk\n"
|
||||
"database and stores it in the given variable. Always returns 0. If the\n"
|
||||
"requested key is not found, jumps to priority n+101 if available.\n";
|
||||
|
||||
static char *p_descrip =
|
||||
" DBput(family/key=value): Stores the given value in the Asterisk\n"
|
||||
"database. Always returns 0.\n";
|
||||
|
||||
/*! \todo XXX Remove this application after 1.4 is relased */
|
||||
static char *d_descrip =
|
||||
" DBdel(family/key): This application will delete a key from the Asterisk\n"
|
||||
"database.\n"
|
||||
" This application has been DEPRECATED in favor of the DB_DELETE function.\n";
|
||||
" DBdel(family/key): Deletes a key from the Asterisk database. Always\n"
|
||||
"returns 0.\n";
|
||||
|
||||
static char *dt_descrip =
|
||||
" DBdeltree(family[/keytree]): This application will delete a family or keytree\n"
|
||||
"from the Asterisk database\n";
|
||||
" DBdeltree(family[/keytree]): Deletes a family or keytree from the Asterisk\n"
|
||||
"database. Always returns 0.\n";
|
||||
|
||||
static char *g_app = "DBget";
|
||||
static char *p_app = "DBput";
|
||||
static char *d_app = "DBdel";
|
||||
static char *dt_app = "DBdeltree";
|
||||
|
||||
static char *g_synopsis = "Retrieve a value from the database";
|
||||
static char *p_synopsis = "Store a value in the database";
|
||||
static char *d_synopsis = "Delete a key from the database";
|
||||
static char *dt_synopsis = "Delete a family or keytree from the database";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
static int deltree_exec(struct ast_channel *chan, void *data)
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int deltree_exec (struct ast_channel *chan, void *data)
|
||||
{
|
||||
int arglen;
|
||||
char *argv, *family, *keytree;
|
||||
struct ast_module_user *u;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
arglen = strlen (data);
|
||||
argv = alloca (arglen + 1);
|
||||
if (!argv) { /* Why would this fail? */
|
||||
ast_log (LOG_DEBUG, "Memory allocation failed\n");
|
||||
return 0;
|
||||
}
|
||||
memcpy (argv, data, arglen + 1);
|
||||
|
||||
argv = ast_strdupa(data);
|
||||
|
||||
if (strchr(argv, '/')) {
|
||||
family = strsep(&argv, "/");
|
||||
keytree = strsep(&argv, "\0");
|
||||
if (strchr (argv, '/')) {
|
||||
family = strsep (&argv, "/");
|
||||
keytree = strsep (&argv, "\0");
|
||||
if (!family || !keytree) {
|
||||
ast_log(LOG_DEBUG, "Ignoring; Syntax error in argument\n");
|
||||
ast_module_user_remove(u);
|
||||
ast_log (LOG_DEBUG, "Ignoring; Syntax error in argument\n");
|
||||
return 0;
|
||||
}
|
||||
if (ast_strlen_zero(keytree))
|
||||
if (!strlen (keytree))
|
||||
keytree = 0;
|
||||
} else {
|
||||
family = argv;
|
||||
@@ -91,77 +89,167 @@ static int deltree_exec(struct ast_channel *chan, void *data)
|
||||
|
||||
if (option_verbose > 2) {
|
||||
if (keytree)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: family=%s, keytree=%s\n", family, keytree);
|
||||
ast_verbose (VERBOSE_PREFIX_3 "DBdeltree: family=%s, keytree=%s\n", family, keytree);
|
||||
else
|
||||
ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: family=%s\n", family);
|
||||
ast_verbose (VERBOSE_PREFIX_3 "DBdeltree: family=%s\n", family);
|
||||
}
|
||||
|
||||
if (ast_db_deltree(family, keytree)) {
|
||||
if (ast_db_deltree (family, keytree)) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: Error deleting key from database.\n");
|
||||
ast_verbose (VERBOSE_PREFIX_3 "DBdeltree: Error deleting key from database.\n");
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int del_exec(struct ast_channel *chan, void *data)
|
||||
static int del_exec (struct ast_channel *chan, void *data)
|
||||
{
|
||||
int arglen;
|
||||
char *argv, *family, *key;
|
||||
struct ast_module_user *u;
|
||||
static int deprecation_warning = 0;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (!deprecation_warning) {
|
||||
deprecation_warning = 1;
|
||||
ast_log(LOG_WARNING, "The DBdel application has been deprecated in favor of the DB_DELETE dialplan function!\n");
|
||||
arglen = strlen (data);
|
||||
argv = alloca (arglen + 1);
|
||||
if (!argv) { /* Why would this fail? */
|
||||
ast_log (LOG_DEBUG, "Memory allocation failed\n");
|
||||
return 0;
|
||||
}
|
||||
memcpy (argv, data, arglen + 1);
|
||||
|
||||
argv = ast_strdupa(data);
|
||||
|
||||
if (strchr(argv, '/')) {
|
||||
family = strsep(&argv, "/");
|
||||
key = strsep(&argv, "\0");
|
||||
if (strchr (argv, '/')) {
|
||||
family = strsep (&argv, "/");
|
||||
key = strsep (&argv, "\0");
|
||||
if (!family || !key) {
|
||||
ast_log(LOG_DEBUG, "Ignoring; Syntax error in argument\n");
|
||||
ast_module_user_remove(u);
|
||||
ast_log (LOG_DEBUG, "Ignoring; Syntax error in argument\n");
|
||||
return 0;
|
||||
}
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "DBdel: family=%s, key=%s\n", family, key);
|
||||
if (ast_db_del(family, key)) {
|
||||
ast_verbose (VERBOSE_PREFIX_3 "DBdel: family=%s, key=%s\n", family, key);
|
||||
if (ast_db_del (family, key)) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "DBdel: Error deleting key from database.\n");
|
||||
ast_verbose (VERBOSE_PREFIX_3 "DBdel: Error deleting key from database.\n");
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_DEBUG, "Ignoring, no parameters\n");
|
||||
ast_log (LOG_DEBUG, "Ignoring, no parameters\n");
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
static int put_exec (struct ast_channel *chan, void *data)
|
||||
{
|
||||
int arglen;
|
||||
char *argv, *value, *family, *key;
|
||||
|
||||
arglen = strlen (data);
|
||||
argv = alloca (arglen + 1);
|
||||
if (!argv) { /* Why would this fail? */
|
||||
ast_log (LOG_DEBUG, "Memory allocation failed\n");
|
||||
return 0;
|
||||
}
|
||||
memcpy (argv, data, arglen + 1);
|
||||
|
||||
if (strchr (argv, '/') && strchr (argv, '=')) {
|
||||
family = strsep (&argv, "/");
|
||||
key = strsep (&argv, "=");
|
||||
value = strsep (&argv, "\0");
|
||||
if (!value || !family || !key) {
|
||||
ast_log (LOG_DEBUG, "Ignoring; Syntax error in argument\n");
|
||||
return 0;
|
||||
}
|
||||
if (option_verbose > 2)
|
||||
ast_verbose (VERBOSE_PREFIX_3 "DBput: family=%s, key=%s, value=%s\n", family, key, value);
|
||||
if (ast_db_put (family, key, value)) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose (VERBOSE_PREFIX_3 "DBput: Error writing value to database.\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
ast_log (LOG_DEBUG, "Ignoring, no parameters\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_exec (struct ast_channel *chan, void *data)
|
||||
{
|
||||
int arglen;
|
||||
char *argv, *varname, *family, *key;
|
||||
char dbresult[256];
|
||||
|
||||
arglen = strlen (data);
|
||||
argv = alloca (arglen + 1);
|
||||
if (!argv) { /* Why would this fail? */
|
||||
ast_log (LOG_DEBUG, "Memory allocation failed\n");
|
||||
return 0;
|
||||
}
|
||||
memcpy (argv, data, arglen + 1);
|
||||
|
||||
if (strchr (argv, '=') && strchr (argv, '/')) {
|
||||
varname = strsep (&argv, "=");
|
||||
family = strsep (&argv, "/");
|
||||
key = strsep (&argv, "\0");
|
||||
if (!varname || !family || !key) {
|
||||
ast_log (LOG_DEBUG, "Ignoring; Syntax error in argument\n");
|
||||
return 0;
|
||||
}
|
||||
if (option_verbose > 2)
|
||||
ast_verbose (VERBOSE_PREFIX_3 "DBget: varname=%s, family=%s, key=%s\n", varname, family, key);
|
||||
if (!ast_db_get (family, key, dbresult, sizeof (dbresult) - 1)) {
|
||||
pbx_builtin_setvar_helper (chan, varname, dbresult);
|
||||
if (option_verbose > 2)
|
||||
ast_verbose (VERBOSE_PREFIX_3 "DBget: set variable %s to %s\n", varname, dbresult);
|
||||
} else {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose (VERBOSE_PREFIX_3 "DBget: Value not found in database.\n");
|
||||
/* Send the call to n+101 priority, where n is the current priority */
|
||||
if (ast_exists_extension (chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
|
||||
chan->priority += 100;
|
||||
}
|
||||
|
||||
} else {
|
||||
ast_log (LOG_DEBUG, "Ignoring, no parameters\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unload_module (void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = ast_unregister_application(dt_app);
|
||||
retval |= ast_unregister_application(d_app);
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
retval = ast_unregister_application (dt_app);
|
||||
retval |= ast_unregister_application (d_app);
|
||||
retval |= ast_unregister_application (p_app);
|
||||
retval |= ast_unregister_application (g_app);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module (void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = ast_register_application(d_app, del_exec, d_synopsis, d_descrip);
|
||||
retval |= ast_register_application(dt_app, deltree_exec, dt_synopsis, dt_descrip);
|
||||
|
||||
retval = ast_register_application (g_app, get_exec, g_synopsis, g_descrip);
|
||||
if (!retval)
|
||||
retval = ast_register_application (p_app, put_exec, p_synopsis, p_descrip);
|
||||
if (!retval)
|
||||
retval = ast_register_application (d_app, del_exec, d_synopsis, d_descrip);
|
||||
if (!retval)
|
||||
retval = ast_register_application (dt_app, deltree_exec, dt_synopsis, dt_descrip);
|
||||
return retval;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Database Access Functions");
|
||||
char *description (void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount (void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT (res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key ()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
2586
apps/app_dial.c
2586
apps/app_dial.c
File diff suppressed because it is too large
Load Diff
@@ -1,349 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2005, Anthony Minessale II
|
||||
*
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* Donated by Sangoma Technologies <http://www.samgoma.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Virtual Dictation Machine Application For Asterisk
|
||||
*
|
||||
* \author Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/say.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/app.h"
|
||||
|
||||
static char *app = "Dictate";
|
||||
static char *synopsis = "Virtual Dictation Machine";
|
||||
static char *desc = " Dictate([<base_dir>[|<filename>]])\n"
|
||||
"Start dictation machine using optional base dir for files.\n";
|
||||
|
||||
|
||||
typedef enum {
|
||||
DFLAG_RECORD = (1 << 0),
|
||||
DFLAG_PLAY = (1 << 1),
|
||||
DFLAG_TRUNC = (1 << 2),
|
||||
DFLAG_PAUSE = (1 << 3),
|
||||
} dflags;
|
||||
|
||||
typedef enum {
|
||||
DMODE_INIT,
|
||||
DMODE_RECORD,
|
||||
DMODE_PLAY
|
||||
} dmodes;
|
||||
|
||||
#define ast_toggle_flag(it,flag) if(ast_test_flag(it, flag)) ast_clear_flag(it, flag); else ast_set_flag(it, flag)
|
||||
|
||||
static int play_and_wait(struct ast_channel *chan, char *file, char *digits)
|
||||
{
|
||||
int res = -1;
|
||||
if (!ast_streamfile(chan, file, chan->language)) {
|
||||
res = ast_waitstream(chan, digits);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int dictate_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
char *path = NULL, filein[256], *filename = "";
|
||||
char *parse;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(base);
|
||||
AST_APP_ARG(filename);
|
||||
);
|
||||
char dftbase[256];
|
||||
char *base;
|
||||
struct ast_flags flags = {0};
|
||||
struct ast_filestream *fs;
|
||||
struct ast_frame *f = NULL;
|
||||
struct ast_module_user *u;
|
||||
int ffactor = 320 * 80,
|
||||
res = 0,
|
||||
done = 0,
|
||||
oldr = 0,
|
||||
lastop = 0,
|
||||
samples = 0,
|
||||
speed = 1,
|
||||
digit = 0,
|
||||
len = 0,
|
||||
maxlen = 0,
|
||||
mode = 0;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
snprintf(dftbase, sizeof(dftbase), "%s/dictate", ast_config_AST_SPOOL_DIR);
|
||||
if (!ast_strlen_zero(data)) {
|
||||
parse = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
} else
|
||||
args.argc = 0;
|
||||
|
||||
if (args.argc && !ast_strlen_zero(args.base)) {
|
||||
base = args.base;
|
||||
} else {
|
||||
base = dftbase;
|
||||
}
|
||||
if (args.argc > 1 && args.filename) {
|
||||
filename = args.filename;
|
||||
}
|
||||
oldr = chan->readformat;
|
||||
if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR)) < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set to linear mode.\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_answer(chan);
|
||||
ast_safe_sleep(chan, 200);
|
||||
for (res = 0; !res;) {
|
||||
if (ast_strlen_zero(filename)) {
|
||||
if (ast_app_getdata(chan, "dictate/enter_filename", filein, sizeof(filein), 0) ||
|
||||
ast_strlen_zero(filein)) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ast_copy_string(filein, filename, sizeof(filein));
|
||||
filename = "";
|
||||
}
|
||||
mkdir(base, 0755);
|
||||
len = strlen(base) + strlen(filein) + 2;
|
||||
if (!path || len > maxlen) {
|
||||
path = alloca(len);
|
||||
memset(path, 0, len);
|
||||
maxlen = len;
|
||||
} else {
|
||||
memset(path, 0, maxlen);
|
||||
}
|
||||
|
||||
snprintf(path, len, "%s/%s", base, filein);
|
||||
fs = ast_writefile(path, "raw", NULL, O_CREAT|O_APPEND, 0, 0700);
|
||||
mode = DMODE_PLAY;
|
||||
memset(&flags, 0, sizeof(flags));
|
||||
ast_set_flag(&flags, DFLAG_PAUSE);
|
||||
digit = play_and_wait(chan, "dictate/forhelp", AST_DIGIT_ANY);
|
||||
done = 0;
|
||||
speed = 1;
|
||||
res = 0;
|
||||
lastop = 0;
|
||||
samples = 0;
|
||||
while (!done && ((res = ast_waitfor(chan, -1)) > -1) && fs && (f = ast_read(chan))) {
|
||||
if (digit) {
|
||||
struct ast_frame fr = {AST_FRAME_DTMF, digit};
|
||||
ast_queue_frame(chan, &fr);
|
||||
digit = 0;
|
||||
}
|
||||
if ((f->frametype == AST_FRAME_DTMF)) {
|
||||
int got = 1;
|
||||
switch(mode) {
|
||||
case DMODE_PLAY:
|
||||
switch(f->subclass) {
|
||||
case '1':
|
||||
ast_set_flag(&flags, DFLAG_PAUSE);
|
||||
mode = DMODE_RECORD;
|
||||
break;
|
||||
case '2':
|
||||
speed++;
|
||||
if (speed > 4) {
|
||||
speed = 1;
|
||||
}
|
||||
res = ast_say_number(chan, speed, AST_DIGIT_ANY, chan->language, (char *) NULL);
|
||||
break;
|
||||
case '7':
|
||||
samples -= ffactor;
|
||||
if(samples < 0) {
|
||||
samples = 0;
|
||||
}
|
||||
ast_seekstream(fs, samples, SEEK_SET);
|
||||
break;
|
||||
case '8':
|
||||
samples += ffactor;
|
||||
ast_seekstream(fs, samples, SEEK_SET);
|
||||
break;
|
||||
|
||||
default:
|
||||
got = 0;
|
||||
}
|
||||
break;
|
||||
case DMODE_RECORD:
|
||||
switch(f->subclass) {
|
||||
case '1':
|
||||
ast_set_flag(&flags, DFLAG_PAUSE);
|
||||
mode = DMODE_PLAY;
|
||||
break;
|
||||
case '8':
|
||||
ast_toggle_flag(&flags, DFLAG_TRUNC);
|
||||
lastop = 0;
|
||||
break;
|
||||
default:
|
||||
got = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
got = 0;
|
||||
}
|
||||
if (!got) {
|
||||
switch(f->subclass) {
|
||||
case '#':
|
||||
done = 1;
|
||||
continue;
|
||||
break;
|
||||
case '*':
|
||||
ast_toggle_flag(&flags, DFLAG_PAUSE);
|
||||
if (ast_test_flag(&flags, DFLAG_PAUSE)) {
|
||||
digit = play_and_wait(chan, "dictate/pause", AST_DIGIT_ANY);
|
||||
} else {
|
||||
digit = play_and_wait(chan, mode == DMODE_PLAY ? "dictate/playback" : "dictate/record", AST_DIGIT_ANY);
|
||||
}
|
||||
break;
|
||||
case '0':
|
||||
ast_set_flag(&flags, DFLAG_PAUSE);
|
||||
digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
|
||||
switch(mode) {
|
||||
case DMODE_PLAY:
|
||||
digit = play_and_wait(chan, "dictate/play_help", AST_DIGIT_ANY);
|
||||
break;
|
||||
case DMODE_RECORD:
|
||||
digit = play_and_wait(chan, "dictate/record_help", AST_DIGIT_ANY);
|
||||
break;
|
||||
}
|
||||
if (digit == 0) {
|
||||
digit = play_and_wait(chan, "dictate/both_help", AST_DIGIT_ANY);
|
||||
} else if (digit < 0) {
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (f->frametype == AST_FRAME_VOICE) {
|
||||
switch(mode) {
|
||||
struct ast_frame *fr;
|
||||
int x;
|
||||
case DMODE_PLAY:
|
||||
if (lastop != DMODE_PLAY) {
|
||||
if (ast_test_flag(&flags, DFLAG_PAUSE)) {
|
||||
digit = play_and_wait(chan, "dictate/playback_mode", AST_DIGIT_ANY);
|
||||
if (digit == 0) {
|
||||
digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
|
||||
} else if (digit < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lastop != DFLAG_PLAY) {
|
||||
lastop = DFLAG_PLAY;
|
||||
ast_closestream(fs);
|
||||
if (!(fs = ast_openstream(chan, path, chan->language)))
|
||||
break;
|
||||
ast_seekstream(fs, samples, SEEK_SET);
|
||||
chan->stream = NULL;
|
||||
}
|
||||
lastop = DMODE_PLAY;
|
||||
}
|
||||
|
||||
if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
|
||||
for (x = 0; x < speed; x++) {
|
||||
if ((fr = ast_readframe(fs))) {
|
||||
ast_write(chan, fr);
|
||||
samples += fr->samples;
|
||||
ast_frfree(fr);
|
||||
fr = NULL;
|
||||
} else {
|
||||
samples = 0;
|
||||
ast_seekstream(fs, 0, SEEK_SET);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DMODE_RECORD:
|
||||
if (lastop != DMODE_RECORD) {
|
||||
int oflags = O_CREAT | O_WRONLY;
|
||||
if (ast_test_flag(&flags, DFLAG_PAUSE)) {
|
||||
digit = play_and_wait(chan, "dictate/record_mode", AST_DIGIT_ANY);
|
||||
if (digit == 0) {
|
||||
digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
|
||||
} else if (digit < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
lastop = DMODE_RECORD;
|
||||
ast_closestream(fs);
|
||||
if ( ast_test_flag(&flags, DFLAG_TRUNC)) {
|
||||
oflags |= O_TRUNC;
|
||||
digit = play_and_wait(chan, "dictate/truncating_audio", AST_DIGIT_ANY);
|
||||
} else {
|
||||
oflags |= O_APPEND;
|
||||
}
|
||||
fs = ast_writefile(path, "raw", NULL, oflags, 0, 0700);
|
||||
if (ast_test_flag(&flags, DFLAG_TRUNC)) {
|
||||
ast_seekstream(fs, 0, SEEK_SET);
|
||||
ast_clear_flag(&flags, DFLAG_TRUNC);
|
||||
} else {
|
||||
ast_seekstream(fs, 0, SEEK_END);
|
||||
}
|
||||
}
|
||||
if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
|
||||
res = ast_writestream(fs, f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ast_frfree(f);
|
||||
}
|
||||
}
|
||||
if (oldr) {
|
||||
ast_set_read_format(chan, oldr);
|
||||
}
|
||||
ast_module_user_remove(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
res = ast_unregister_application(app);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, dictate_exec, synopsis, desc);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Virtual Dictation Machine");
|
||||
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2005, Joshua Colp
|
||||
*
|
||||
* Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Directed Call Pickup Support
|
||||
*
|
||||
* \author Joshua Colp <jcolp@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/options.h"
|
||||
|
||||
#define PICKUPMARK "PICKUPMARK"
|
||||
|
||||
static const char *app = "Pickup";
|
||||
static const char *synopsis = "Directed Call Pickup";
|
||||
static const char *descrip =
|
||||
" Pickup(extension[@context][&extension2@context...]): This application can pickup any ringing channel\n"
|
||||
"that is calling the specified extension. If no context is specified, the current\n"
|
||||
"context will be used. If you use the special string \"PICKUPMARK\" for the context parameter, for example\n"
|
||||
"10@PICKUPMARK, this application tries to find a channel which has defined a channel variable with the same content\n"
|
||||
"as \"extension\".";
|
||||
|
||||
/* Perform actual pickup between two channels */
|
||||
static int pickup_do(struct ast_channel *chan, struct ast_channel *target)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Call pickup on '%s' by '%s'\n", target->name, chan->name);
|
||||
|
||||
if ((res = ast_answer(chan))) {
|
||||
ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((res = ast_queue_control(chan, AST_CONTROL_ANSWER))) {
|
||||
ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((res = ast_channel_masquerade(target, chan))) {
|
||||
ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Helper function that determines whether a channel is capable of being picked up */
|
||||
static int can_pickup(struct ast_channel *chan)
|
||||
{
|
||||
if (!chan->pbx && (chan->_state == AST_STATE_RINGING || chan->_state == AST_STATE_RING || chan->_state == AST_STATE_DOWN))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Attempt to pick up specified extension with context */
|
||||
static int pickup_by_exten(struct ast_channel *chan, char *exten, char *context)
|
||||
{
|
||||
int res = -1;
|
||||
struct ast_channel *target = NULL;
|
||||
|
||||
while ((target = ast_channel_walk_locked(target))) {
|
||||
if ((!strcasecmp(target->macroexten, exten) || !strcasecmp(target->exten, exten)) &&
|
||||
!strcasecmp(target->dialcontext, context) &&
|
||||
(chan != target) && can_pickup(target)) {
|
||||
res = pickup_do(chan, target);
|
||||
ast_channel_unlock(target);
|
||||
break;
|
||||
}
|
||||
ast_channel_unlock(target);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Attempt to pick up specified mark */
|
||||
static int pickup_by_mark(struct ast_channel *chan, char *mark)
|
||||
{
|
||||
int res = -1;
|
||||
const char *tmp = NULL;
|
||||
struct ast_channel *target = NULL;
|
||||
|
||||
while ((target = ast_channel_walk_locked(target))) {
|
||||
if ((tmp = pbx_builtin_getvar_helper(target, PICKUPMARK)) &&
|
||||
!strcasecmp(tmp, mark) &&
|
||||
can_pickup(target)) {
|
||||
res = pickup_do(chan, target);
|
||||
ast_channel_unlock(target);
|
||||
break;
|
||||
}
|
||||
ast_channel_unlock(target);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Main application entry point */
|
||||
static int pickup_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_module_user *u = NULL;
|
||||
char *tmp = ast_strdupa(data);
|
||||
char *exten = NULL, *context = NULL;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Pickup requires an argument (extension)!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
/* Parse extension (and context if there) */
|
||||
while (!ast_strlen_zero(tmp) && (exten = strsep(&tmp, "&"))) {
|
||||
if ((context = strchr(exten, '@')))
|
||||
*context++ = '\0';
|
||||
if (context && !strcasecmp(context, PICKUPMARK)) {
|
||||
if (!pickup_by_mark(chan, exten))
|
||||
break;
|
||||
} else {
|
||||
if (!pickup_by_exten(chan, exten, context ? context : chan->context))
|
||||
break;
|
||||
}
|
||||
ast_log(LOG_NOTICE, "No target channel found for %s.\n", exten);
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, pickup_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Directed Call Pickup Application");
|
||||
@@ -1,213 +1,64 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Provide a directory of extensions
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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 Provide a directory of extensions
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/config.h>
|
||||
#include <asterisk/say.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "../asterisk.h"
|
||||
#include "../astconf.h"
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/say.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/app.h"
|
||||
|
||||
#ifdef ODBC_STORAGE
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include "asterisk/res_odbc.h"
|
||||
|
||||
static char odbc_database[80] = "asterisk";
|
||||
static char odbc_table[80] = "voicemessages";
|
||||
static char vmfmts[80] = "wav";
|
||||
#endif
|
||||
|
||||
static char *tdesc = "Extension Directory";
|
||||
static char *app = "Directory";
|
||||
|
||||
static char *synopsis = "Provide directory of voicemail extensions";
|
||||
static char *descrip =
|
||||
" Directory(vm-context[|dial-context[|options]]): This application will present\n"
|
||||
"the calling channel with a directory of extensions from which they can search\n"
|
||||
"by name. The list of names and corresponding extensions is retrieved from the\n"
|
||||
"voicemail configuration file, voicemail.conf.\n"
|
||||
" This application will immediately exit if one of the following DTMF digits are\n"
|
||||
"received and the extension to jump to exists:\n"
|
||||
" 0 - Jump to the 'o' extension, if it exists.\n"
|
||||
" * - Jump to the 'a' extension, if it exists.\n\n"
|
||||
" Parameters:\n"
|
||||
" vm-context - This is the context within voicemail.conf to use for the\n"
|
||||
" Directory.\n"
|
||||
" dial-context - This is the dialplan context to use when looking for an\n"
|
||||
" extension that the user has selected, or when jumping to the\n"
|
||||
" 'o' or 'a' extension.\n\n"
|
||||
" Options:\n"
|
||||
" e - In addition to the name, also read the extension number to the\n"
|
||||
" caller before presenting dialing options.\n"
|
||||
" f - Allow the caller to enter the first name of a user in the directory\n"
|
||||
" instead of using the last name.\n";
|
||||
" Directory(vm-context[|dial-context[|options]]): Presents the user with a directory\n"
|
||||
"of extensions from which they may select by name. The list of names \n"
|
||||
"and extensions is discovered from voicemail.conf. The vm-context argument\n"
|
||||
"is required, and specifies the context of voicemail.conf to use. The\n"
|
||||
"dial-context is the context to use for dialing the users, and defaults to\n"
|
||||
"the vm-context if unspecified. The 'f' option causes the directory to match\n"
|
||||
"based on the first name in voicemail.conf instead of the last name.\n"
|
||||
"Returns 0 unless the user hangs up. It also sets up the channel on exit\n"
|
||||
"to enter the extension the user selected.\n";
|
||||
|
||||
/* For simplicity, I'm keeping the format compatible with the voicemail config,
|
||||
but i'm open to suggestions for isolating it */
|
||||
|
||||
#define VOICEMAIL_CONFIG "voicemail.conf"
|
||||
#define DIRECTORY_CONFIG "voicemail.conf"
|
||||
|
||||
/* How many digits to read in */
|
||||
#define NUMDIGITS 3
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
#ifdef ODBC_STORAGE
|
||||
struct generic_prepare_struct {
|
||||
const char *sql;
|
||||
const char *param;
|
||||
};
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
|
||||
{
|
||||
struct generic_prepare_struct *gps = data;
|
||||
SQLHSTMT stmt;
|
||||
int res;
|
||||
|
||||
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
|
||||
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||
ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
|
||||
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||
ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *)gps->sql);
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(gps->param))
|
||||
SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->param), 0, (void *)gps->param, 0, NULL);
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
static void retrieve_file(char *dir)
|
||||
{
|
||||
int x = 0;
|
||||
int res;
|
||||
int fd=-1;
|
||||
size_t fdlen = 0;
|
||||
void *fdm = MAP_FAILED;
|
||||
SQLHSTMT stmt;
|
||||
char sql[256];
|
||||
char fmt[80]="", empty[10] = "";
|
||||
char *c;
|
||||
SQLLEN colsize;
|
||||
char full_fn[256];
|
||||
struct odbc_obj *obj;
|
||||
struct generic_prepare_struct gps = { .sql = sql, .param = dir };
|
||||
|
||||
obj = ast_odbc_request_obj(odbc_database, 1);
|
||||
if (obj) {
|
||||
do {
|
||||
ast_copy_string(fmt, vmfmts, sizeof(fmt));
|
||||
c = strchr(fmt, '|');
|
||||
if (c)
|
||||
*c = '\0';
|
||||
if (!strcasecmp(fmt, "wav49"))
|
||||
strcpy(fmt, "WAV");
|
||||
snprintf(full_fn, sizeof(full_fn), "%s.%s", dir, fmt);
|
||||
snprintf(sql, sizeof(sql), "SELECT recording FROM %s WHERE dir=? AND msgnum=-1", odbc_table);
|
||||
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
|
||||
|
||||
if (!stmt) {
|
||||
ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
|
||||
break;
|
||||
}
|
||||
res = SQLFetch(stmt);
|
||||
if (res == SQL_NO_DATA) {
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
break;
|
||||
} else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||
ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
break;
|
||||
}
|
||||
fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
|
||||
if (fd < 0) {
|
||||
ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
break;
|
||||
}
|
||||
|
||||
res = SQLGetData(stmt, 1, SQL_BINARY, empty, 0, &colsize);
|
||||
fdlen = colsize;
|
||||
if (fd > -1) {
|
||||
char tmp[1]="";
|
||||
lseek(fd, fdlen - 1, SEEK_SET);
|
||||
if (write(fd, tmp, 1) != 1) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
break;
|
||||
}
|
||||
if (fd > -1)
|
||||
fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
}
|
||||
if (fdm != MAP_FAILED) {
|
||||
memset(fdm, 0, fdlen);
|
||||
res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
|
||||
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
|
||||
ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
|
||||
} while (0);
|
||||
ast_odbc_release_obj(obj);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
|
||||
if (fdm != MAP_FAILED)
|
||||
munmap(fdm, fdlen);
|
||||
if (fd > -1)
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char *convert(const char *lastname)
|
||||
static char *convert(char *lastname)
|
||||
{
|
||||
char *tmp;
|
||||
int lcount = 0;
|
||||
tmp = ast_malloc(NUMDIGITS + 1);
|
||||
tmp = malloc(NUMDIGITS + 1);
|
||||
if (tmp) {
|
||||
while((*lastname > 32) && lcount < NUMDIGITS) {
|
||||
switch(toupper(*lastname)) {
|
||||
@@ -277,195 +128,105 @@ static char *convert(const char *lastname)
|
||||
* '1' for selected entry from directory
|
||||
* '*' for skipped entry from directory
|
||||
*/
|
||||
static int play_mailbox_owner(struct ast_channel *chan, char *context,
|
||||
char *dialcontext, char *ext, char *name, int readext,
|
||||
int fromappvm)
|
||||
{
|
||||
static int play_mailbox_owner(struct ast_channel *chan, char *context, char *dialcontext, char *ext, char *name) {
|
||||
int res = 0;
|
||||
int loop;
|
||||
int loop = 3;
|
||||
char fn[256];
|
||||
char fn2[256];
|
||||
|
||||
/* Check for the VoiceMail2 greeting first */
|
||||
snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
|
||||
ast_config_AST_SPOOL_DIR, context, ext);
|
||||
#ifdef ODBC_STORAGE
|
||||
retrieve_file(fn);
|
||||
#endif
|
||||
(char *)ast_config_AST_SPOOL_DIR, context, ext);
|
||||
|
||||
if (ast_fileexists(fn, NULL, chan->language) <= 0) {
|
||||
/* no file, check for an old-style Voicemail greeting */
|
||||
snprintf(fn, sizeof(fn), "%s/vm/%s/greet",
|
||||
ast_config_AST_SPOOL_DIR, ext);
|
||||
}
|
||||
#ifdef ODBC_STORAGE
|
||||
retrieve_file(fn);
|
||||
#endif
|
||||
/* Otherwise, check for an old-style Voicemail greeting */
|
||||
snprintf(fn2, sizeof(fn2), "%s/vm/%s/greet",
|
||||
(char *)ast_config_AST_SPOOL_DIR, ext);
|
||||
|
||||
if (ast_fileexists(fn, NULL, chan->language) > 0) {
|
||||
res = ast_stream_and_wait(chan, fn, chan->language, AST_DIGIT_ANY);
|
||||
res = ast_streamfile(chan, fn, chan->language);
|
||||
if (!res) {
|
||||
res = ast_waitstream(chan, AST_DIGIT_ANY);
|
||||
}
|
||||
ast_stopstream(chan);
|
||||
/* If Option 'e' was specified, also read the extension number with the name */
|
||||
if (readext) {
|
||||
ast_stream_and_wait(chan, "vm-extension", chan->language, AST_DIGIT_ANY);
|
||||
res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
|
||||
} else if (ast_fileexists(fn2, NULL, chan->language) > 0) {
|
||||
res = ast_streamfile(chan, fn2, chan->language);
|
||||
if (!res) {
|
||||
res = ast_waitstream(chan, AST_DIGIT_ANY);
|
||||
}
|
||||
ast_stopstream(chan);
|
||||
} else {
|
||||
res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, chan->language);
|
||||
if (!ast_strlen_zero(name) && readext) {
|
||||
ast_stream_and_wait(chan, "vm-extension", chan->language, AST_DIGIT_ANY);
|
||||
res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
|
||||
}
|
||||
res = ast_say_character_str(chan, !ast_strlen_zero(name) ? name : ext,
|
||||
AST_DIGIT_ANY, chan->language);
|
||||
}
|
||||
#ifdef ODBC_STORAGE
|
||||
ast_filedelete(fn, NULL);
|
||||
#endif
|
||||
|
||||
for (loop = 3 ; loop > 0; loop--) {
|
||||
if (!res)
|
||||
res = ast_stream_and_wait(chan, "dir-instr", chan->language, AST_DIGIT_ANY);
|
||||
if (!res)
|
||||
while (loop) {
|
||||
if (!res) {
|
||||
res = ast_streamfile(chan, "dir-instr", chan->language);
|
||||
}
|
||||
if (!res) {
|
||||
res = ast_waitstream(chan, AST_DIGIT_ANY);
|
||||
}
|
||||
if (!res) {
|
||||
res = ast_waitfordigit(chan, 3000);
|
||||
}
|
||||
ast_stopstream(chan);
|
||||
|
||||
if (res < 0) /* User hungup, so jump out now */
|
||||
break;
|
||||
if (res == '0') {
|
||||
if (!ast_goto_if_exists(chan, dialcontext, "o", 1) ||
|
||||
(!ast_strlen_zero(chan->macrocontext) &&
|
||||
!ast_goto_if_exists(chan, chan->macrocontext, "o", 1))) {
|
||||
/* return 1 to indicate goto has been performed */
|
||||
return '1';
|
||||
}
|
||||
if (res > -1) {
|
||||
switch (res) {
|
||||
case '1':
|
||||
/* Name selected */
|
||||
loop = 0;
|
||||
if (ast_exists_extension(chan,dialcontext,ext,1,chan->callerid)) {
|
||||
strncpy(chan->exten, ext, sizeof(chan->exten)-1);
|
||||
chan->priority = 0;
|
||||
strncpy(chan->context, dialcontext, sizeof(chan->context)-1);
|
||||
} else {
|
||||
ast_log(LOG_WARNING,
|
||||
"Can't find extension '%s' in context '%s'. "
|
||||
"Did you pass the wrong context to Directory?\n",
|
||||
ext, dialcontext);
|
||||
res = -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case '*':
|
||||
/* Skip to next match in list */
|
||||
loop = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Not '1', or '*', so decrement number of tries */
|
||||
res = 0;
|
||||
loop--;
|
||||
break;
|
||||
} /* end switch */
|
||||
} /* end if */
|
||||
else {
|
||||
/* User hungup, so jump out now */
|
||||
loop = 0;
|
||||
}
|
||||
if (res == '1') { /* Name selected */
|
||||
if (fromappvm) {
|
||||
/* We still want to set the exten though */
|
||||
ast_copy_string(chan->exten, ext, sizeof(chan->exten));
|
||||
} else {
|
||||
if (ast_goto_if_exists(chan, dialcontext, ext, 1)) {
|
||||
ast_log(LOG_WARNING,
|
||||
"Can't find extension '%s' in context '%s'. "
|
||||
"Did you pass the wrong context to Directory?\n",
|
||||
ext, dialcontext);
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (res == '*') /* Skip to next match in list */
|
||||
break;
|
||||
if (res == '#')
|
||||
break;
|
||||
|
||||
/* Not '1', or '*', so decrement number of tries */
|
||||
res = 0;
|
||||
}
|
||||
} /* end while */
|
||||
|
||||
return(res);
|
||||
}
|
||||
|
||||
static struct ast_config *realtime_directory(char *context)
|
||||
{
|
||||
struct ast_config *cfg;
|
||||
struct ast_config *rtdata;
|
||||
struct ast_category *cat;
|
||||
struct ast_variable *var;
|
||||
char *mailbox;
|
||||
const char *fullname;
|
||||
const char *hidefromdir;
|
||||
char tmp[100];
|
||||
|
||||
/* Load flat file config. */
|
||||
cfg = ast_config_load(VOICEMAIL_CONFIG);
|
||||
|
||||
if (!cfg) {
|
||||
/* Loading config failed. */
|
||||
ast_log(LOG_WARNING, "Loading config failed.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get realtime entries, categorized by their mailbox number
|
||||
and present in the requested context */
|
||||
rtdata = ast_load_realtime_multientry("voicemail", "mailbox LIKE", "%", "context", context, NULL);
|
||||
|
||||
/* if there are no results, just return the entries from the config file */
|
||||
if (!rtdata)
|
||||
return cfg;
|
||||
|
||||
/* Does the context exist within the config file? If not, make one */
|
||||
cat = ast_category_get(cfg, context);
|
||||
if (!cat) {
|
||||
cat = ast_category_new(context);
|
||||
if (!cat) {
|
||||
ast_log(LOG_WARNING, "Out of memory\n");
|
||||
ast_config_destroy(cfg);
|
||||
if (rtdata) {
|
||||
ast_config_destroy(rtdata);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
ast_category_append(cfg, cat);
|
||||
}
|
||||
|
||||
mailbox = NULL;
|
||||
while ( (mailbox = ast_category_browse(rtdata, mailbox)) ) {
|
||||
fullname = ast_variable_retrieve(rtdata, mailbox, "fullname");
|
||||
hidefromdir = ast_variable_retrieve(rtdata, mailbox, "hidefromdir");
|
||||
snprintf(tmp, sizeof(tmp), "no-password,%s,hidefromdir=%s",
|
||||
fullname ? fullname : "",
|
||||
hidefromdir ? hidefromdir : "no");
|
||||
var = ast_variable_new(mailbox, tmp);
|
||||
if (var)
|
||||
ast_variable_append(cat, var);
|
||||
else
|
||||
ast_log(LOG_WARNING, "Out of memory adding mailbox '%s'\n", mailbox);
|
||||
}
|
||||
ast_config_destroy(rtdata);
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
static int do_directory(struct ast_channel *chan, struct ast_config *cfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, int last, int readext, int fromappvm)
|
||||
static int do_directory(struct ast_channel *chan, struct ast_config *cfg, char *context, char *dialcontext, char digit, int last)
|
||||
{
|
||||
/* Read in the first three digits.. "digit" is the first digit, already read */
|
||||
char ext[NUMDIGITS + 1], *cat;
|
||||
char ext[NUMDIGITS + 1];
|
||||
char name[80] = "";
|
||||
struct ast_variable *v;
|
||||
int res;
|
||||
int found=0;
|
||||
int lastuserchoice = 0;
|
||||
char *start, *conv, *stringp = NULL;
|
||||
const char *pos;
|
||||
int breakout = 0;
|
||||
char *start, *pos, *conv,*stringp=NULL;
|
||||
|
||||
if (ast_strlen_zero(context)) {
|
||||
if (!context || ast_strlen_zero(context)) {
|
||||
ast_log(LOG_WARNING,
|
||||
"Directory must be called with an argument "
|
||||
"(context in which to interpret extensions)\n");
|
||||
return -1;
|
||||
}
|
||||
if (digit == '0') {
|
||||
if (!ast_goto_if_exists(chan, dialcontext, "o", 1) ||
|
||||
(!ast_strlen_zero(chan->macrocontext) &&
|
||||
!ast_goto_if_exists(chan, chan->macrocontext, "o", 1))) {
|
||||
return 0;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Can't find extension 'o' in current context. "
|
||||
"Not Exiting the Directory!\n");
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
if (digit == '*') {
|
||||
if (!ast_goto_if_exists(chan, dialcontext, "a", 1) ||
|
||||
(!ast_strlen_zero(chan->macrocontext) &&
|
||||
!ast_goto_if_exists(chan, chan->macrocontext, "a", 1))) {
|
||||
return 0;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Can't find extension 'a' in current context. "
|
||||
"Not Exiting the Directory!\n");
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
memset(ext, 0, sizeof(ext));
|
||||
ext[0] = digit;
|
||||
res = 0;
|
||||
@@ -478,18 +239,18 @@ static int do_directory(struct ast_channel *chan, struct ast_config *cfg, struct
|
||||
while(v) {
|
||||
/* Find a candidate extension */
|
||||
start = strdup(v->value);
|
||||
if (start && !strcasestr(start, "hidefromdir=yes")) {
|
||||
if (start) {
|
||||
stringp=start;
|
||||
strsep(&stringp, ",");
|
||||
pos = strsep(&stringp, ",");
|
||||
if (pos) {
|
||||
ast_copy_string(name, pos, sizeof(name));
|
||||
strncpy(name, pos, sizeof(name) - 1);
|
||||
/* Grab the last name */
|
||||
if (last && strrchr(pos,' '))
|
||||
pos = strrchr(pos, ' ') + 1;
|
||||
conv = convert(pos);
|
||||
if (conv) {
|
||||
if (!strncmp(conv, ext, strlen(ext))) {
|
||||
if (!strcmp(conv, ext)) {
|
||||
/* Match! */
|
||||
found++;
|
||||
free(conv);
|
||||
@@ -506,7 +267,7 @@ static int do_directory(struct ast_channel *chan, struct ast_config *cfg, struct
|
||||
|
||||
if (v) {
|
||||
/* We have a match -- play a greeting if they have it */
|
||||
res = play_mailbox_owner(chan, context, dialcontext, v->name, name, readext, fromappvm);
|
||||
res = play_mailbox_owner(chan, context, dialcontext, v->name, name);
|
||||
switch (res) {
|
||||
case -1:
|
||||
/* user pressed '1' but extension does not exist, or
|
||||
@@ -515,20 +276,17 @@ static int do_directory(struct ast_channel *chan, struct ast_config *cfg, struct
|
||||
lastuserchoice = 0;
|
||||
break;
|
||||
case '1':
|
||||
/* user pressed '1' and extensions exists;
|
||||
play_mailbox_owner will already have done
|
||||
a goto() on the channel
|
||||
*/
|
||||
/* user pressed '1' and extensions exists */
|
||||
lastuserchoice = res;
|
||||
strncpy(chan->context, dialcontext, sizeof(chan->context) - 1);
|
||||
strncpy(chan->exten, v->name, sizeof(chan->exten) - 1);
|
||||
chan->priority = 0;
|
||||
break;
|
||||
case '*':
|
||||
/* user pressed '*' to skip something found */
|
||||
lastuserchoice = res;
|
||||
res = 0;
|
||||
break;
|
||||
case '#':
|
||||
lastuserchoice = res;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -536,66 +294,11 @@ static int do_directory(struct ast_channel *chan, struct ast_config *cfg, struct
|
||||
}
|
||||
}
|
||||
|
||||
if (!res && ucfg) {
|
||||
/* Search users.conf for all names which start with those digits */
|
||||
for (cat = ast_category_browse(ucfg, NULL); cat && !res ; cat = ast_category_browse(ucfg, cat)) {
|
||||
if (!strcasecmp(cat, "general"))
|
||||
continue;
|
||||
if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
|
||||
continue;
|
||||
|
||||
/* Find all candidate extensions */
|
||||
if ((pos = ast_variable_retrieve(ucfg, cat, "fullname"))) {
|
||||
ast_copy_string(name, pos, sizeof(name));
|
||||
/* Grab the last name */
|
||||
if (last && strrchr(pos,' '))
|
||||
pos = strrchr(pos, ' ') + 1;
|
||||
conv = convert(pos);
|
||||
if (conv) {
|
||||
if (!strcmp(conv, ext)) {
|
||||
/* Match! */
|
||||
found++;
|
||||
/* We have a match -- play a greeting if they have it */
|
||||
res = play_mailbox_owner(chan, context, dialcontext, cat, name, readext, fromappvm);
|
||||
switch (res) {
|
||||
case -1:
|
||||
/* user pressed '1' but extension does not exist, or
|
||||
* user hungup
|
||||
*/
|
||||
lastuserchoice = 0;
|
||||
breakout = 1;
|
||||
break;
|
||||
case '1':
|
||||
/* user pressed '1' and extensions exists;
|
||||
play_mailbox_owner will already have done
|
||||
a goto() on the channel
|
||||
*/
|
||||
lastuserchoice = res;
|
||||
breakout = 1;
|
||||
break;
|
||||
case '*':
|
||||
/* user pressed '*' to skip something found */
|
||||
lastuserchoice = res;
|
||||
breakout = 0;
|
||||
res = 0;
|
||||
break;
|
||||
default:
|
||||
breakout = 1;
|
||||
break;
|
||||
}
|
||||
free(conv);
|
||||
if (breakout)
|
||||
break;
|
||||
}
|
||||
else
|
||||
free(conv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lastuserchoice != '1') {
|
||||
res = ast_streamfile(chan, found ? "dir-nomore" : "dir-nomatch", chan->language);
|
||||
if (found)
|
||||
res = ast_streamfile(chan, "dir-nomore", chan->language);
|
||||
else
|
||||
res = ast_streamfile(chan, "dir-nomatch", chan->language);
|
||||
if (!res)
|
||||
res = 1;
|
||||
return res;
|
||||
@@ -608,115 +311,93 @@ static int do_directory(struct ast_channel *chan, struct ast_config *cfg, struct
|
||||
static int directory_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_module_user *u;
|
||||
struct ast_config *cfg, *ucfg;
|
||||
struct localuser *u;
|
||||
struct ast_config *cfg;
|
||||
int last = 1;
|
||||
int readext = 0;
|
||||
int fromappvm = 0;
|
||||
const char *dirintro;
|
||||
char *parse;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(vmcontext);
|
||||
AST_APP_ARG(dialcontext);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
char *context, *dialcontext, *dirintro, *options;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
|
||||
if (!data) {
|
||||
ast_log(LOG_WARNING, "directory requires an argument (context[,dialcontext])\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
parse = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (args.options) {
|
||||
if (strchr(args.options, 'f'))
|
||||
last = 0;
|
||||
if (strchr(args.options, 'e'))
|
||||
readext = 1;
|
||||
if (strchr(args.options, 'v'))
|
||||
fromappvm = 1;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(args.dialcontext))
|
||||
args.dialcontext = args.vmcontext;
|
||||
|
||||
cfg = realtime_directory(args.vmcontext);
|
||||
cfg = ast_load(DIRECTORY_CONFIG);
|
||||
if (!cfg) {
|
||||
ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
|
||||
ast_module_user_remove(u);
|
||||
ast_log(LOG_WARNING, "Unable to open directory configuration %s\n", DIRECTORY_CONFIG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ucfg = ast_config_load("users.conf");
|
||||
|
||||
dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
|
||||
if (ast_strlen_zero(dirintro))
|
||||
LOCAL_USER_ADD(u);
|
||||
top:
|
||||
context = ast_strdupa(data);
|
||||
dialcontext = strchr(context, '|');
|
||||
if (dialcontext) {
|
||||
*dialcontext = '\0';
|
||||
dialcontext++;
|
||||
options = strchr(dialcontext, '|');
|
||||
if (options) {
|
||||
*options = '\0';
|
||||
options++;
|
||||
if (strchr(options, 'f'))
|
||||
last = 0;
|
||||
}
|
||||
} else
|
||||
dialcontext = context;
|
||||
dirintro = ast_variable_retrieve(cfg, context, "directoryintro");
|
||||
if (!dirintro || ast_strlen_zero(dirintro))
|
||||
dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
|
||||
if (ast_strlen_zero(dirintro))
|
||||
dirintro = last ? "dir-intro" : "dir-intro-fn";
|
||||
/* the above prompts probably should be modified to include 0 for dialing operator
|
||||
and # for exiting (continues in dialplan) */
|
||||
|
||||
if (!dirintro || ast_strlen_zero(dirintro)) {
|
||||
if (last)
|
||||
dirintro = "dir-intro";
|
||||
else
|
||||
dirintro = "dir-intro-fn";
|
||||
}
|
||||
if (chan->_state != AST_STATE_UP)
|
||||
res = ast_answer(chan);
|
||||
|
||||
for (;;) {
|
||||
if (!res)
|
||||
res = ast_stream_and_wait(chan, dirintro, chan->language, AST_DIGIT_ANY);
|
||||
ast_stopstream(chan);
|
||||
if (!res)
|
||||
res = ast_waitfordigit(chan, 5000);
|
||||
if (!res)
|
||||
res = ast_streamfile(chan, dirintro, chan->language);
|
||||
if (!res)
|
||||
res = ast_waitstream(chan, AST_DIGIT_ANY);
|
||||
ast_stopstream(chan);
|
||||
if (!res)
|
||||
res = ast_waitfordigit(chan, 5000);
|
||||
if (res > 0) {
|
||||
res = do_directory(chan, cfg, context, dialcontext, res, last);
|
||||
if (res > 0) {
|
||||
res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, last, readext, fromappvm);
|
||||
if (res > 0) {
|
||||
res = ast_waitstream(chan, AST_DIGIT_ANY);
|
||||
ast_stopstream(chan);
|
||||
if (res >= 0)
|
||||
continue;
|
||||
res = ast_waitstream(chan, AST_DIGIT_ANY);
|
||||
ast_stopstream(chan);
|
||||
if (res >= 0) {
|
||||
goto top;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ucfg)
|
||||
ast_config_destroy(ucfg);
|
||||
ast_config_destroy(cfg);
|
||||
ast_module_user_remove(u);
|
||||
ast_destroy(cfg);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
res = ast_unregister_application(app);
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
#ifdef ODBC_STORAGE
|
||||
struct ast_config *cfg = ast_config_load(VOICEMAIL_CONFIG);
|
||||
const char *tmp;
|
||||
|
||||
if (cfg) {
|
||||
if ((tmp = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
|
||||
ast_copy_string(odbc_database, tmp, sizeof(odbc_database));
|
||||
}
|
||||
if ((tmp = ast_variable_retrieve(cfg, "general", "odbctable"))) {
|
||||
ast_copy_string(odbc_table, tmp, sizeof(odbc_table));
|
||||
}
|
||||
if ((tmp = ast_variable_retrieve(cfg, "general", "format"))) {
|
||||
ast_copy_string(vmfmts, tmp, sizeof(vmfmts));
|
||||
}
|
||||
ast_config_destroy(cfg);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Unable to load " VOICEMAIL_CONFIG " - ODBC defaults will be used\n");
|
||||
#endif
|
||||
|
||||
return ast_register_application(app, directory_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Directory");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
454
apps/app_disa.c
454
apps/app_disa.c
@@ -1,80 +1,66 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* DISA -- Direct Inward System Access Application 6/20/2001
|
||||
*
|
||||
* Copyright (C) 2001, Linux Support Services, Inc.
|
||||
*
|
||||
* Jim Dixon <jim@lambdatel.com>
|
||||
*
|
||||
* Made only slightly more sane by Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief DISA -- Direct Inward System Access Application
|
||||
*
|
||||
* \author Jim Dixon <jim@lambdatel.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/indications.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/ulaw.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/indications.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/ulaw.h"
|
||||
#include "asterisk/callerid.h"
|
||||
#include "asterisk/stringfields.h"
|
||||
|
||||
static char *tdesc = "DISA (Direct Inward System Access) Application";
|
||||
|
||||
static char *app = "DISA";
|
||||
|
||||
static char *synopsis = "DISA (Direct Inward System Access)";
|
||||
|
||||
static char *descrip =
|
||||
"DISA(<numeric passcode>[|<context>]) or DISA(<filename>)\n"
|
||||
"The DISA, Direct Inward System Access, application allows someone from \n"
|
||||
"outside the telephone switch (PBX) to obtain an \"internal\" system \n"
|
||||
"dialtone and to place calls from it as if they were placing a call from \n"
|
||||
"within the switch.\n"
|
||||
"DISA plays a dialtone. The user enters their numeric passcode, followed by\n"
|
||||
"the pound sign (#). If the passcode is correct, the user is then given\n"
|
||||
"DISA (Direct Inward System Access) -- Allows someone from outside\n"
|
||||
"the telephone switch (PBX) to obtain an \"internal\" system dialtone\n"
|
||||
"and to place calls from it as if they were placing a call from within\n"
|
||||
"the switch. A user calls a number that connects to the DISA application\n"
|
||||
"and is given dialtone. The user enters their passcode, followed by the\n"
|
||||
"pound sign (#). If the passcode is correct, the user is then given\n"
|
||||
"system dialtone on which a call may be placed. Obviously, this type\n"
|
||||
"of access has SERIOUS security implications, and GREAT care must be\n"
|
||||
"taken NOT to compromise your security.\n\n"
|
||||
"There is a possibility of accessing DISA without password. Simply\n"
|
||||
"exchange your password with \"no-password\".\n\n"
|
||||
" Example: exten => s,1,DISA(no-password|local)\n\n"
|
||||
"Be aware that using this compromises the security of your PBX.\n\n"
|
||||
"exchange your password with no-password.\n\n"
|
||||
" Example: exten => s,1,DISA,no-password|local\n\n"
|
||||
"but be aware of using this for your security compromising.\n\n"
|
||||
"The arguments to this application (in extensions.conf) allow either\n"
|
||||
"specification of a single global passcode (that everyone uses), or\n"
|
||||
"individual passcodes contained in a file. It also allows specification\n"
|
||||
"specification of a single global password (that everyone uses), or\n"
|
||||
"individual passwords contained in a file. It also allow specification\n"
|
||||
"of the context on which the user will be dialing. If no context is\n"
|
||||
"specified, the DISA application defaults the context to \"disa\".\n"
|
||||
"Presumably a normal system will have a special context set up\n"
|
||||
"for DISA use with some or a lot of restrictions. \n\n"
|
||||
"specified, the DISA application defaults the context to \"disa\"\n"
|
||||
"presumably that a normal system will have a special context set up\n"
|
||||
"for DISA use with some or a lot of restrictions. The arguments are\n"
|
||||
"one of the following:\n\n"
|
||||
" numeric-passcode\n"
|
||||
" numeric-passcode|context\n"
|
||||
" full-pathname-of-file-that-contains-passcodes\n\n"
|
||||
"The file that contains the passcodes (if used) allows specification\n"
|
||||
"of either just a passcode (defaulting to the \"disa\" context, or\n"
|
||||
"passcode|context on each line of the file. The file may contain blank\n"
|
||||
@@ -82,27 +68,35 @@ static char *descrip =
|
||||
"above arguments may have |new-callerid-string appended to them, to\n"
|
||||
"specify a new (different) callerid to be used for this call, for\n"
|
||||
"example: numeric-passcode|context|\"My Phone\" <(234) 123-4567> or \n"
|
||||
"full-pathname-of-passcode-file|\"My Phone\" <(234) 123-4567>. Last\n"
|
||||
"but not least, |mailbox[@context] may be appended, which will cause\n"
|
||||
"a stutter-dialtone (indication \"dialrecall\") to be used, if the\n"
|
||||
"specified mailbox contains any new messages, for example:\n"
|
||||
"numeric-passcode|context||1234 (w/a changing callerid). Note that\n"
|
||||
"full-pathname-of-passcode-file|\"My Phone\" <(234) 123-4567>. Note that\n"
|
||||
"in the case of specifying the numeric-passcode, the context must be\n"
|
||||
"specified if the callerid is specified also.\n\n"
|
||||
"If login is successful, the application looks up the dialed number in\n"
|
||||
"the specified (or default) context, and executes it if found.\n"
|
||||
"If the user enters an invalid extension and extension \"i\" (invalid) \n"
|
||||
"exists in the context, it will be used. Also, if you set the 5th argument\n"
|
||||
"to 'NOANSWER', the DISA application will not answer initially.\n";
|
||||
"If login is successful, the application parses the dialed number in\n"
|
||||
"the specified (or default) context, and returns 0 with the new extension\n"
|
||||
"context filled-in and the priority set to 1, so that the PBX may\n"
|
||||
"re-apply the routing tables to it and complete the call normally.";
|
||||
|
||||
|
||||
static void play_dialtone(struct ast_channel *chan, char *mailbox)
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int firstdigittimeout = 20000; /* 20 seconds first digit timeout */
|
||||
static int digittimeout = 10000; /* 10 seconds subsequent digit timeout */
|
||||
|
||||
static int ms_diff(struct timeval *tv1, struct timeval *tv2)
|
||||
{
|
||||
int ms;
|
||||
|
||||
ms = (tv1->tv_sec - tv2->tv_sec) * 1000;
|
||||
ms += (tv1->tv_usec - tv2->tv_usec) / 1000;
|
||||
return(ms);
|
||||
}
|
||||
|
||||
static void play_dialtone(struct ast_channel *chan)
|
||||
{
|
||||
const struct tone_zone_sound *ts = NULL;
|
||||
if(ast_app_has_voicemail(mailbox, NULL))
|
||||
ts = ast_get_indication_tone(chan->zone, "dialrecall");
|
||||
else
|
||||
ts = ast_get_indication_tone(chan->zone, "dial");
|
||||
ts = ast_get_indication_tone(chan->zone, "dial");
|
||||
if (ts)
|
||||
ast_playtones_start(chan, 0, ts->data, 0);
|
||||
else
|
||||
@@ -111,92 +105,77 @@ static void play_dialtone(struct ast_channel *chan, char *mailbox)
|
||||
|
||||
static int disa_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int i,j,k,x,did_ignore,special_noanswer;
|
||||
int firstdigittimeout = 20000;
|
||||
int digittimeout = 10000;
|
||||
struct ast_module_user *u;
|
||||
char *tmp, exten[AST_MAX_EXTENSION],acctcode[20]="";
|
||||
char pwline[256];
|
||||
char ourcidname[256],ourcidnum[256];
|
||||
int i,j,k,x,did_ignore;
|
||||
struct localuser *u;
|
||||
char tmp[256],arg2[256]="",exten[AST_MAX_EXTENSION],acctcode[20]="";
|
||||
char *ourcontext,*ourcallerid;
|
||||
struct ast_frame *f;
|
||||
struct timeval lastdigittime;
|
||||
struct timeval lastout, now, lastdigittime;
|
||||
int res;
|
||||
time_t rstart;
|
||||
FILE *fp;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(passcode);
|
||||
AST_APP_ARG(context);
|
||||
AST_APP_ARG(cid);
|
||||
AST_APP_ARG(mailbox);
|
||||
AST_APP_ARG(noanswer);
|
||||
);
|
||||
char *stringp=NULL;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (chan->pbx) {
|
||||
firstdigittimeout = chan->pbx->rtimeout*1000;
|
||||
digittimeout = chan->pbx->dtimeout*1000;
|
||||
}
|
||||
|
||||
if (ast_set_write_format(chan,AST_FORMAT_ULAW)) {
|
||||
ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n", chan->name);
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
if (ast_set_read_format(chan,AST_FORMAT_ULAW)) {
|
||||
ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n", chan->name);
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_log(LOG_DEBUG, "Digittimeout: %d\n", digittimeout);
|
||||
ast_log(LOG_DEBUG, "Responsetimeout: %d\n", firstdigittimeout);
|
||||
|
||||
tmp = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, tmp);
|
||||
|
||||
if (ast_strlen_zero(args.context))
|
||||
args.context = "disa";
|
||||
if (ast_strlen_zero(args.mailbox))
|
||||
args.mailbox = "";
|
||||
|
||||
ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
|
||||
|
||||
|
||||
special_noanswer = 0;
|
||||
if ((!args.noanswer) || strcmp(args.noanswer,"NOANSWER"))
|
||||
if (ast_set_write_format(chan,AST_FORMAT_ULAW))
|
||||
{
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
/* answer */
|
||||
ast_answer(chan);
|
||||
}
|
||||
} else special_noanswer = 1;
|
||||
ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n",chan->name);
|
||||
return -1;
|
||||
}
|
||||
if (ast_set_read_format(chan,AST_FORMAT_ULAW))
|
||||
{
|
||||
ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n",chan->name);
|
||||
return -1;
|
||||
}
|
||||
lastout.tv_sec = lastout.tv_usec = 0;
|
||||
if (!data || !strlen((char *)data)) {
|
||||
ast_log(LOG_WARNING, "disa requires an argument (passcode/passcode file)\n");
|
||||
return -1;
|
||||
}
|
||||
strncpy(tmp, (char *)data, sizeof(tmp)-1);
|
||||
stringp=tmp;
|
||||
strsep(&stringp, "|");
|
||||
ourcontext = strsep(&stringp, "|");
|
||||
/* if context specified, save 2nd arg and parse third */
|
||||
if (ourcontext) {
|
||||
strncpy(arg2,ourcontext, sizeof(arg2) - 1);
|
||||
ourcallerid = strsep(&stringp,"|");
|
||||
}
|
||||
/* if context not specified, use "disa" */
|
||||
else {
|
||||
arg2[0] = 0;
|
||||
ourcallerid = NULL;
|
||||
ourcontext = "disa";
|
||||
}
|
||||
LOCAL_USER_ADD(u);
|
||||
if (chan->_state != AST_STATE_UP)
|
||||
{
|
||||
/* answer */
|
||||
ast_answer(chan);
|
||||
}
|
||||
i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */
|
||||
did_ignore = 0;
|
||||
exten[0] = 0;
|
||||
acctcode[0] = 0;
|
||||
/* can we access DISA without password? */
|
||||
|
||||
ast_log(LOG_DEBUG, "Context: %s\n",args.context);
|
||||
ast_log(LOG_DEBUG, "Context: %s\n",ourcontext);
|
||||
|
||||
if (!strcasecmp(args.passcode, "no-password")) {
|
||||
if (!strcasecmp(tmp, "no-password"))
|
||||
{;
|
||||
k |= 1; /* We have the password */
|
||||
ast_log(LOG_DEBUG, "DISA no-password login success\n");
|
||||
}
|
||||
lastdigittime = ast_tvnow();
|
||||
gettimeofday(&lastdigittime,NULL);
|
||||
|
||||
play_dialtone(chan, args.mailbox);
|
||||
play_dialtone(chan);
|
||||
|
||||
for (;;) {
|
||||
for(;;)
|
||||
{
|
||||
gettimeofday(&now,NULL);
|
||||
/* if outa time, give em reorder */
|
||||
if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) >
|
||||
((k&2) ? digittimeout : firstdigittimeout)) {
|
||||
if (ms_diff(&now,&lastdigittime) >
|
||||
((k&2) ? digittimeout : firstdigittimeout))
|
||||
{
|
||||
ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n",
|
||||
((k&1) ? "extension" : "password"),chan->name);
|
||||
break;
|
||||
@@ -207,116 +186,108 @@ static int disa_exec(struct ast_channel *chan, void *data)
|
||||
}
|
||||
|
||||
f = ast_read(chan);
|
||||
if (f == NULL) {
|
||||
ast_module_user_remove(u);
|
||||
if (f == NULL)
|
||||
{
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
}
|
||||
if ((f->frametype == AST_FRAME_CONTROL) &&
|
||||
(f->subclass == AST_CONTROL_HANGUP)) {
|
||||
(f->subclass == AST_CONTROL_HANGUP))
|
||||
{
|
||||
ast_frfree(f);
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
}
|
||||
if (f->frametype == AST_FRAME_VOICE) {
|
||||
ast_frfree(f);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if not DTMF, just do it again */
|
||||
if (f->frametype != AST_FRAME_DTMF) {
|
||||
/* if not DTMF, just do it again */
|
||||
if (f->frametype != AST_FRAME_DTMF)
|
||||
{
|
||||
ast_frfree(f);
|
||||
continue;
|
||||
}
|
||||
|
||||
j = f->subclass; /* save digit */
|
||||
ast_frfree(f);
|
||||
if (i == 0) {
|
||||
if (i == 0)
|
||||
{
|
||||
k|=2; /* We have the first digit */
|
||||
ast_playtones_stop(chan);
|
||||
}
|
||||
lastdigittime = ast_tvnow();
|
||||
gettimeofday(&lastdigittime,NULL);
|
||||
/* got a DTMF tone */
|
||||
if (i < AST_MAX_EXTENSION) { /* if still valid number of digits */
|
||||
if (!(k&1)) { /* if in password state */
|
||||
if (j == '#') { /* end of password */
|
||||
if (i < AST_MAX_EXTENSION) /* if still valid number of digits */
|
||||
{
|
||||
if (!(k&1)) /* if in password state */
|
||||
{
|
||||
if (j == '#') /* end of password */
|
||||
{
|
||||
/* see if this is an integer */
|
||||
if (sscanf(args.passcode,"%30d",&j) < 1) { /* nope, it must be a filename */
|
||||
fp = fopen(args.passcode,"r");
|
||||
if (!fp) {
|
||||
ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",args.passcode,chan->name);
|
||||
ast_module_user_remove(u);
|
||||
if (sscanf(tmp,"%d",&j) < 1)
|
||||
{ /* nope, it must be a filename */
|
||||
fp = fopen(tmp,"r");
|
||||
if (!fp)
|
||||
{
|
||||
ast_log(LOG_WARNING,"DISA password file %s not found on chan %s\n",tmp,chan->name);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
}
|
||||
pwline[0] = 0;
|
||||
while(fgets(pwline,sizeof(pwline) - 1,fp)) {
|
||||
if (!pwline[0])
|
||||
continue;
|
||||
if (pwline[strlen(pwline) - 1] == '\n')
|
||||
pwline[strlen(pwline) - 1] = 0;
|
||||
if (!pwline[0])
|
||||
continue;
|
||||
/* skip comments */
|
||||
if (pwline[0] == '#')
|
||||
continue;
|
||||
if (pwline[0] == ';')
|
||||
continue;
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, pwline);
|
||||
|
||||
ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox);
|
||||
|
||||
/* password must be in valid format (numeric) */
|
||||
if (sscanf(args.passcode,"%30d", &j) < 1)
|
||||
continue;
|
||||
/* if we got it */
|
||||
if (!strcmp(exten,args.passcode)) {
|
||||
if (ast_strlen_zero(args.context))
|
||||
args.context = "disa";
|
||||
if (ast_strlen_zero(args.mailbox))
|
||||
args.mailbox = "";
|
||||
break;
|
||||
}
|
||||
tmp[0] = 0;
|
||||
while(fgets(tmp,sizeof(tmp) - 1,fp))
|
||||
{
|
||||
char *stringp=NULL,*stringp2;
|
||||
if (!tmp[0]) continue;
|
||||
if (tmp[strlen(tmp) - 1] == '\n')
|
||||
tmp[strlen(tmp) - 1] = 0;
|
||||
if (!tmp[0]) continue;
|
||||
/* skip comments */
|
||||
if (tmp[0] == '#') continue;
|
||||
if (tmp[0] == ';') continue;
|
||||
stringp=tmp;
|
||||
strsep(&stringp, "|");
|
||||
stringp2=strsep(&stringp, "|");
|
||||
if (stringp2) {
|
||||
ourcontext=stringp2;
|
||||
stringp2=strsep(&stringp, "|");
|
||||
if (stringp2) ourcallerid=stringp2;
|
||||
}
|
||||
}
|
||||
/* password must be in valid format (numeric) */
|
||||
if (sscanf(tmp,"%d",&j) < 1) continue;
|
||||
/* if we got it */
|
||||
if (!strcmp(exten,tmp)) break;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
/* compare the two */
|
||||
if (strcmp(exten,args.passcode)) {
|
||||
}
|
||||
/* compare the two */
|
||||
if (strcmp(exten,tmp))
|
||||
{
|
||||
ast_log(LOG_WARNING,"DISA on chan %s got bad password %s\n",chan->name,exten);
|
||||
goto reorder;
|
||||
|
||||
}
|
||||
/* password good, set to dial state */
|
||||
ast_log(LOG_DEBUG,"DISA on chan %s password is good\n",chan->name);
|
||||
play_dialtone(chan, args.mailbox);
|
||||
play_dialtone(chan);
|
||||
|
||||
k|=1; /* In number mode */
|
||||
i = 0; /* re-set buffer pointer */
|
||||
exten[sizeof(acctcode)] = 0;
|
||||
ast_copy_string(acctcode, exten, sizeof(acctcode));
|
||||
strncpy(acctcode,exten, sizeof(acctcode) - 1);
|
||||
exten[0] = 0;
|
||||
ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n", chan->name);
|
||||
ast_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n",chan->name);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (j == '#') { /* end of extension .. maybe */
|
||||
if (i == 0 &&
|
||||
(ast_matchmore_extension(chan, args.context, "#", 1, chan->cid.cid_num) ||
|
||||
ast_exists_extension(chan, args.context, "#", 1, chan->cid.cid_num)) ) {
|
||||
/* Let the # be the part of, or the entire extension */
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exten[i++] = j; /* save digit */
|
||||
exten[i] = 0;
|
||||
if (!(k&1))
|
||||
continue; /* if getting password, continue doing it */
|
||||
/* if this exists */
|
||||
if (!(k&1)) continue; /* if getting password, continue doing it */
|
||||
/* if this exists */
|
||||
|
||||
if (ast_ignore_pattern(args.context, exten)) {
|
||||
play_dialtone(chan, "");
|
||||
if (ast_ignore_pattern(ourcontext, exten)) {
|
||||
play_dialtone(chan);
|
||||
did_ignore = 1;
|
||||
} else
|
||||
if (did_ignore) {
|
||||
@@ -324,50 +295,39 @@ static int disa_exec(struct ast_channel *chan, void *data)
|
||||
did_ignore = 0;
|
||||
}
|
||||
|
||||
/* if can do some more, do it */
|
||||
if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
|
||||
/* if can do some more, do it */
|
||||
if (!ast_matchmore_extension(chan,ourcontext,exten,1, chan->callerid)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (k == 3) {
|
||||
int recheck = 0;
|
||||
struct ast_flags flags = { AST_CDR_FLAG_POSTED };
|
||||
|
||||
if (!ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
|
||||
pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
|
||||
exten[0] = 'i';
|
||||
exten[1] = '\0';
|
||||
recheck = 1;
|
||||
}
|
||||
if (!recheck || ast_exists_extension(chan, args.context, exten, 1, chan->cid.cid_num)) {
|
||||
ast_playtones_stop(chan);
|
||||
/* We're authenticated and have a target extension */
|
||||
if (!ast_strlen_zero(args.cid)) {
|
||||
ast_callerid_split(args.cid, ourcidname, sizeof(ourcidname), ourcidnum, sizeof(ourcidnum));
|
||||
ast_set_callerid(chan, ourcidnum, ourcidname, ourcidnum);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(acctcode))
|
||||
ast_string_field_set(chan, accountcode, acctcode);
|
||||
|
||||
if (special_noanswer) flags.flags = 0;
|
||||
ast_cdr_reset(chan->cdr, &flags);
|
||||
ast_explicit_goto(chan, args.context, exten, 1);
|
||||
ast_module_user_remove(u);
|
||||
return 0;
|
||||
if ((k==3) && (ast_exists_extension(chan,ourcontext,exten,1, chan->callerid)))
|
||||
{
|
||||
ast_playtones_stop(chan);
|
||||
/* We're authenticated and have a valid extension */
|
||||
if (ourcallerid && *ourcallerid)
|
||||
{
|
||||
if (chan->callerid) free(chan->callerid);
|
||||
chan->callerid = strdup(ourcallerid);
|
||||
}
|
||||
strncpy(chan->exten, exten, sizeof(chan->exten) - 1);
|
||||
strncpy(chan->context, ourcontext, sizeof(chan->context) - 1);
|
||||
if (!ast_strlen_zero(acctcode))
|
||||
strncpy(chan->accountcode, acctcode, sizeof(chan->accountcode) - 1);
|
||||
chan->priority = 0;
|
||||
ast_cdr_init(chan->cdr,chan);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Received invalid, but no "i" extension exists in the given context */
|
||||
|
||||
reorder:
|
||||
|
||||
ast_indicate(chan,AST_CONTROL_CONGESTION);
|
||||
/* something is invalid, give em reorder for several seconds */
|
||||
time(&rstart);
|
||||
while(time(NULL) < rstart + 10) {
|
||||
while(time(NULL) < rstart + 10)
|
||||
{
|
||||
if (ast_waitfor(chan, -1) < 0)
|
||||
break;
|
||||
f = ast_read(chan);
|
||||
@@ -376,24 +336,34 @@ reorder:
|
||||
ast_frfree(f);
|
||||
}
|
||||
ast_playtones_stop(chan);
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, disa_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key(void)
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2004 - 2005, Anthony Minessale II.
|
||||
*
|
||||
* Anthony Minessale <anthmct@yahoo.com>
|
||||
*
|
||||
* A license has been granted to Digium (via disclaimer) for the use of
|
||||
* this code.
|
||||
*
|
||||
* 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 Application to dump channel variables
|
||||
*
|
||||
* \author Anthony Minessale <anthmct@yahoo.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/utils.h"
|
||||
|
||||
static char *app = "DumpChan";
|
||||
static char *synopsis = "Dump Info About The Calling Channel";
|
||||
static char *desc =
|
||||
" DumpChan([<min_verbose_level>])\n"
|
||||
"Displays information on channel and listing of all channel\n"
|
||||
"variables. If min_verbose_level is specified, output is only\n"
|
||||
"displayed when the verbose level is currently set to that number\n"
|
||||
"or greater. \n";
|
||||
|
||||
|
||||
static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
|
||||
{
|
||||
struct timeval now;
|
||||
long elapsed_seconds = 0;
|
||||
int hour = 0, min = 0, sec = 0;
|
||||
char cgrp[BUFSIZ/2];
|
||||
char pgrp[BUFSIZ/2];
|
||||
char formatbuf[BUFSIZ/2];
|
||||
|
||||
now = ast_tvnow();
|
||||
memset(buf, 0, size);
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
if (c->cdr) {
|
||||
elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
|
||||
hour = elapsed_seconds / 3600;
|
||||
min = (elapsed_seconds % 3600) / 60;
|
||||
sec = elapsed_seconds % 60;
|
||||
}
|
||||
|
||||
snprintf(buf,size,
|
||||
"Name= %s\n"
|
||||
"Type= %s\n"
|
||||
"UniqueID= %s\n"
|
||||
"CallerID= %s\n"
|
||||
"CallerIDName= %s\n"
|
||||
"DNIDDigits= %s\n"
|
||||
"RDNIS= %s\n"
|
||||
"State= %s (%d)\n"
|
||||
"Rings= %d\n"
|
||||
"NativeFormat= %s\n"
|
||||
"WriteFormat= %s\n"
|
||||
"ReadFormat= %s\n"
|
||||
"1stFileDescriptor= %d\n"
|
||||
"Framesin= %d %s\n"
|
||||
"Framesout= %d %s\n"
|
||||
"TimetoHangup= %ld\n"
|
||||
"ElapsedTime= %dh%dm%ds\n"
|
||||
"Context= %s\n"
|
||||
"Extension= %s\n"
|
||||
"Priority= %d\n"
|
||||
"CallGroup= %s\n"
|
||||
"PickupGroup= %s\n"
|
||||
"Application= %s\n"
|
||||
"Data= %s\n"
|
||||
"Blocking_in= %s\n",
|
||||
c->name,
|
||||
c->tech->type,
|
||||
c->uniqueid,
|
||||
S_OR(c->cid.cid_num, "(N/A)"),
|
||||
S_OR(c->cid.cid_name, "(N/A)"),
|
||||
S_OR(c->cid.cid_dnid, "(N/A)"),
|
||||
S_OR(c->cid.cid_rdnis, "(N/A)"),
|
||||
ast_state2str(c->_state),
|
||||
c->_state,
|
||||
c->rings,
|
||||
ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->nativeformats),
|
||||
ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->writeformat),
|
||||
ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->readformat),
|
||||
c->fds[0], c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
|
||||
c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "", (long)c->whentohangup,
|
||||
hour,
|
||||
min,
|
||||
sec,
|
||||
c->context,
|
||||
c->exten,
|
||||
c->priority,
|
||||
ast_print_group(cgrp, sizeof(cgrp), c->callgroup),
|
||||
ast_print_group(pgrp, sizeof(pgrp), c->pickupgroup),
|
||||
( c->appl ? c->appl : "(N/A)" ),
|
||||
( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
|
||||
(ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dumpchan_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_module_user *u;
|
||||
char vars[BUFSIZ * 4];
|
||||
char info[1024];
|
||||
int level = 0;
|
||||
static char *line = "================================================================================";
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (!ast_strlen_zero(data))
|
||||
level = atoi(data);
|
||||
|
||||
pbx_builtin_serialize_variables(chan, vars, sizeof(vars));
|
||||
serialize_showchan(chan, info, sizeof(info));
|
||||
if (option_verbose >= level)
|
||||
ast_verbose("\nDumping Info For Channel: %s:\n%s\nInfo:\n%s\nVariables:\n%s%s\n", chan->name, line, info, vars, line);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, dumpchan_exec, synopsis, desc);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dump Info About The Calling Channel");
|
||||
133
apps/app_echo.c
133
apps/app_echo.c
@@ -1,105 +1,100 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Echo application -- play back what you hear to evaluate latency
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Echo application -- play back what you hear to evaluate latency
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
static char *tdesc = "Simple Echo Application";
|
||||
|
||||
static char *app = "Echo";
|
||||
|
||||
static char *synopsis = "Echo audio, video, or DTMF back to the calling party";
|
||||
static char *synopsis = "Echo audio read back to the user";
|
||||
|
||||
static char *descrip =
|
||||
" Echo(): This application will echo any audio, video, or DTMF frames read from\n"
|
||||
"the calling channel back to itself. If the DTMF digit '#' is received, the\n"
|
||||
"application will exit.\n";
|
||||
" Echo(): Echo audio read from channel back to the channel. Returns 0\n"
|
||||
"if the user exits with the '#' key, or -1 if the user hangs up.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int echo_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = -1;
|
||||
int format;
|
||||
struct ast_module_user *u;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
format = ast_best_codec(chan->nativeformats);
|
||||
ast_set_write_format(chan, format);
|
||||
ast_set_read_format(chan, format);
|
||||
|
||||
while (ast_waitfor(chan, -1) > -1) {
|
||||
struct ast_frame *f = ast_read(chan);
|
||||
if (!f) {
|
||||
int res=-1;
|
||||
struct localuser *u;
|
||||
struct ast_frame *f;
|
||||
LOCAL_USER_ADD(u);
|
||||
ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
|
||||
ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
|
||||
/* Do our thing here */
|
||||
while(ast_waitfor(chan, -1) > -1) {
|
||||
f = ast_read(chan);
|
||||
if (!f)
|
||||
break;
|
||||
}
|
||||
f->delivery.tv_sec = 0;
|
||||
f->delivery.tv_usec = 0;
|
||||
if (ast_write(chan, f)) {
|
||||
ast_frfree(f);
|
||||
goto end;
|
||||
}
|
||||
if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
|
||||
res = 0;
|
||||
ast_frfree(f);
|
||||
goto end;
|
||||
if (f->frametype == AST_FRAME_VOICE) {
|
||||
if (ast_write(chan, f))
|
||||
break;
|
||||
} else if (f->frametype == AST_FRAME_VIDEO) {
|
||||
if (ast_write(chan, f))
|
||||
break;
|
||||
} else if (f->frametype == AST_FRAME_DTMF) {
|
||||
if (f->subclass == '#') {
|
||||
res = 0;
|
||||
break;
|
||||
} else
|
||||
if (ast_write(chan, f))
|
||||
break;
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
end:
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, echo_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Echo Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
203
apps/app_enumlookup.c
Normal file
203
apps/app_enumlookup.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Time of day - Report the time of day
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/config.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/enum.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static char *tdesc = "ENUM Lookup";
|
||||
|
||||
static char *app = "EnumLookup";
|
||||
|
||||
static char *synopsis = "Lookup number in ENUM";
|
||||
|
||||
static char *descrip =
|
||||
" EnumLookup(exten): Looks up an extension via ENUM and sets\n"
|
||||
"the variable 'ENUM'. For VoIP URIs this variable will \n"
|
||||
"look like 'TECHNOLOGY/URI' with the appropriate technology.\n"
|
||||
"Returns -1 on hangup, or 0 on completion regardless of whether the \n"
|
||||
"lookup was successful. \n"
|
||||
"Currently, the enumservices SIP, H323, IAX, IAX2 and TEL are recognized. \n"
|
||||
"A good SIP, H323, IAX or IAX2 entry will result in normal priority handling, \n"
|
||||
"whereas a good TEL entry will increase the priority by 51 (if existing).\n"
|
||||
"If the lookup was *not* successful and there exists a priority n + 101,\n"
|
||||
"then that priority will be taken next.\n" ;
|
||||
|
||||
#define ENUM_CONFIG "enum.conf"
|
||||
|
||||
static char h323driver[80] = "";
|
||||
#define H323DRIVERDEFAULT "H323"
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int enumlookup_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
char tech[80];
|
||||
char dest[80];
|
||||
char tmp[256];
|
||||
char *c,*t;
|
||||
struct localuser *u;
|
||||
if (!data || ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "EnumLookup requires an argument (extension)\n");
|
||||
res = 1;
|
||||
}
|
||||
LOCAL_USER_ADD(u);
|
||||
if (!res) {
|
||||
res = ast_get_enum(chan, data, dest, sizeof(dest), tech, sizeof(tech));
|
||||
printf("ENUM got '%d'\n", res);
|
||||
}
|
||||
LOCAL_USER_REMOVE(u);
|
||||
/* Parse it out */
|
||||
if (res > 0) {
|
||||
if (!strcasecmp(tech, "SIP")) {
|
||||
c = dest;
|
||||
if (!strncmp(c, "sip:", 4))
|
||||
c += 4;
|
||||
snprintf(tmp, sizeof(tmp), "SIP/%s", c);
|
||||
pbx_builtin_setvar_helper(chan, "ENUM", tmp);
|
||||
} else if (!strcasecmp(tech, "h323")) {
|
||||
c = dest;
|
||||
if (!strncmp(c, "h323:", 5))
|
||||
c += 5;
|
||||
snprintf(tmp, sizeof(tmp), "%s/%s", h323driver, c);
|
||||
/* do a s!;.*!! on the H323 URI */
|
||||
t = strchr(c,';');
|
||||
if (t)
|
||||
*t = 0;
|
||||
pbx_builtin_setvar_helper(chan, "ENUM", tmp);
|
||||
} else if (!strcasecmp(tech, "iax")) {
|
||||
c = dest;
|
||||
if (!strncmp(c, "iax:", 4))
|
||||
c += 4;
|
||||
snprintf(tmp, sizeof(tmp), "IAX/%s", c);
|
||||
pbx_builtin_setvar_helper(chan, "ENUM", tmp);
|
||||
} else if (!strcasecmp(tech, "iax2")) {
|
||||
c = dest;
|
||||
if (!strncmp(c, "iax2:", 5))
|
||||
c += 5;
|
||||
snprintf(tmp, sizeof(tmp), "IAX2/%s", c);
|
||||
pbx_builtin_setvar_helper(chan, "ENUM", tmp);
|
||||
} else if (!strcasecmp(tech, "tel")) {
|
||||
c = dest;
|
||||
if (!strncmp(c, "tel:", 4))
|
||||
c += 4;
|
||||
|
||||
if (c[0] != '+') {
|
||||
ast_log(LOG_NOTICE, "tel: uri must start with a \"+\" (got '%s')\n", c);
|
||||
res = 0;
|
||||
} else {
|
||||
/* now copy over the number, skipping all non-digits and stop at ; or NULL */
|
||||
t = tmp;
|
||||
while( *c && (*c != ';') && (t - tmp < (sizeof(tmp) - 1))) {
|
||||
if (isdigit(*c))
|
||||
*t++ = *c;
|
||||
c++;
|
||||
}
|
||||
*t = 0;
|
||||
pbx_builtin_setvar_helper(chan, "ENUM", tmp);
|
||||
ast_log(LOG_NOTICE, "tel: ENUM set to \"%s\"\n", tmp);
|
||||
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 51, chan->callerid))
|
||||
chan->priority += 50;
|
||||
else
|
||||
res = 0;
|
||||
}
|
||||
} else if (!ast_strlen_zero(tech)) {
|
||||
ast_log(LOG_NOTICE, "Don't know how to handle technology '%s'\n", tech);
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
if (!res) {
|
||||
/* Look for a "busy" place */
|
||||
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
|
||||
chan->priority += 100;
|
||||
} else if (res > 0)
|
||||
res = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_config(void)
|
||||
{
|
||||
struct ast_config *cfg;
|
||||
char *s;
|
||||
|
||||
cfg = ast_load(ENUM_CONFIG);
|
||||
if (cfg) {
|
||||
if (!(s=ast_variable_retrieve(cfg, "general", "h323driver"))) {
|
||||
strncpy(h323driver, H323DRIVERDEFAULT, sizeof(h323driver) - 1);
|
||||
} else {
|
||||
strncpy(h323driver, s, sizeof(h323driver) - 1);
|
||||
}
|
||||
ast_destroy(cfg);
|
||||
return 0;
|
||||
}
|
||||
ast_log(LOG_NOTICE, "No ENUM Config file, using defaults\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
int res;
|
||||
res = ast_register_application(app, enumlookup_exec, synopsis, descrip);
|
||||
if (res)
|
||||
return(res);
|
||||
if ((res=load_config())) {
|
||||
return(res);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int reload(void)
|
||||
{
|
||||
return(load_config());
|
||||
}
|
||||
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
101
apps/app_eval.c
Normal file
101
apps/app_eval.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Eval application
|
||||
*
|
||||
* Copyright (c) 2004 Tilghman Lesher. All rights reserved.
|
||||
*
|
||||
* Tilghman Lesher <app_eval__v001@the-tilghman.com>
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* This code is released by the author with no restrictions on usage.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
|
||||
/* Maximum length of any variable */
|
||||
#define MAXRESULT 1024
|
||||
|
||||
static char *tdesc = "Reevaluates strings";
|
||||
|
||||
static char *app_eval = "Eval";
|
||||
|
||||
static char *eval_synopsis = "Evaluates a string";
|
||||
|
||||
static char *eval_descrip =
|
||||
"Usage: Eval(newvar=somestring)\n"
|
||||
" Normally Asterisk evaluates variables inline. But what if you want to\n"
|
||||
"store variable offsets in a database, to be evaluated later? Eval is\n"
|
||||
"the answer, by allowing a string to be evaluated twice in the dialplan,\n"
|
||||
"the first time as part of the normal dialplan, and the second using Eval.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int eval_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
struct localuser *u;
|
||||
char *s, *newvar=NULL, tmp[MAXRESULT];
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
/* Check and parse arguments */
|
||||
if (data) {
|
||||
s = ast_strdupa((char *)data);
|
||||
if (s) {
|
||||
newvar = strsep(&s, "=");
|
||||
if (newvar && (newvar[0] != '\0')) {
|
||||
memset(tmp, 0, MAXRESULT);
|
||||
pbx_substitute_variables_helper(chan, s, tmp, MAXRESULT - 1);
|
||||
pbx_builtin_setvar_helper(chan, newvar, tmp);
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Out of memory\n");
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app_eval);
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app_eval, eval_exec, eval_synopsis, eval_descrip);
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
230
apps/app_exec.c
230
apps/app_exec.c
@@ -1,221 +1,113 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (c) 2004 - 2005, Tilghman Lesher. All rights reserved.
|
||||
* Portions copyright (c) 2006, Philipp Dunkel.
|
||||
* Exec application
|
||||
*
|
||||
* Tilghman Lesher <app_exec__v002@the-tilghman.com>
|
||||
* Copyright (c) 2004 Tilghman Lesher. All rights reserved.
|
||||
*
|
||||
* Tilghman Lesher <app_exec__v001@the-tilghman.com>
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* This code is released by the author with no restrictions on usage.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Exec application
|
||||
*
|
||||
* \author Tilghman Lesher <app_exec__v002@the-tilghman.com>
|
||||
* \author Philipp Dunkel <philipp.dunkel@ebox.at>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
|
||||
/* Maximum length of any variable */
|
||||
#define MAXRESULT 1024
|
||||
|
||||
/*! Note
|
||||
*
|
||||
* The key difference between these two apps is exit status. In a
|
||||
* nutshell, Exec tries to be transparent as possible, behaving
|
||||
* in exactly the same way as if the application it calls was
|
||||
* directly invoked from the dialplan.
|
||||
*
|
||||
* TryExec, on the other hand, provides a way to execute applications
|
||||
* and catch any possible fatal error without actually fatally
|
||||
* affecting the dialplan.
|
||||
*/
|
||||
static char *tdesc = "Executes applications";
|
||||
|
||||
static char *app_exec = "Exec";
|
||||
static char *exec_synopsis = "Executes dialplan application";
|
||||
|
||||
static char *exec_synopsis = "Exec(Appname(arguments))";
|
||||
|
||||
static char *exec_descrip =
|
||||
"Usage: Exec(appname(arguments))\n"
|
||||
"Exec(appname(arguments))\n"
|
||||
" Allows an arbitrary application to be invoked even when not\n"
|
||||
"hardcoded into the dialplan. If the underlying application\n"
|
||||
"terminates the dialplan, or if the application cannot be found,\n"
|
||||
"Exec will terminate the dialplan.\n"
|
||||
" To invoke external applications, see the application System.\n"
|
||||
" If you would like to catch any error instead, see TryExec.\n";
|
||||
"hardcoded into the dialplan. Returns whatever value the\n"
|
||||
"app returns or -2 when the app cannot be found.\n";
|
||||
|
||||
static char *app_tryexec = "TryExec";
|
||||
static char *tryexec_synopsis = "Executes dialplan application, always returning";
|
||||
static char *tryexec_descrip =
|
||||
"Usage: TryExec(appname(arguments))\n"
|
||||
" Allows an arbitrary application to be invoked even when not\n"
|
||||
"hardcoded into the dialplan. To invoke external applications\n"
|
||||
"see the application System. Always returns to the dialplan.\n"
|
||||
"The channel variable TRYSTATUS will be set to:\n"
|
||||
" SUCCESS if the application returned zero\n"
|
||||
" FAILED if the application returned non-zero\n"
|
||||
" NOAPP if the application was not found or was not specified\n";
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
static char *app_execif = "ExecIf";
|
||||
static char *execif_synopsis = "Executes dialplan application, conditionally";
|
||||
static char *execif_descrip =
|
||||
"Usage: ExecIF (<expr>|<app>|<data>)\n"
|
||||
"If <expr> is true, execute and return the result of <app>(<data>).\n"
|
||||
"If <expr> is true, but <app> is not found, then the application\n"
|
||||
"will return a non-zero value.\n";
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int exec_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
struct ast_module_user *u;
|
||||
char *s, *appname, *endargs, args[MAXRESULT] = "";
|
||||
struct localuser *u;
|
||||
char *s, *appname, *endargs, args[MAXRESULT];
|
||||
struct ast_app *app;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
memset(args, 0, MAXRESULT);
|
||||
|
||||
/* Check and parse arguments */
|
||||
if (data) {
|
||||
s = ast_strdupa(data);
|
||||
appname = strsep(&s, "(");
|
||||
s = ast_strdupa((char *)data);
|
||||
if (s) {
|
||||
endargs = strrchr(s, ')');
|
||||
if (endargs)
|
||||
*endargs = '\0';
|
||||
pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1);
|
||||
}
|
||||
if (appname) {
|
||||
app = pbx_findapp(appname);
|
||||
if (app) {
|
||||
res = pbx_exec(chan, app, args);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
|
||||
res = -1;
|
||||
appname = strsep(&s, "(");
|
||||
if (s) {
|
||||
endargs = strrchr(s, ')');
|
||||
if (endargs)
|
||||
*endargs = '\0';
|
||||
pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1);
|
||||
}
|
||||
if (appname) {
|
||||
app = pbx_findapp(appname);
|
||||
if (app) {
|
||||
res = pbx_exec(chan, app, args, 1);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
|
||||
res = -2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Out of memory\n");
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int tryexec_exec(struct ast_channel *chan, void *data)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res=0;
|
||||
struct ast_module_user *u;
|
||||
char *s, *appname, *endargs, args[MAXRESULT] = "";
|
||||
struct ast_app *app;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
/* Check and parse arguments */
|
||||
if (data) {
|
||||
s = ast_strdupa(data);
|
||||
appname = strsep(&s, "(");
|
||||
if (s) {
|
||||
endargs = strrchr(s, ')');
|
||||
if (endargs)
|
||||
*endargs = '\0';
|
||||
pbx_substitute_variables_helper(chan, s, args, MAXRESULT - 1);
|
||||
}
|
||||
if (appname) {
|
||||
app = pbx_findapp(appname);
|
||||
if (app) {
|
||||
res = pbx_exec(chan, app, args);
|
||||
pbx_builtin_setvar_helper(chan, "TRYSTATUS", res ? "FAILED" : "SUCCESS");
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
|
||||
pbx_builtin_setvar_helper(chan, "TRYSTATUS", "NOAPP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
return 0;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app_exec);
|
||||
}
|
||||
|
||||
static int execif_exec(struct ast_channel *chan, void *data)
|
||||
int load_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_module_user *u;
|
||||
char *myapp = NULL;
|
||||
char *mydata = NULL;
|
||||
char *expr = NULL;
|
||||
struct ast_app *app = NULL;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
expr = ast_strdupa(data);
|
||||
|
||||
if ((myapp = strchr(expr,'|'))) {
|
||||
*myapp = '\0';
|
||||
myapp++;
|
||||
if ((mydata = strchr(myapp,'|'))) {
|
||||
*mydata = '\0';
|
||||
mydata++;
|
||||
} else
|
||||
mydata = "";
|
||||
|
||||
if (pbx_checkcondition(expr)) {
|
||||
if ((app = pbx_findapp(myapp))) {
|
||||
res = pbx_exec(chan, app, mydata);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Could not find application! (%s)\n", myapp);
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_ERROR,"Invalid Syntax.\n");
|
||||
res = -1;
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
return ast_register_application(app_exec, exec_exec, exec_synopsis, exec_descrip);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app_exec);
|
||||
res |= ast_unregister_application(app_tryexec);
|
||||
res |= ast_unregister_application(app_execif);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
char *key()
|
||||
{
|
||||
int res = ast_register_application(app_exec, exec_exec, exec_synopsis, exec_descrip);
|
||||
res |= ast_register_application(app_tryexec, tryexec_exec, tryexec_synopsis, tryexec_descrip);
|
||||
res |= ast_register_application(app_execif, execif_exec, execif_synopsis, execif_descrip);
|
||||
return res;
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Executes dialplan applications");
|
||||
|
||||
@@ -1,601 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Kevin P. Fleming <kpfleming@digium.com>
|
||||
*
|
||||
* Portions taken from the file-based music-on-hold work
|
||||
* created by Anthony Minessale II in res_musiconhold.c
|
||||
*
|
||||
* 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 External IVR application interface
|
||||
*
|
||||
* \author Kevin P. Fleming <kpfleming@digium.com>
|
||||
*
|
||||
* \note Portions taken from the file-based music-on-hold work
|
||||
* created by Anthony Minessale II in res_musiconhold.c
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>working_fork</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_CAP
|
||||
#include <sys/capability.h>
|
||||
#endif /* HAVE_CAP */
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/options.h"
|
||||
|
||||
static const char *app = "ExternalIVR";
|
||||
|
||||
static const char *synopsis = "Interfaces with an external IVR application";
|
||||
|
||||
static const char *descrip =
|
||||
" ExternalIVR(command[|arg[|arg...]]): Forks a process to run the supplied command,\n"
|
||||
"and starts a generator on the channel. The generator's play list is\n"
|
||||
"controlled by the external application, which can add and clear entries\n"
|
||||
"via simple commands issued over its stdout. The external application\n"
|
||||
"will receive all DTMF events received on the channel, and notification\n"
|
||||
"if the channel is hung up. The application will not be forcibly terminated\n"
|
||||
"when the channel is hung up.\n"
|
||||
"See doc/externalivr.txt for a protocol specification.\n";
|
||||
|
||||
/* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
|
||||
#define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)
|
||||
|
||||
struct playlist_entry {
|
||||
AST_LIST_ENTRY(playlist_entry) list;
|
||||
char filename[1];
|
||||
};
|
||||
|
||||
struct ivr_localuser {
|
||||
struct ast_channel *chan;
|
||||
AST_LIST_HEAD(playlist, playlist_entry) playlist;
|
||||
AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
|
||||
int abort_current_sound;
|
||||
int playing_silence;
|
||||
int option_autoclear;
|
||||
};
|
||||
|
||||
|
||||
struct gen_state {
|
||||
struct ivr_localuser *u;
|
||||
struct ast_filestream *stream;
|
||||
struct playlist_entry *current;
|
||||
int sample_queue;
|
||||
};
|
||||
|
||||
static void send_child_event(FILE *handle, const char event, const char *data,
|
||||
const struct ast_channel *chan)
|
||||
{
|
||||
char tmp[256];
|
||||
|
||||
if (!data) {
|
||||
snprintf(tmp, sizeof(tmp), "%c,%10d", event, (int)time(NULL));
|
||||
} else {
|
||||
snprintf(tmp, sizeof(tmp), "%c,%10d,%s", event, (int)time(NULL), data);
|
||||
}
|
||||
|
||||
fprintf(handle, "%s\n", tmp);
|
||||
ast_chan_log(LOG_DEBUG, chan, "sent '%s'\n", tmp);
|
||||
}
|
||||
|
||||
static void *gen_alloc(struct ast_channel *chan, void *params)
|
||||
{
|
||||
struct ivr_localuser *u = params;
|
||||
struct gen_state *state;
|
||||
|
||||
if (!(state = ast_calloc(1, sizeof(*state))))
|
||||
return NULL;
|
||||
|
||||
state->u = u;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void gen_closestream(struct gen_state *state)
|
||||
{
|
||||
if (!state->stream)
|
||||
return;
|
||||
|
||||
ast_closestream(state->stream);
|
||||
state->u->chan->stream = NULL;
|
||||
state->stream = NULL;
|
||||
}
|
||||
|
||||
static void gen_release(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct gen_state *state = data;
|
||||
|
||||
gen_closestream(state);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* caller has the playlist locked */
|
||||
static int gen_nextfile(struct gen_state *state)
|
||||
{
|
||||
struct ivr_localuser *u = state->u;
|
||||
char *file_to_stream;
|
||||
|
||||
u->abort_current_sound = 0;
|
||||
u->playing_silence = 0;
|
||||
gen_closestream(state);
|
||||
|
||||
while (!state->stream) {
|
||||
state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list);
|
||||
if (state->current) {
|
||||
file_to_stream = state->current->filename;
|
||||
} else {
|
||||
file_to_stream = "silence/10";
|
||||
u->playing_silence = 1;
|
||||
}
|
||||
|
||||
if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
|
||||
ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
|
||||
if (!u->playing_silence) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (!state->stream);
|
||||
}
|
||||
|
||||
static struct ast_frame *gen_readframe(struct gen_state *state)
|
||||
{
|
||||
struct ast_frame *f = NULL;
|
||||
struct ivr_localuser *u = state->u;
|
||||
|
||||
if (u->abort_current_sound ||
|
||||
(u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
|
||||
gen_closestream(state);
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
gen_nextfile(state);
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
}
|
||||
|
||||
if (!(state->stream && (f = ast_readframe(state->stream)))) {
|
||||
if (state->current) {
|
||||
AST_LIST_LOCK(&u->finishlist);
|
||||
AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
|
||||
AST_LIST_UNLOCK(&u->finishlist);
|
||||
state->current = NULL;
|
||||
}
|
||||
if (!gen_nextfile(state))
|
||||
f = ast_readframe(state->stream);
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
|
||||
{
|
||||
struct gen_state *state = data;
|
||||
struct ast_frame *f = NULL;
|
||||
int res = 0;
|
||||
|
||||
state->sample_queue += samples;
|
||||
|
||||
while (state->sample_queue > 0) {
|
||||
if (!(f = gen_readframe(state)))
|
||||
return -1;
|
||||
|
||||
res = ast_write(chan, f);
|
||||
ast_frfree(f);
|
||||
if (res < 0) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
state->sample_queue -= f->samples;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct ast_generator gen =
|
||||
{
|
||||
alloc: gen_alloc,
|
||||
release: gen_release,
|
||||
generate: gen_generate,
|
||||
};
|
||||
|
||||
static struct playlist_entry *make_entry(const char *filename)
|
||||
{
|
||||
struct playlist_entry *entry;
|
||||
|
||||
if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
|
||||
return NULL;
|
||||
|
||||
strcpy(entry->filename, filename);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static int app_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_module_user *lu;
|
||||
struct playlist_entry *entry;
|
||||
const char *args = data;
|
||||
int child_stdin[2] = { 0,0 };
|
||||
int child_stdout[2] = { 0,0 };
|
||||
int child_stderr[2] = { 0,0 };
|
||||
int res = -1;
|
||||
int test_available_fd = -1;
|
||||
int gen_active = 0;
|
||||
int pid;
|
||||
char *argv[32];
|
||||
int argc = 1;
|
||||
char *buf, *command;
|
||||
FILE *child_commands = NULL;
|
||||
FILE *child_errors = NULL;
|
||||
FILE *child_events = NULL;
|
||||
struct ivr_localuser foo = {
|
||||
.playlist = AST_LIST_HEAD_INIT_VALUE,
|
||||
.finishlist = AST_LIST_HEAD_INIT_VALUE,
|
||||
};
|
||||
struct ivr_localuser *u = &foo;
|
||||
sigset_t fullset, oldset;
|
||||
|
||||
lu = ast_module_user_add(chan);
|
||||
|
||||
sigfillset(&fullset);
|
||||
pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
|
||||
|
||||
u->abort_current_sound = 0;
|
||||
u->chan = chan;
|
||||
|
||||
if (ast_strlen_zero(args)) {
|
||||
ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
|
||||
ast_module_user_remove(lu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = ast_strdupa(data);
|
||||
|
||||
argc = ast_app_separate_args(buf, '|', argv, sizeof(argv) / sizeof(argv[0]));
|
||||
|
||||
if (pipe(child_stdin)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (pipe(child_stdout)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (pipe(child_stderr)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
ast_answer(chan);
|
||||
}
|
||||
|
||||
if (ast_activate_generator(chan, &gen, u) < 0) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
|
||||
goto exit;
|
||||
} else
|
||||
gen_active = 1;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!pid) {
|
||||
/* child process */
|
||||
int i;
|
||||
#ifdef HAVE_CAP
|
||||
cap_t cap = cap_from_text("cap_net_admin-eip");
|
||||
|
||||
if (cap_set_proc(cap)) {
|
||||
/* Careful with order! Logging cannot happen after we close FDs */
|
||||
ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
|
||||
}
|
||||
cap_free(cap);
|
||||
#endif
|
||||
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
|
||||
|
||||
if (ast_opt_high_priority)
|
||||
ast_set_priority(0);
|
||||
|
||||
dup2(child_stdin[0], STDIN_FILENO);
|
||||
dup2(child_stdout[1], STDOUT_FILENO);
|
||||
dup2(child_stderr[1], STDERR_FILENO);
|
||||
for (i = STDERR_FILENO + 1; i < 1024; i++)
|
||||
close(i);
|
||||
execv(argv[0], argv);
|
||||
fprintf(stderr, "Failed to execute '%s': %s\n", argv[0], strerror(errno));
|
||||
_exit(1);
|
||||
} else {
|
||||
/* parent process */
|
||||
int child_events_fd = child_stdin[1];
|
||||
int child_commands_fd = child_stdout[0];
|
||||
int child_errors_fd = child_stderr[0];
|
||||
struct ast_frame *f;
|
||||
int ms;
|
||||
int exception;
|
||||
int ready_fd;
|
||||
int waitfds[2] = { child_errors_fd, child_commands_fd };
|
||||
struct ast_channel *rchan;
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||
|
||||
close(child_stdin[0]);
|
||||
child_stdin[0] = 0;
|
||||
close(child_stdout[1]);
|
||||
child_stdout[1] = 0;
|
||||
close(child_stderr[1]);
|
||||
child_stderr[1] = 0;
|
||||
|
||||
if (!(child_events = fdopen(child_events_fd, "w"))) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Could not open stream for child events\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!(child_commands = fdopen(child_commands_fd, "r"))) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Could not open stream for child commands\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!(child_errors = fdopen(child_errors_fd, "r"))) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Could not open stream for child errors\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
test_available_fd = open("/dev/null", O_RDONLY);
|
||||
|
||||
setvbuf(child_events, NULL, _IONBF, 0);
|
||||
setvbuf(child_commands, NULL, _IONBF, 0);
|
||||
setvbuf(child_errors, NULL, _IONBF, 0);
|
||||
|
||||
res = 0;
|
||||
|
||||
while (1) {
|
||||
if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ast_check_hangup(chan)) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
|
||||
send_child_event(child_events, 'H', NULL, chan);
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
ready_fd = 0;
|
||||
ms = 100;
|
||||
errno = 0;
|
||||
exception = 0;
|
||||
|
||||
rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms);
|
||||
|
||||
if (!AST_LIST_EMPTY(&u->finishlist)) {
|
||||
AST_LIST_LOCK(&u->finishlist);
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
|
||||
send_child_event(child_events, 'F', entry->filename, chan);
|
||||
free(entry);
|
||||
}
|
||||
AST_LIST_UNLOCK(&u->finishlist);
|
||||
}
|
||||
|
||||
if (rchan) {
|
||||
/* the channel has something */
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
|
||||
send_child_event(child_events, 'H', NULL, chan);
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (f->frametype == AST_FRAME_DTMF) {
|
||||
send_child_event(child_events, f->subclass, NULL, chan);
|
||||
if (u->option_autoclear) {
|
||||
if (!u->abort_current_sound && !u->playing_silence)
|
||||
send_child_event(child_events, 'T', NULL, chan);
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
|
||||
send_child_event(child_events, 'D', entry->filename, chan);
|
||||
free(entry);
|
||||
}
|
||||
if (!u->playing_silence)
|
||||
u->abort_current_sound = 1;
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
}
|
||||
} else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
|
||||
send_child_event(child_events, 'H', NULL, chan);
|
||||
ast_frfree(f);
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
ast_frfree(f);
|
||||
} else if (ready_fd == child_commands_fd) {
|
||||
char input[1024];
|
||||
|
||||
if (exception || feof(child_commands)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!fgets(input, sizeof(input), child_commands))
|
||||
continue;
|
||||
|
||||
command = ast_strip(input);
|
||||
|
||||
ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input);
|
||||
|
||||
if (strlen(input) < 4)
|
||||
continue;
|
||||
|
||||
if (input[0] == 'S') {
|
||||
if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
|
||||
send_child_event(child_events, 'Z', NULL, chan);
|
||||
strcpy(&input[2], "exception");
|
||||
}
|
||||
if (!u->abort_current_sound && !u->playing_silence)
|
||||
send_child_event(child_events, 'T', NULL, chan);
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
|
||||
send_child_event(child_events, 'D', entry->filename, chan);
|
||||
free(entry);
|
||||
}
|
||||
if (!u->playing_silence)
|
||||
u->abort_current_sound = 1;
|
||||
entry = make_entry(&input[2]);
|
||||
if (entry)
|
||||
AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
} else if (input[0] == 'A') {
|
||||
if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
|
||||
send_child_event(child_events, 'Z', NULL, chan);
|
||||
strcpy(&input[2], "exception");
|
||||
}
|
||||
entry = make_entry(&input[2]);
|
||||
if (entry) {
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
}
|
||||
} else if (input[0] == 'H') {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
|
||||
send_child_event(child_events, 'H', NULL, chan);
|
||||
break;
|
||||
} else if (input[0] == 'O') {
|
||||
if (!strcasecmp(&input[2], "autoclear"))
|
||||
u->option_autoclear = 1;
|
||||
else if (!strcasecmp(&input[2], "noautoclear"))
|
||||
u->option_autoclear = 0;
|
||||
else
|
||||
ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
|
||||
}
|
||||
} else if (ready_fd == child_errors_fd) {
|
||||
char input[1024];
|
||||
|
||||
if (exception || (dup2(child_commands_fd, test_available_fd) == -1) || feof(child_errors)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fgets(input, sizeof(input), child_errors)) {
|
||||
command = ast_strip(input);
|
||||
ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
|
||||
}
|
||||
} else if ((ready_fd < 0) && ms) {
|
||||
if (errno == 0 || errno == EINTR)
|
||||
continue;
|
||||
|
||||
ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (gen_active)
|
||||
ast_deactivate_generator(chan);
|
||||
|
||||
if (child_events)
|
||||
fclose(child_events);
|
||||
|
||||
if (child_commands)
|
||||
fclose(child_commands);
|
||||
|
||||
if (child_errors)
|
||||
fclose(child_errors);
|
||||
|
||||
if (test_available_fd > -1) {
|
||||
close(test_available_fd);
|
||||
}
|
||||
|
||||
if (child_stdin[0])
|
||||
close(child_stdin[0]);
|
||||
|
||||
if (child_stdin[1])
|
||||
close(child_stdin[1]);
|
||||
|
||||
if (child_stdout[0])
|
||||
close(child_stdout[0]);
|
||||
|
||||
if (child_stdout[1])
|
||||
close(child_stdout[1]);
|
||||
|
||||
if (child_stderr[0])
|
||||
close(child_stderr[0]);
|
||||
|
||||
if (child_stderr[1])
|
||||
close(child_stderr[1]);
|
||||
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
|
||||
free(entry);
|
||||
|
||||
ast_module_user_remove(lu);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, app_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");
|
||||
@@ -1,39 +1,26 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Connect to festival
|
||||
*
|
||||
* Copyright (C) 2002, Christos Ricudis
|
||||
*
|
||||
* Christos Ricudis <ricudis@itc.auth.gr>
|
||||
*
|
||||
* 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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Connect to festival
|
||||
*
|
||||
* \author Christos Ricudis <ricudis@itc.auth.gr>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>working_fork</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/md5.h>
|
||||
#include <asterisk/config.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
@@ -49,24 +36,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_CAP
|
||||
#include <sys/capability.h>
|
||||
#endif /* HAVE_CAP */
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/md5.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/options.h"
|
||||
|
||||
#define FESTIVAL_CONFIG "festival.conf"
|
||||
|
||||
static char *tdesc = "Simple Festival Interface";
|
||||
|
||||
static char *app = "Festival";
|
||||
|
||||
static char *synopsis = "Say text to the user";
|
||||
@@ -76,6 +51,9 @@ static char *descrip =
|
||||
"play it to the user, allowing any given interrupt keys to immediately terminate and return\n"
|
||||
"the value, or 'any' to allow any number back (useful in dialplan)\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static char *socket_receive_file_to_buff(int fd,int *size)
|
||||
{
|
||||
@@ -90,10 +68,7 @@ static char *socket_receive_file_to_buff(int fd,int *size)
|
||||
char c;
|
||||
|
||||
bufflen = 1024;
|
||||
if (!(buff = ast_malloc(bufflen)))
|
||||
{
|
||||
/* TODO: Handle memory allocation failure */
|
||||
}
|
||||
buff = (char *)malloc(bufflen);
|
||||
*size=0;
|
||||
|
||||
for (k=0; file_stuff_key[k] != '\0';)
|
||||
@@ -103,10 +78,7 @@ static char *socket_receive_file_to_buff(int fd,int *size)
|
||||
if ((*size)+k+1 >= bufflen)
|
||||
{ /* +1 so you can add a NULL if you want */
|
||||
bufflen += bufflen/4;
|
||||
if (!(buff = ast_realloc(buff, bufflen)))
|
||||
{
|
||||
/* TODO: Handle memory allocation failure */
|
||||
}
|
||||
buff = (char *)realloc(buff,bufflen);
|
||||
}
|
||||
if (file_stuff_key[k] == c)
|
||||
k++;
|
||||
@@ -138,39 +110,17 @@ static int send_waveform_to_fd(char *waveform, int length, int fd) {
|
||||
#ifdef __PPC__
|
||||
char c;
|
||||
#endif
|
||||
sigset_t fullset, oldset;
|
||||
#ifdef HAVE_CAP
|
||||
cap_t cap;
|
||||
#endif
|
||||
|
||||
sigfillset(&fullset);
|
||||
pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
|
||||
|
||||
res = fork();
|
||||
if (res < 0)
|
||||
ast_log(LOG_WARNING, "Fork failed\n");
|
||||
if (res) {
|
||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||
return res;
|
||||
}
|
||||
#ifdef HAVE_CAP
|
||||
cap = cap_from_text("cap_net_admin-eip");
|
||||
|
||||
if (cap_set_proc(cap)) {
|
||||
/* Careful with order! Logging cannot happen after we close FDs */
|
||||
ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
|
||||
}
|
||||
cap_free(cap);
|
||||
#endif
|
||||
for (x=0;x<256;x++) {
|
||||
if (x != fd)
|
||||
close(x);
|
||||
}
|
||||
if (ast_opt_high_priority)
|
||||
ast_set_priority(0);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
|
||||
/*IAS */
|
||||
res = fork();
|
||||
if (res < 0)
|
||||
ast_log(LOG_WARNING, "Fork failed\n");
|
||||
if (res)
|
||||
return res;
|
||||
for (x=0;x<256;x++) {
|
||||
if (x != fd)
|
||||
close(x);
|
||||
}
|
||||
//IAS
|
||||
#ifdef __PPC__
|
||||
for( x=0; x<length; x+=2)
|
||||
{
|
||||
@@ -180,9 +130,7 @@ static int send_waveform_to_fd(char *waveform, int length, int fd) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (write(fd,waveform,length) < 0) {
|
||||
ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
|
||||
}
|
||||
write(fd,waveform,length);
|
||||
close(fd);
|
||||
exit(0);
|
||||
}
|
||||
@@ -200,9 +148,7 @@ static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, in
|
||||
struct ast_frame f;
|
||||
char offset[AST_FRIENDLY_OFFSET];
|
||||
char frdata[2048];
|
||||
} myf = {
|
||||
.f = { 0, },
|
||||
};
|
||||
} myf;
|
||||
|
||||
if (pipe(fds)) {
|
||||
ast_log(LOG_WARNING, "Unable to create pipe\n");
|
||||
@@ -213,8 +159,7 @@ static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, in
|
||||
if (chan->_state != AST_STATE_UP)
|
||||
ast_answer(chan);
|
||||
ast_stopstream(chan);
|
||||
ast_indicate(chan, -1);
|
||||
|
||||
|
||||
owriteformat = chan->writeformat;
|
||||
res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
|
||||
if (res < 0) {
|
||||
@@ -262,18 +207,17 @@ static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, in
|
||||
myf.f.subclass = AST_FORMAT_SLINEAR;
|
||||
myf.f.datalen = res;
|
||||
myf.f.samples = res / 2;
|
||||
myf.f.mallocd = 0;
|
||||
myf.f.offset = AST_FRIENDLY_OFFSET;
|
||||
myf.f.src = __PRETTY_FUNCTION__;
|
||||
myf.f.data = myf.frdata;
|
||||
if (ast_write(chan, &myf.f) < 0) {
|
||||
res = -1;
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
if (res < needed) { /* last frame */
|
||||
if (res < needed) { // last frame
|
||||
ast_log(LOG_DEBUG, "Last frame\n");
|
||||
res=0;
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -286,9 +230,8 @@ static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, in
|
||||
}
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
|
||||
/* if (pid > -1) */
|
||||
/* kill(pid, SIGKILL); */
|
||||
// if (pid > -1)
|
||||
// kill(pid, SIGKILL);
|
||||
if (!res && owriteformat)
|
||||
ast_set_write_format(chan, owriteformat);
|
||||
return res;
|
||||
@@ -304,16 +247,16 @@ static int festival_exec(struct ast_channel *chan, void *vdata)
|
||||
{
|
||||
int usecache;
|
||||
int res=0;
|
||||
struct ast_module_user *u;
|
||||
struct localuser *u;
|
||||
struct sockaddr_in serv_addr;
|
||||
struct hostent *serverhost;
|
||||
struct ast_hostent ahp;
|
||||
int fd;
|
||||
FILE *fs;
|
||||
const char *host;
|
||||
const char *cachedir;
|
||||
const char *temp;
|
||||
const char *festivalcommand;
|
||||
char *host;
|
||||
char *cachedir;
|
||||
char *temp;
|
||||
char *festivalcommand;
|
||||
int port=1314;
|
||||
int n;
|
||||
char ack[4];
|
||||
@@ -333,22 +276,13 @@ static int festival_exec(struct ast_channel *chan, void *vdata)
|
||||
int fdesc = -1;
|
||||
char buffer[16384];
|
||||
int seekpos = 0;
|
||||
char *data;
|
||||
char data[256] = "";
|
||||
char *intstr;
|
||||
|
||||
struct ast_config *cfg;
|
||||
char *newfestivalcommand;
|
||||
|
||||
if (ast_strlen_zero(vdata)) {
|
||||
ast_log(LOG_WARNING, "festival requires an argument (text)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
cfg = ast_config_load(FESTIVAL_CONFIG);
|
||||
cfg = ast_load(FESTIVAL_CONFIG);
|
||||
if (!cfg) {
|
||||
ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
if (!(host = ast_variable_retrieve(cfg, "general", "host"))) {
|
||||
@@ -369,34 +303,20 @@ static int festival_exec(struct ast_channel *chan, void *vdata)
|
||||
}
|
||||
if (!(festivalcommand = ast_variable_retrieve(cfg, "general", "festivalcommand"))) {
|
||||
festivalcommand = "(tts_textasterisk \"%s\" 'file)(quit)\n";
|
||||
} else { /* This else parses the festivalcommand that we're sent from the config file for \n's, etc */
|
||||
int i, j;
|
||||
newfestivalcommand = alloca(strlen(festivalcommand) + 1);
|
||||
|
||||
for (i = 0, j = 0; i < strlen(festivalcommand); i++) {
|
||||
if (festivalcommand[i] == '\\' && festivalcommand[i + 1] == 'n') {
|
||||
newfestivalcommand[j++] = '\n';
|
||||
i++;
|
||||
} else if (festivalcommand[i] == '\\') {
|
||||
newfestivalcommand[j++] = festivalcommand[i + 1];
|
||||
i++;
|
||||
} else
|
||||
newfestivalcommand[j++] = festivalcommand[i];
|
||||
}
|
||||
newfestivalcommand[j] = '\0';
|
||||
festivalcommand = newfestivalcommand;
|
||||
}
|
||||
|
||||
data = ast_strdupa(vdata);
|
||||
|
||||
intstr = strchr(data, '|');
|
||||
if (intstr) {
|
||||
if (!vdata || ast_strlen_zero(vdata)) {
|
||||
ast_log(LOG_WARNING, "festival requires an argument (text)\n");
|
||||
ast_destroy(cfg);
|
||||
return -1;
|
||||
}
|
||||
strncpy(data, vdata, sizeof(data) - 1);
|
||||
if ((intstr = strchr(data, '|'))) {
|
||||
*intstr = '\0';
|
||||
intstr++;
|
||||
if (!strcasecmp(intstr, "any"))
|
||||
intstr = AST_DIGIT_ANY;
|
||||
}
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
ast_log(LOG_DEBUG, "Text passed to festival server : %s\n",(char *)data);
|
||||
/* Connect to local festival server */
|
||||
|
||||
@@ -404,8 +324,7 @@ static int festival_exec(struct ast_channel *chan, void *vdata)
|
||||
|
||||
if (fd < 0) {
|
||||
ast_log(LOG_WARNING,"festival_client: can't get socket\n");
|
||||
ast_config_destroy(cfg);
|
||||
ast_module_user_remove(u);
|
||||
ast_destroy(cfg);
|
||||
return -1;
|
||||
}
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
@@ -414,8 +333,7 @@ static int festival_exec(struct ast_channel *chan, void *vdata)
|
||||
serverhost = ast_gethostbyname(host, &ahp);
|
||||
if (serverhost == (struct hostent *)0) {
|
||||
ast_log(LOG_WARNING,"festival_client: gethostbyname failed\n");
|
||||
ast_config_destroy(cfg);
|
||||
ast_module_user_remove(u);
|
||||
ast_destroy(cfg);
|
||||
return -1;
|
||||
}
|
||||
memmove(&serv_addr.sin_addr,serverhost->h_addr, serverhost->h_length);
|
||||
@@ -425,8 +343,7 @@ static int festival_exec(struct ast_channel *chan, void *vdata)
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) != 0) {
|
||||
ast_log(LOG_WARNING,"festival_client: connect to server failed\n");
|
||||
ast_config_destroy(cfg);
|
||||
ast_module_user_remove(u);
|
||||
ast_destroy(cfg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -453,25 +370,17 @@ static int festival_exec(struct ast_channel *chan, void *vdata)
|
||||
writecache=1;
|
||||
strln=strlen((char *)data);
|
||||
ast_log(LOG_DEBUG,"line length : %d\n",strln);
|
||||
if (write(fdesc,&strln,sizeof(int)) < 0) {
|
||||
ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
|
||||
}
|
||||
if (write(fdesc,data,strln) < 0) {
|
||||
ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
|
||||
}
|
||||
write(fdesc,&strln,sizeof(int));
|
||||
write(fdesc,data,strln);
|
||||
seekpos=lseek(fdesc,0,SEEK_CUR);
|
||||
ast_log(LOG_DEBUG,"Seek position : %d\n",seekpos);
|
||||
}
|
||||
} else {
|
||||
if (read(fdesc,&strln,sizeof(int)) != sizeof(int)) {
|
||||
ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
|
||||
}
|
||||
read(fdesc,&strln,sizeof(int));
|
||||
ast_log(LOG_DEBUG,"Cache file exists, strln=%d, strlen=%d\n",strln,(int)strlen((char *)data));
|
||||
if (strlen((char *)data)==strln) {
|
||||
ast_log(LOG_DEBUG,"Size OK\n");
|
||||
if (read(fdesc,&bigstring,strln) != strln) {
|
||||
ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
|
||||
}
|
||||
read(fdesc,&bigstring,strln);
|
||||
bigstring[strln] = 0;
|
||||
if (strcmp(bigstring,data)==0) {
|
||||
readcache=1;
|
||||
@@ -500,9 +409,7 @@ static int festival_exec(struct ast_channel *chan, void *vdata)
|
||||
if (writecache==1) {
|
||||
ast_log(LOG_DEBUG,"Writing result to cache...\n");
|
||||
while ((strln=read(fd,buffer,16384))!=0) {
|
||||
if (write(fdesc,buffer,strln) < 0) {
|
||||
ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
|
||||
}
|
||||
write(fdesc,buffer,strln);
|
||||
}
|
||||
close(fd);
|
||||
close(fdesc);
|
||||
@@ -516,23 +423,8 @@ static int festival_exec(struct ast_channel *chan, void *vdata)
|
||||
/* This assumes only one waveform will come back, also LP is unlikely */
|
||||
wave = 0;
|
||||
do {
|
||||
int read_data;
|
||||
for (n=0; n < 3; )
|
||||
{
|
||||
read_data = read(fd,ack+n,3-n);
|
||||
/* this avoids falling in infinite loop
|
||||
* in case that festival server goes down
|
||||
* */
|
||||
if ( read_data == -1 )
|
||||
{
|
||||
ast_log(LOG_WARNING,"Unable to read from cache/festival fd\n");
|
||||
close(fd);
|
||||
ast_config_destroy(cfg);
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
n += read_data;
|
||||
}
|
||||
n += read(fd,ack+n,3-n);
|
||||
ack[3] = '\0';
|
||||
if (strcmp(ack,"WV\n") == 0) { /* receive a waveform */
|
||||
ast_log(LOG_DEBUG,"Festival WV command\n");
|
||||
@@ -554,32 +446,37 @@ static int festival_exec(struct ast_channel *chan, void *vdata)
|
||||
}
|
||||
} while (strcmp(ack,"OK\n") != 0);
|
||||
close(fd);
|
||||
ast_config_destroy(cfg);
|
||||
ast_module_user_remove(u);
|
||||
ast_destroy(cfg);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG);
|
||||
if (!cfg) {
|
||||
ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
ast_config_destroy(cfg);
|
||||
|
||||
return ast_register_application(app, festival_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Festival Interface");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
145
apps/app_flash.c
145
apps/app_flash.c
@@ -1,81 +1,58 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* App to flash a zap trunk
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief App to flash a DAHDI trunk
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>dahdi</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/image.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <sys/ioctl.h>
|
||||
#ifdef __linux__
|
||||
#include <linux/zaptel.h>
|
||||
#else
|
||||
#include <zaptel.h>
|
||||
#endif /* __linux__ */
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/image.h"
|
||||
#include "asterisk/options.h"
|
||||
|
||||
#include "asterisk/dahdi_compat.h"
|
||||
static char *tdesc = "Flash zap trunk application";
|
||||
|
||||
static char *app = "Flash";
|
||||
|
||||
static char *dahdi_synopsis = "Flashes a DAHDI trunk";
|
||||
static char *synopsis = "Flashes a Zap Trunk";
|
||||
|
||||
static char *dahdi_descrip =
|
||||
"Performs a flash on a DAHDI trunk. This can be used\n"
|
||||
"to access features provided on an incoming analogue circuit\n"
|
||||
"such as conference and call waiting. Use with SendDTMF() to\n"
|
||||
"perform external transfers\n";
|
||||
static char *descrip =
|
||||
" Flash(): Sends a flash on a zap trunk. This is only a hack for\n"
|
||||
"people who want to perform transfers and such via AGI and is generally\n"
|
||||
"quite useless otherwise. Returns 0 on success or -1 if this is not\n"
|
||||
"a zap trunk\n";
|
||||
|
||||
static char *zap_synopsis = "Flashes a Zap trunk";
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
static char *zap_descrip =
|
||||
"Performs a flash on a Zap trunk. This can be used\n"
|
||||
"to access features provided on an incoming analogue circuit\n"
|
||||
"such as conference and call waiting. Use with SendDTMF() to\n"
|
||||
"perform external transfers\n";
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static inline int zt_wait_event(int fd)
|
||||
{
|
||||
/* Avoid the silly zt_waitevent which ignores a bunch of events */
|
||||
int i,j=0;
|
||||
i = DAHDI_IOMUX_SIGEVENT;
|
||||
if (ioctl(fd, DAHDI_IOMUX, &i) == -1) return -1;
|
||||
if (ioctl(fd, DAHDI_GETEVENT, &j) == -1) return -1;
|
||||
i = ZT_IOMUX_SIGEVENT;
|
||||
if (ioctl(fd, ZT_IOMUX, &i) == -1) return -1;
|
||||
if (ioctl(fd, ZT_GETEVENT, &j) == -1) return -1;
|
||||
return j;
|
||||
}
|
||||
|
||||
@@ -83,16 +60,16 @@ static int flash_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = -1;
|
||||
int x;
|
||||
struct ast_module_user *u;
|
||||
struct dahdi_params ztp;
|
||||
u = ast_module_user_add(chan);
|
||||
if (!strcasecmp(chan->tech->type, dahdi_chan_name)) {
|
||||
struct localuser *u;
|
||||
struct zt_params ztp;
|
||||
LOCAL_USER_ADD(u);
|
||||
if (!strcasecmp(chan->type, "Zap")) {
|
||||
memset(&ztp, 0, sizeof(ztp));
|
||||
res = ioctl(chan->fds[0], DAHDI_GET_PARAMS, &ztp);
|
||||
res = ioctl(chan->fds[0], ZT_GET_PARAMS, &ztp);
|
||||
if (!res) {
|
||||
if (ztp.sigtype & __DAHDI_SIG_FXS) {
|
||||
x = DAHDI_FLASH;
|
||||
res = ioctl(chan->fds[0], DAHDI_HOOK, &x);
|
||||
if (ztp.sigtype & __ZT_SIG_FXS) {
|
||||
x = ZT_FLASH;
|
||||
res = ioctl(chan->fds[0], ZT_HOOK, &x);
|
||||
if (!res || (errno == EINPROGRESS)) {
|
||||
if (res) {
|
||||
/* Wait for the event to finish */
|
||||
@@ -108,29 +85,35 @@ static int flash_exec(struct ast_channel *chan, void *data)
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Unable to get parameters of %s: %s\n", chan->name, strerror(errno));
|
||||
} else
|
||||
ast_log(LOG_WARNING, "%s is not a DAHDI channel\n", chan->name);
|
||||
ast_module_user_remove(u);
|
||||
ast_log(LOG_WARNING, "%s is not a Zap channel\n", chan->name);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, flash_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
char *key()
|
||||
{
|
||||
if (*dahdi_chan_mode == CHAN_ZAP_MODE) {
|
||||
return ast_register_application(app, flash_exec, zap_synopsis, zap_descrip);
|
||||
} else {
|
||||
return ast_register_application(app, flash_exec, dahdi_synopsis, dahdi_descrip);
|
||||
}
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Flash channel application");
|
||||
|
||||
1148
apps/app_followme.c
1148
apps/app_followme.c
File diff suppressed because it is too large
Load Diff
@@ -1,267 +1,91 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Anthony Minessale anthmct@yahoo.com
|
||||
* Fork CDR application
|
||||
* Copyright Anthony Minessale anthmct@yahoo.com
|
||||
* Development of this app Sponsered/Funded by TAAN Softworks Corp
|
||||
*
|
||||
* 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 Fork CDR application
|
||||
*
|
||||
* \author Anthony Minessale anthmct@yahoo.com
|
||||
*
|
||||
* \note Development of this app Sponsored/Funded by TAAN Softworks Corp
|
||||
*
|
||||
* \ingroup applications
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/cdr.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/cdr.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/module.h"
|
||||
|
||||
static char *tdesc = "Fork The CDR into 2 separate entities.";
|
||||
static char *app = "ForkCDR";
|
||||
static char *synopsis =
|
||||
"Forks the Call Data Record";
|
||||
static char *descrip =
|
||||
" ForkCDR([options]): Causes the Call Data Record to fork an additional\n"
|
||||
"cdr record starting from the time of the fork call. This new cdr record will\n"
|
||||
"be linked to end of the list of cdr records attached to the channel. The original CDR is\n"
|
||||
"has a LOCKED flag set, which forces most cdr operations to skip it, except\n"
|
||||
"for the functions that set the answer and end times, which ignore the LOCKED\n"
|
||||
"flag. This allows all the cdr records in the channel to be 'ended' together\n"
|
||||
"when the channel is closed.\n"
|
||||
"The CDR() func (when setting CDR values) normally ignores the LOCKED flag also,\n"
|
||||
"but has options to vary its behavior. The 'T' option (described below), can\n"
|
||||
"override this behavior, but beware the risks.\n"
|
||||
"\n"
|
||||
"Detailed Behavior Description:\n"
|
||||
"First, this app finds the last cdr record in the list, and makes\n"
|
||||
"a copy of it. This new copy will be the newly forked cdr record.\n"
|
||||
"Next, this new record is linked to the end of the cdr record list.\n"
|
||||
"Next, The new cdr record is RESET (unless you use an option to prevent this)\n"
|
||||
"This means that:\n"
|
||||
" 1. All flags are unset on the cdr record\n"
|
||||
" 2. the start, end, and answer times are all set to zero.\n"
|
||||
" 3. the billsec and duration fields are set to zero.\n"
|
||||
" 4. the start time is set to the current time.\n"
|
||||
" 5. the disposition is set to NULL.\n"
|
||||
"Next, unless you specified the 'v' option, all variables will be\n"
|
||||
"removed from the original cdr record. Thus, the 'v' option allows\n"
|
||||
"any CDR variables to be replicated to all new forked cdr records.\n"
|
||||
"Without the 'v' option, the variables on the original are effectively\n"
|
||||
"moved to the new forked cdr record.\n"
|
||||
"Next, if the 's' option is set, the provided variable and value\n"
|
||||
"are set on the original cdr record.\n"
|
||||
"Next, if the 'a' option is given, and the original cdr record has an\n"
|
||||
"answer time set, then the new forked cdr record will have its answer\n"
|
||||
"time set to its start time. If the old answer time were carried forward,\n"
|
||||
"the answer time would be earlier than the start time, giving strange\n"
|
||||
"duration and billsec times.\n"
|
||||
"Next, if the 'd' option was specified, the disposition is copied from\n"
|
||||
"the original cdr record to the new forked cdr.\n"
|
||||
"Next, if the 'D' option was specified, the destination channel field\n"
|
||||
"in the new forked CDR is erased.\n"
|
||||
"Next, if the 'e' option was specified, the 'end' time for the original\n"
|
||||
"cdr record is set to the current time. Future hang-up or ending events\n"
|
||||
"will not override this time stamp.\n"
|
||||
"Next, If the 'A' option is specified, the original cdr record will have\n"
|
||||
"it ANS_LOCKED flag set, which prevent future answer events\n"
|
||||
"from updating the original cdr record's disposition. Normally, an\n"
|
||||
"'ANSWERED' event would mark all cdr records in the chain as 'ANSWERED'.\n"
|
||||
"Next, if the 'T' option is specified, the original cdr record will have\n"
|
||||
"its 'DONT_TOUCH' flag set, which will force the cdr_answer, cdr_end, and\n"
|
||||
"cdr_setvar functions to leave that cdr record alone.\n"
|
||||
"And, last but not least, the original cdr record has its LOCKED flag\n"
|
||||
"set. Almost all internal CDR functions (except for the funcs that set\n"
|
||||
"the end, and answer times, and set a variable) will honor this flag\n"
|
||||
"and leave a LOCKED cdr record alone.\n"
|
||||
"This means that the newly created forked cdr record will affected\n"
|
||||
"by events transpiring within Asterisk, with the previously noted\n"
|
||||
"exceptions.\n"
|
||||
" Options:\n"
|
||||
" a - update the answer time on the NEW CDR just after it's been inited..\n"
|
||||
" The new CDR may have been answered already, the reset that forkcdr.\n"
|
||||
" does will erase the answer time. This will bring it back, but\n"
|
||||
" the answer time will be a copy of the fork/start time. It will.\n"
|
||||
" only do this if the initial cdr was indeed already answered..\n"
|
||||
" A - Lock the original CDR against the answer time being updated.\n"
|
||||
" This will allow the disposition on the original CDR to remain the same.\n"
|
||||
" d - Copy the disposition forward from the old cdr, after the .\n"
|
||||
" init..\n"
|
||||
" D - Clear the dstchannel on the new CDR after reset..\n"
|
||||
" e - end the original CDR. Do this after all the necc. data.\n"
|
||||
" is copied from the original CDR to the new forked CDR..\n"
|
||||
" R - do NOT reset the new cdr..\n"
|
||||
" s(name=val) - Set the CDR var 'name' in the original CDR, with value.\n"
|
||||
" 'val'.\n"
|
||||
" T - Mark the original CDR with a DONT_TOUCH flag. setvar, answer, and end\n"
|
||||
" cdr funcs will obey this flag; normally they don't honor the LOCKED\n"
|
||||
" flag set on the original CDR record.\n"
|
||||
" Beware-- using this flag may cause CDR's not to have their end times\n"
|
||||
" updated! It is suggested that if you specify this flag, you might\n"
|
||||
" wish to use the 'e' flag as well!\n"
|
||||
" v - When the new CDR is forked, it gets a copy of the vars attached\n"
|
||||
" to the current CDR. The vars attached to the original CDR are removed\n"
|
||||
" unless this option is specified.\n";
|
||||
" ForkCDR(): Causes the Call Data Record to fork an additional\n"
|
||||
"cdr record starting from the time of the fork call\n";
|
||||
|
||||
|
||||
enum {
|
||||
OPT_SETANS = (1 << 0),
|
||||
OPT_SETDISP = (1 << 1),
|
||||
OPT_RESETDEST = (1 << 2),
|
||||
OPT_ENDCDR = (1 << 3),
|
||||
OPT_NORESET = (1 << 4),
|
||||
OPT_KEEPVARS = (1 << 5),
|
||||
OPT_VARSET = (1 << 6),
|
||||
OPT_ANSLOCK = (1 << 7),
|
||||
OPT_DONTOUCH = (1 << 8),
|
||||
};
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
enum {
|
||||
OPT_ARG_VARSET = 0,
|
||||
/* note: this entry _MUST_ be the last one in the enum */
|
||||
OPT_ARG_ARRAY_SIZE,
|
||||
};
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
AST_APP_OPTIONS(forkcdr_exec_options, {
|
||||
AST_APP_OPTION('a', OPT_SETANS),
|
||||
AST_APP_OPTION('A', OPT_ANSLOCK),
|
||||
AST_APP_OPTION('d', OPT_SETDISP),
|
||||
AST_APP_OPTION('D', OPT_RESETDEST),
|
||||
AST_APP_OPTION('e', OPT_ENDCDR),
|
||||
AST_APP_OPTION('R', OPT_NORESET),
|
||||
AST_APP_OPTION_ARG('s', OPT_VARSET, OPT_ARG_VARSET),
|
||||
AST_APP_OPTION('T', OPT_DONTOUCH),
|
||||
AST_APP_OPTION('v', OPT_KEEPVARS),
|
||||
});
|
||||
|
||||
static void ast_cdr_fork(struct ast_channel *chan, struct ast_flags optflags, char *set)
|
||||
{
|
||||
struct ast_cdr *cdr;
|
||||
struct ast_cdr *newcdr;
|
||||
struct ast_flags flags = { AST_CDR_FLAG_KEEP_VARS };
|
||||
static void ast_cdr_clone(struct ast_cdr *cdr) {
|
||||
struct ast_cdr *newcdr = ast_cdr_alloc();
|
||||
memcpy(newcdr,cdr,sizeof(struct ast_cdr));
|
||||
newcdr->next = NULL;
|
||||
ast_cdr_append(cdr,newcdr);
|
||||
gettimeofday(&newcdr->start, NULL);
|
||||
memset(&newcdr->answer, 0, sizeof(newcdr->answer));
|
||||
ast_cdr_add_flag(cdr,AST_CDR_FLAG_CHILD|AST_CDR_FLAG_LOCKED);
|
||||
}
|
||||
|
||||
cdr = chan->cdr;
|
||||
|
||||
while (cdr->next)
|
||||
cdr = cdr->next;
|
||||
|
||||
if (!(newcdr = ast_cdr_dup(cdr)))
|
||||
return;
|
||||
|
||||
ast_cdr_append(cdr, newcdr);
|
||||
|
||||
if (!ast_test_flag(&optflags, OPT_NORESET))
|
||||
ast_cdr_reset(newcdr, &flags);
|
||||
|
||||
if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS))
|
||||
ast_cdr_free_vars(cdr, 0);
|
||||
|
||||
if (!ast_strlen_zero(set)) {
|
||||
char *varname = ast_strdupa(set), *varval;
|
||||
varval = strchr(varname,'=');
|
||||
if (varval) {
|
||||
*varval = 0;
|
||||
varval++;
|
||||
ast_cdr_setvar(cdr, varname, varval, 0);
|
||||
}
|
||||
static void ast_cdr_fork(struct ast_channel *chan) {
|
||||
if(chan && chan->cdr) {
|
||||
ast_cdr_clone(chan->cdr);
|
||||
}
|
||||
|
||||
if (ast_test_flag(&optflags, OPT_SETANS) && !ast_tvzero(cdr->answer))
|
||||
newcdr->answer = newcdr->start;
|
||||
|
||||
if (ast_test_flag(&optflags, OPT_SETDISP))
|
||||
newcdr->disposition = cdr->disposition;
|
||||
|
||||
if (ast_test_flag(&optflags, OPT_RESETDEST))
|
||||
newcdr->dstchannel[0] = 0;
|
||||
|
||||
if (ast_test_flag(&optflags, OPT_ENDCDR))
|
||||
ast_cdr_end(cdr);
|
||||
|
||||
if (ast_test_flag(&optflags, OPT_ANSLOCK))
|
||||
ast_set_flag(cdr, AST_CDR_FLAG_ANSLOCKED);
|
||||
|
||||
if (ast_test_flag(&optflags, OPT_DONTOUCH))
|
||||
ast_set_flag(cdr, AST_CDR_FLAG_DONT_TOUCH);
|
||||
|
||||
ast_set_flag(cdr, AST_CDR_FLAG_CHILD | AST_CDR_FLAG_LOCKED);
|
||||
}
|
||||
|
||||
static int forkcdr_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_module_user *u;
|
||||
char *argcopy = NULL;
|
||||
struct ast_flags flags = {0};
|
||||
char *opts[OPT_ARG_ARRAY_SIZE];
|
||||
AST_DECLARE_APP_ARGS(arglist,
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
int res=0;
|
||||
struct localuser *u;
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
if (!chan->cdr) {
|
||||
ast_log(LOG_WARNING, "Channel does not have a CDR\n");
|
||||
return 0;
|
||||
}
|
||||
ast_cdr_fork(chan);
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
argcopy = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(arglist, argcopy);
|
||||
|
||||
opts[OPT_ARG_VARSET] = 0;
|
||||
|
||||
if (!ast_strlen_zero(arglist.options))
|
||||
ast_app_parse_options(forkcdr_exec_options, &flags, opts, arglist.options);
|
||||
|
||||
if (!ast_strlen_zero(data))
|
||||
ast_set2_flag(chan->cdr, ast_test_flag(&flags, OPT_KEEPVARS), AST_CDR_FLAG_KEEP_VARS);
|
||||
|
||||
ast_cdr_fork(chan, flags, opts[OPT_ARG_VARSET]);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, forkcdr_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Fork The CDR into 2 separate entities");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,47 +1,30 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Execute arbitrary system commands
|
||||
*
|
||||
* Copyright (C) 1999-2005, Digium
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Get ADSI CPE ID
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/adsi.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/adsi.h"
|
||||
#include "asterisk/options.h"
|
||||
static char *tdesc = "Get ADSI CPE ID";
|
||||
|
||||
static char *app = "GetCPEID";
|
||||
|
||||
@@ -49,8 +32,12 @@ static char *synopsis = "Get ADSI CPE ID";
|
||||
|
||||
static char *descrip =
|
||||
" GetCPEID: Obtains and displays ADSI CPE ID and other information in order\n"
|
||||
"to properly setup chan_dahdi.conf for on-hook operations.\n";
|
||||
"to properly setup zapata.conf for on-hook operations.\n"
|
||||
"Returns -1 on hangup only.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int cpeid_setstatus(struct ast_channel *chan, char *stuff[], int voice)
|
||||
{
|
||||
@@ -60,42 +47,43 @@ static int cpeid_setstatus(struct ast_channel *chan, char *stuff[], int voice)
|
||||
for (x=0;x<4;x++)
|
||||
tmp[x] = stuff[x];
|
||||
tmp[4] = NULL;
|
||||
return ast_adsi_print(chan, tmp, justify, voice);
|
||||
return adsi_print(chan, tmp, justify, voice);
|
||||
}
|
||||
|
||||
static int cpeid_exec(struct ast_channel *chan, void *idata)
|
||||
{
|
||||
int res=0;
|
||||
struct ast_module_user *u;
|
||||
struct localuser *u;
|
||||
unsigned char cpeid[4];
|
||||
int gotgeometry = 0;
|
||||
int gotcpeid = 0;
|
||||
int width, height, buttons;
|
||||
char *data[4];
|
||||
unsigned int x;
|
||||
char data[4][80];
|
||||
char *stuff[4];
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
for (x = 0; x < 4; x++)
|
||||
data[x] = alloca(80);
|
||||
|
||||
strcpy(data[0], "** CPE Info **");
|
||||
strcpy(data[1], "Identifying CPE...");
|
||||
strcpy(data[2], "Please wait...");
|
||||
res = ast_adsi_load_session(chan, NULL, 0, 1);
|
||||
LOCAL_USER_ADD(u);
|
||||
stuff[0] = data[0];
|
||||
stuff[1] = data[1];
|
||||
stuff[2] = data[2];
|
||||
stuff[3] = data[3];
|
||||
memset(data, 0, sizeof(data));
|
||||
strncpy(stuff[0], "** CPE Info **", sizeof(data[0]) - 1);
|
||||
strncpy(stuff[1], "Identifying CPE...", sizeof(data[1]) - 1);
|
||||
strncpy(stuff[2], "Please wait...", sizeof(data[2]) - 1);
|
||||
res = adsi_load_session(chan, NULL, 0, 1);
|
||||
if (res > 0) {
|
||||
cpeid_setstatus(chan, data, 0);
|
||||
res = ast_adsi_get_cpeid(chan, cpeid, 0);
|
||||
cpeid_setstatus(chan, stuff, 0);
|
||||
res = adsi_get_cpeid(chan, cpeid, 0);
|
||||
if (res > 0) {
|
||||
gotcpeid = 1;
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Got CPEID of '%02x:%02x:%02x:%02x' on '%s'\n", cpeid[0], cpeid[1], cpeid[2], cpeid[3], chan->name);
|
||||
}
|
||||
if (res > -1) {
|
||||
strcpy(data[1], "Measuring CPE...");
|
||||
strcpy(data[2], "Please wait...");
|
||||
cpeid_setstatus(chan, data, 0);
|
||||
res = ast_adsi_get_cpeinfo(chan, &width, &height, &buttons, 0);
|
||||
strncpy(stuff[1], "Measuring CPE...", sizeof(data[1]) - 1);
|
||||
strncpy(stuff[2], "Please wait...", sizeof(data[2]) - 1);
|
||||
cpeid_setstatus(chan, stuff, 0);
|
||||
res = adsi_get_cpeinfo(chan, &width, &height, &buttons, 0);
|
||||
if (res > -1) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "CPE has %d lines, %d columns, and %d buttons on '%s'\n", height, width, buttons, chan->name);
|
||||
@@ -104,15 +92,15 @@ static int cpeid_exec(struct ast_channel *chan, void *idata)
|
||||
}
|
||||
if (res > -1) {
|
||||
if (gotcpeid)
|
||||
snprintf(data[1], 80, "CPEID: %02x:%02x:%02x:%02x", cpeid[0], cpeid[1], cpeid[2], cpeid[3]);
|
||||
snprintf(stuff[1], sizeof(data[1]), "CPEID: %02x:%02x:%02x:%02x", cpeid[0], cpeid[1], cpeid[2], cpeid[3]);
|
||||
else
|
||||
strcpy(data[1], "CPEID Unknown");
|
||||
strncpy(stuff[1], "CPEID Unknown", sizeof(data[1]) - 1);
|
||||
if (gotgeometry)
|
||||
snprintf(data[2], 80, "Geom: %dx%d, %d buttons", width, height, buttons);
|
||||
snprintf(stuff[2], sizeof(data[2]), "Geom: %dx%d, %d buttons", width, height, buttons);
|
||||
else
|
||||
strcpy(data[2], "Geometry unknown");
|
||||
strcpy(data[3], "Press # to exit");
|
||||
cpeid_setstatus(chan, data, 1);
|
||||
strncpy(stuff[2], "Geometry unknown", sizeof(data[2]) - 1);
|
||||
strncpy(stuff[3], "Press # to exit", sizeof(data[3]) - 1);
|
||||
cpeid_setstatus(chan, stuff, 1);
|
||||
for(;;) {
|
||||
res = ast_waitfordigit(chan, 1000);
|
||||
if (res < 0)
|
||||
@@ -122,27 +110,37 @@ static int cpeid_exec(struct ast_channel *chan, void *idata)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ast_adsi_unload_session(chan);
|
||||
adsi_unload_session(chan);
|
||||
}
|
||||
}
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, cpeid_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Get ADSI CPE ID");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
224
apps/app_groupcount.c
Normal file
224
apps/app_groupcount.c
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Group Manipulation Applications
|
||||
*
|
||||
* Copyright (c) 2004 Digium
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/utils.h>
|
||||
|
||||
static char *tdesc = "Group Management Routines";
|
||||
|
||||
static char *app_group_count = "GetGroupCount";
|
||||
static char *app_group_set = "SetGroup";
|
||||
static char *app_group_check = "CheckGroup";
|
||||
|
||||
static char *group_count_synopsis = "GetGroupCount([groupname][@category])";
|
||||
static char *group_set_synopsis = "SetGroup(groupname[@category])";
|
||||
static char *group_check_synopsis = "CheckGroup(max[@category])";
|
||||
|
||||
static char *group_count_descrip =
|
||||
"GetGroupCount([group][@category])\n"
|
||||
" Calculates the group count for the specified group, or uses\n"
|
||||
"the current channel's group if not specifed (and non-empty).\n"
|
||||
"Stores result in GROUPCOUNT. Always returns 0.\n";
|
||||
|
||||
static char *group_set_descrip =
|
||||
"SetGroup(group)\n"
|
||||
" Sets the channel group to the specified value. Equivalent to\n"
|
||||
"SetVar(GROUP=group). Always returns 0.\n";
|
||||
|
||||
static char *group_check_descrip =
|
||||
"CheckGroup(max)\n"
|
||||
" Checks that the current number of total channels in the\n"
|
||||
"current channel's group does not exceed 'max'. If the number\n"
|
||||
"does not exceed 'max', we continue to the next step. If the\n"
|
||||
"number does in fact exceed max, if priority n+101 exists, then\n"
|
||||
"execution continues at that step, otherwise -1 is returned.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
#define DEFAULT_CATEGORY "GROUP"
|
||||
|
||||
static int group_get_count(char *group, char *category)
|
||||
{
|
||||
struct ast_channel *chan;
|
||||
int count = 0;
|
||||
char *test;
|
||||
if (group && !ast_strlen_zero(group)) {
|
||||
chan = ast_channel_walk_locked(NULL);
|
||||
while(chan) {
|
||||
test = pbx_builtin_getvar_helper(chan, category);
|
||||
if (test && !strcasecmp(test, group))
|
||||
count++;
|
||||
ast_mutex_unlock(&chan->lock);
|
||||
chan = ast_channel_walk_locked(chan);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int group_count_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
int count;
|
||||
struct localuser *u;
|
||||
char *group=NULL;
|
||||
char *cat = NULL;
|
||||
char ret[80]="";
|
||||
char tmp[256]="";
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
/* Check and parse arguments */
|
||||
if (data && !ast_strlen_zero(data)) {
|
||||
strncpy(tmp, data, sizeof(tmp) - 1);
|
||||
group = tmp;
|
||||
cat = strchr(tmp, '@');
|
||||
if (cat) {
|
||||
*cat = '\0';
|
||||
cat++;
|
||||
}
|
||||
}
|
||||
if (cat)
|
||||
snprintf(ret, sizeof(ret), "GROUP_%s", cat);
|
||||
else
|
||||
strncpy(ret, DEFAULT_CATEGORY, sizeof(ret) - 1);
|
||||
|
||||
if (!group || ast_strlen_zero(group)) {
|
||||
group = pbx_builtin_getvar_helper(chan, ret);
|
||||
}
|
||||
count = group_get_count(group, ret);
|
||||
snprintf(ret, sizeof(ret), "%d", count);
|
||||
pbx_builtin_setvar_helper(chan, "GROUPCOUNT", ret);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int group_set_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
struct localuser *u;
|
||||
char ret[80] = "";
|
||||
char tmp[256] = "";
|
||||
char *cat=NULL, *group=NULL;
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
/* Check and parse arguments */
|
||||
if (data && !ast_strlen_zero(data)) {
|
||||
strncpy(tmp, data, sizeof(tmp) - 1);
|
||||
group = tmp;
|
||||
cat = strchr(tmp, '@');
|
||||
if (cat) {
|
||||
*cat = '\0';
|
||||
cat++;
|
||||
}
|
||||
}
|
||||
if (cat)
|
||||
snprintf(ret, sizeof(ret), "GROUP_%s", cat);
|
||||
else
|
||||
strncpy(ret, DEFAULT_CATEGORY, sizeof(ret) - 1);
|
||||
|
||||
if (group && !ast_strlen_zero(group)) {
|
||||
pbx_builtin_setvar_helper(chan, ret, group);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "SetGroup requires an argument (group name)\n");
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int group_check_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
int max, count;
|
||||
struct localuser *u;
|
||||
char ret[80] = "";
|
||||
char tmp[256] = "";
|
||||
char *cat, *group;
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
if (data && !ast_strlen_zero(data)) {
|
||||
strncpy(tmp, data, sizeof(tmp) - 1);
|
||||
group = tmp;
|
||||
cat = strchr(tmp, '@');
|
||||
if (cat) {
|
||||
*cat = '\0';
|
||||
cat++;
|
||||
}
|
||||
if ((sscanf((char *)tmp, "%i", &max) == 1) && (max > -1)) {
|
||||
if (cat)
|
||||
snprintf(ret, sizeof(ret), "GROUP_%s", cat);
|
||||
else
|
||||
strncpy(ret, DEFAULT_CATEGORY, sizeof(ret) - 1);
|
||||
|
||||
count = group_get_count(pbx_builtin_getvar_helper(chan, ret), ret);
|
||||
if (count > max) {
|
||||
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
|
||||
chan->priority += 100;
|
||||
else
|
||||
res = -1;
|
||||
}
|
||||
} else
|
||||
ast_log(LOG_WARNING, "CheckGroup requires a positive integer argument (max)\n");
|
||||
} else
|
||||
ast_log(LOG_WARNING, "CheckGroup requires an argument(max)\n");
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
res = ast_unregister_application(app_group_count);
|
||||
res |= ast_unregister_application(app_group_set);
|
||||
res |= ast_unregister_application(app_group_check);
|
||||
return res;
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
int res;
|
||||
res = ast_register_application(app_group_count, group_count_exec, group_count_synopsis, group_count_descrip);
|
||||
res |= ast_register_application(app_group_set, group_set_exec, group_set_synopsis, group_set_descrip);
|
||||
res |= ast_register_application(app_group_check, group_check_exec, group_check_synopsis, group_check_descrip);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Changes Copyright (c) 2004 - 2006 Todd Freeman <freeman@andrews.edu>
|
||||
* HasVoicemail application
|
||||
* Changes Copyright (c) 2004 Todd Freeman <freeman@andrews.edu>
|
||||
*
|
||||
* 95% based on HasNewVoicemail by:
|
||||
*
|
||||
@@ -9,217 +10,160 @@
|
||||
*
|
||||
* Tilghman Lesher <asterisk-hasnewvoicemail-app@the-tilghman.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 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.
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief HasVoicemail application
|
||||
*
|
||||
* \author Todd Freeman <freeman@andrews.edu>
|
||||
*
|
||||
* \note 95% based on HasNewVoicemail by
|
||||
* Tilghman Lesher <asterisk-hasnewvoicemail-app@the-tilghman.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "../astconf.h"
|
||||
|
||||
static char *tdesc = "Indicator for whether a voice mailbox has messages in a given folder.";
|
||||
static char *app_hasvoicemail = "HasVoicemail";
|
||||
static char *hasvoicemail_synopsis = "Conditionally branches to priority + 101 with the right options set";
|
||||
static char *hasvoicemail_synopsis = "Conditionally branches to priority + 101";
|
||||
static char *hasvoicemail_descrip =
|
||||
"HasVoicemail(vmbox[/folder][@context][|varname[|options]])\n"
|
||||
"HasVoicemail(vmbox[@context][:folder][|varname])\n"
|
||||
" Branches to priority + 101, if there is voicemail in folder indicated."
|
||||
" Optionally sets <varname> to the number of messages in that folder."
|
||||
" Assumes folder of INBOX if not specified.\n"
|
||||
" The option string may contain zero or the following character:\n"
|
||||
" 'j' -- jump to priority n+101, if there is voicemail in the folder indicated.\n"
|
||||
" This application sets the following channel variable upon completion:\n"
|
||||
" HASVMSTATUS The result of the voicemail check returned as a text string as follows\n"
|
||||
" <# of messages in the folder, 0 for NONE>\n"
|
||||
"\nThis application has been deprecated in favor of the VMCOUNT() function\n";
|
||||
" Assumes folder of INBOX if not specified.\n";
|
||||
|
||||
static char *app_hasnewvoicemail = "HasNewVoicemail";
|
||||
static char *hasnewvoicemail_synopsis = "Conditionally branches to priority + 101 with the right options set";
|
||||
static char *hasnewvoicemail_synopsis = "Conditionally branches to priority + 101";
|
||||
static char *hasnewvoicemail_descrip =
|
||||
"HasNewVoicemail(vmbox[/folder][@context][|varname[|options]])\n"
|
||||
"Assumes folder 'INBOX' if folder is not specified. Optionally sets <varname> to the number of messages\n"
|
||||
"in that folder.\n"
|
||||
" The option string may contain zero of the following character:\n"
|
||||
" 'j' -- jump to priority n+101, if there is new voicemail in folder 'folder' or INBOX\n"
|
||||
" This application sets the following channel variable upon completion:\n"
|
||||
" HASVMSTATUS The result of the new voicemail check returned as a text string as follows\n"
|
||||
" <# of messages in the folder, 0 for NONE>\n"
|
||||
"\nThis application has been deprecated in favor of the VMCOUNT() function\n";
|
||||
"HasNewVoicemail(vmbox[@context][|varname])\n"
|
||||
" Branches to priority + 101, if there is voicemail in folder INBOX."
|
||||
" Optionally sets <varname> to the number of messages in that folder.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int hasvoicemail_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_module_user *u;
|
||||
char *input, *varname = NULL, *vmbox, *context = "default";
|
||||
char *vmfolder;
|
||||
int res=0;
|
||||
struct localuser *u;
|
||||
char vmpath[256], *temps, *input, *varname = NULL, *vmbox, *vmfolder = "INBOX", *context = "default";
|
||||
DIR *vmdir;
|
||||
struct dirent *vment;
|
||||
int vmcount = 0;
|
||||
static int dep_warning = 0;
|
||||
int priority_jump = 0;
|
||||
char tmp[12];
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(vmbox);
|
||||
AST_APP_ARG(varname);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
if (!dep_warning) {
|
||||
ast_log(LOG_WARNING, "The applications HasVoicemail and HasNewVoicemail have been deprecated. Please use the VMCOUNT() function instead.\n");
|
||||
dep_warning = 1;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
ast_log(LOG_WARNING, "HasVoicemail requires an argument (vm-box[/folder][@context][|varname[|options]])\n");
|
||||
ast_log(LOG_WARNING, "HasVoicemail requires an argument (vm-box[@context][:folder]|varname)\n");
|
||||
return -1;
|
||||
}
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
input = ast_strdupa((char *)data);
|
||||
if (input) {
|
||||
temps = input;
|
||||
if ((temps = strsep(&input, "|"))) {
|
||||
if (input && !ast_strlen_zero(input))
|
||||
varname = input;
|
||||
input = temps;
|
||||
}
|
||||
if ((temps = strsep(&input, ":"))) {
|
||||
if (input && !ast_strlen_zero(input))
|
||||
vmfolder = input;
|
||||
input = temps;
|
||||
}
|
||||
if ((vmbox = strsep(&input, "@")))
|
||||
if (input && !ast_strlen_zero(input))
|
||||
context = input;
|
||||
if (!vmbox)
|
||||
vmbox = input;
|
||||
|
||||
input = ast_strdupa(data);
|
||||
snprintf(vmpath,sizeof(vmpath), "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR, context, vmbox, vmfolder);
|
||||
if (!(vmdir = opendir(vmpath))) {
|
||||
ast_log(LOG_NOTICE, "Voice mailbox %s at %s does not exist\n", vmbox, vmpath);
|
||||
} else {
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, input);
|
||||
/* No matter what the format of VM, there will always be a .txt file for each message. */
|
||||
while ((vment = readdir(vmdir)))
|
||||
if (!strncmp(vment->d_name + 7,".txt",4))
|
||||
vmcount++;
|
||||
closedir(vmdir);
|
||||
}
|
||||
/* Set the count in the channel variable */
|
||||
if (varname) {
|
||||
char tmp[12];
|
||||
snprintf(tmp, sizeof(tmp), "%d", vmcount);
|
||||
pbx_builtin_setvar_helper(chan, varname, tmp);
|
||||
}
|
||||
|
||||
vmbox = strsep(&args.vmbox, "@");
|
||||
|
||||
if (!ast_strlen_zero(args.vmbox))
|
||||
context = args.vmbox;
|
||||
|
||||
vmfolder = strchr(vmbox, '/');
|
||||
if (vmfolder) {
|
||||
*vmfolder = '\0';
|
||||
vmfolder++;
|
||||
} else {
|
||||
vmfolder = "INBOX";
|
||||
}
|
||||
|
||||
if (args.options) {
|
||||
if (strchr(args.options, 'j'))
|
||||
priority_jump = 1;
|
||||
}
|
||||
|
||||
vmcount = ast_app_messagecount(context, vmbox, vmfolder);
|
||||
/* Set the count in the channel variable */
|
||||
if (varname) {
|
||||
snprintf(tmp, sizeof(tmp), "%d", vmcount);
|
||||
pbx_builtin_setvar_helper(chan, varname, tmp);
|
||||
}
|
||||
|
||||
if (vmcount > 0) {
|
||||
/* Branch to the next extension */
|
||||
if (priority_jump || ast_opt_priority_jumping) {
|
||||
if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
|
||||
if (vmcount > 0) {
|
||||
/* Branch to the next extension */
|
||||
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) {
|
||||
chan->priority += 100;
|
||||
} else
|
||||
ast_log(LOG_WARNING, "VM box %s@%s has new voicemail, but extension %s, priority %d doesn't exist\n", vmbox, context, chan->exten, chan->priority + 101);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "%d", vmcount);
|
||||
pbx_builtin_setvar_helper(chan, "HASVMSTATUS", tmp);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acf_vmcount_exec(struct ast_channel *chan, char *cmd, char *argsstr, char *buf, size_t len)
|
||||
{
|
||||
struct ast_module_user *u;
|
||||
char *context;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(vmbox);
|
||||
AST_APP_ARG(folder);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(argsstr))
|
||||
return -1;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
buf[0] = '\0';
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, argsstr);
|
||||
|
||||
if (strchr(args.vmbox, '@')) {
|
||||
context = args.vmbox;
|
||||
args.vmbox = strsep(&context, "@");
|
||||
} else {
|
||||
context = "default";
|
||||
ast_log(LOG_ERROR, "Out of memory error\n");
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(args.folder)) {
|
||||
args.folder = "INBOX";
|
||||
}
|
||||
|
||||
snprintf(buf, len, "%d", ast_app_messagecount(context, args.vmbox, args.folder));
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
struct ast_custom_function acf_vmcount = {
|
||||
.name = "VMCOUNT",
|
||||
.synopsis = "Counts the voicemail in a specified mailbox",
|
||||
.syntax = "VMCOUNT(vmbox[@context][|folder])",
|
||||
.desc =
|
||||
" context - defaults to \"default\"\n"
|
||||
" folder - defaults to \"INBOX\"\n",
|
||||
.read = acf_vmcount_exec,
|
||||
};
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_custom_function_unregister(&acf_vmcount);
|
||||
res |= ast_unregister_application(app_hasvoicemail);
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
res = ast_unregister_application(app_hasvoicemail);
|
||||
res |= ast_unregister_application(app_hasnewvoicemail);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_custom_function_register(&acf_vmcount);
|
||||
res |= ast_register_application(app_hasvoicemail, hasvoicemail_exec, hasvoicemail_synopsis, hasvoicemail_descrip);
|
||||
res = ast_register_application(app_hasvoicemail, hasvoicemail_exec, hasvoicemail_synopsis, hasvoicemail_descrip);
|
||||
res |= ast_register_application(app_hasnewvoicemail, hasvoicemail_exec, hasnewvoicemail_synopsis, hasnewvoicemail_descrip);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Indicator for whether a voice mailbox has messages in a given folder.");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
179
apps/app_ices.c
179
apps/app_ices.c
@@ -1,38 +1,24 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Stream to an icecast server via ICES (see contrib/asterisk-ices.xml)
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Stream to an icecast server via ICES (see contrib/asterisk-ices.xml)
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>working_fork</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/frame.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
@@ -41,22 +27,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_CAP
|
||||
#include <sys/capability.h>
|
||||
#endif /* HAVE_CAP */
|
||||
#include "../astconf.h"
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/frame.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/options.h"
|
||||
#define ICES "/usr/bin/ices"
|
||||
#define LOCAL_ICES "/usr/local/bin/ices"
|
||||
|
||||
#define path_BIN "/usr/bin/"
|
||||
#define path_LOCAL "/usr/local/bin/"
|
||||
static char *tdesc = "Encode and Stream via icecast and ices";
|
||||
|
||||
static char *app = "ICES";
|
||||
|
||||
@@ -65,74 +41,41 @@ static char *synopsis = "Encode and stream using 'ices'";
|
||||
static char *descrip =
|
||||
" ICES(config.xml) Streams to an icecast server using ices\n"
|
||||
"(available separately). A configuration file must be supplied\n"
|
||||
"for ices (see contrib/asterisk-ices.xml). \n";
|
||||
"for ices (see examples/asterisk-ices.conf). Returns -1 on\n"
|
||||
"hangup or 0 otherwise.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int icesencode(char *filename, int fd)
|
||||
{
|
||||
int res;
|
||||
int x;
|
||||
sigset_t fullset, oldset;
|
||||
#ifdef HAVE_CAP
|
||||
cap_t cap;
|
||||
#endif
|
||||
|
||||
sigfillset(&fullset);
|
||||
pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
|
||||
|
||||
res = fork();
|
||||
if (res < 0)
|
||||
ast_log(LOG_WARNING, "Fork failed\n");
|
||||
if (res) {
|
||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Stop ignoring PIPE */
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
|
||||
|
||||
#ifdef HAVE_CAP
|
||||
cap = cap_from_text("cap_net_admin-eip");
|
||||
|
||||
if (cap_set_proc(cap)) {
|
||||
/* Careful with order! Logging cannot happen after we close FDs */
|
||||
ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
|
||||
}
|
||||
cap_free(cap);
|
||||
#endif
|
||||
|
||||
if (ast_opt_high_priority)
|
||||
ast_set_priority(0);
|
||||
dup2(fd, STDIN_FILENO);
|
||||
for (x=STDERR_FILENO + 1;x<1024;x++) {
|
||||
for (x=STDERR_FILENO + 1;x<256;x++) {
|
||||
if ((x != STDIN_FILENO) && (x != STDOUT_FILENO))
|
||||
close(x);
|
||||
}
|
||||
|
||||
/* Most commonly installed in /usr/local/bin
|
||||
* But many places has it in /usr/bin
|
||||
* As a last-ditch effort, try to use PATH
|
||||
*/
|
||||
execl(path_LOCAL "ices2", "ices", filename, (char *)NULL);
|
||||
execl(path_BIN "ices2", "ices", filename, (char *)NULL);
|
||||
execlp("ices2", "ices", filename, (char *)NULL);
|
||||
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Couldn't find ices version 2, attempting to use ices version 1.");
|
||||
|
||||
execl(path_LOCAL "ices", "ices", filename, (char *)NULL);
|
||||
execl(path_BIN "ices", "ices", filename, (char *)NULL);
|
||||
/* Most commonly installed in /usr/local/bin */
|
||||
execl(ICES, "ices", filename, (char *)NULL);
|
||||
/* But many places has it in /usr/bin */
|
||||
execl(LOCAL_ICES, "ices", filename, (char *)NULL);
|
||||
/* As a last-ditch effort, try to use PATH */
|
||||
execlp("ices", "ices", filename, (char *)NULL);
|
||||
|
||||
ast_log(LOG_WARNING, "Execute of ices failed, could not be found.\n");
|
||||
close(fd);
|
||||
_exit(0);
|
||||
ast_log(LOG_WARNING, "Execute of ices failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ices_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
struct ast_module_user *u;
|
||||
struct localuser *u;
|
||||
int fds[2];
|
||||
int ms = -1;
|
||||
int pid = -1;
|
||||
@@ -142,24 +85,20 @@ static int ices_exec(struct ast_channel *chan, void *data)
|
||||
struct ast_frame *f;
|
||||
char filename[256]="";
|
||||
char *c;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
last.tv_usec = 0;
|
||||
last.tv_sec = 0;
|
||||
if (!data || !strlen(data)) {
|
||||
ast_log(LOG_WARNING, "ICES requires an argument (configfile.xml)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
last = ast_tv(0, 0);
|
||||
|
||||
if (pipe(fds)) {
|
||||
ast_log(LOG_WARNING, "Unable to create pipe\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
flags = fcntl(fds[1], F_GETFL);
|
||||
fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
ast_stopstream(chan);
|
||||
|
||||
if (chan->_state != AST_STATE_UP)
|
||||
@@ -169,7 +108,6 @@ static int ices_exec(struct ast_channel *chan, void *data)
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
ast_log(LOG_WARNING, "Answer failed!\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -179,11 +117,10 @@ static int ices_exec(struct ast_channel *chan, void *data)
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
if (((char *)data)[0] == '/')
|
||||
ast_copy_string(filename, (char *) data, sizeof(filename));
|
||||
strncpy(filename, (char *)data, sizeof(filename) - 1);
|
||||
else
|
||||
snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, (char *)data);
|
||||
/* Placeholder for options */
|
||||
@@ -191,6 +128,7 @@ static int ices_exec(struct ast_channel *chan, void *data)
|
||||
if (c)
|
||||
*c = '\0';
|
||||
res = icesencode(filename, fds[0]);
|
||||
close(fds[0]);
|
||||
if (res >= 0) {
|
||||
pid = res;
|
||||
for (;;) {
|
||||
@@ -213,7 +151,6 @@ static int ices_exec(struct ast_channel *chan, void *data)
|
||||
if (errno != EAGAIN) {
|
||||
ast_log(LOG_WARNING, "Write failed to pipe: %s\n", strerror(errno));
|
||||
res = -1;
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -221,33 +158,39 @@ static int ices_exec(struct ast_channel *chan, void *data)
|
||||
ast_frfree(f);
|
||||
}
|
||||
}
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
if (pid > -1)
|
||||
kill(pid, SIGKILL);
|
||||
if (!res && oreadformat)
|
||||
ast_set_read_format(chan, oreadformat);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, ices_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Encode and Stream via icecast and ices");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
142
apps/app_image.c
142
apps/app_image.c
@@ -1,125 +1,89 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* App to transmit an image
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief App to transmit an image
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/image.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/image.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/options.h"
|
||||
static char *tdesc = "Image Transmission Application";
|
||||
|
||||
static char *app = "SendImage";
|
||||
|
||||
static char *synopsis = "Send an image file";
|
||||
|
||||
static char *descrip =
|
||||
" SendImage(filename): Sends an image on a channel. \n"
|
||||
"If the channel supports image transport but the image send\n"
|
||||
"fails, the channel will be hung up. Otherwise, the dialplan\n"
|
||||
"continues execution.\n"
|
||||
"The option string may contain the following character:\n"
|
||||
" 'j' -- jump to priority n+101 if the channel doesn't support image transport\n"
|
||||
"This application sets the following channel variable upon completion:\n"
|
||||
" SENDIMAGESTATUS The status is the result of the attempt as a text string, one of\n"
|
||||
" OK | NOSUPPORT \n";
|
||||
" SendImage(filename): Sends an image on a channel. If the channel\n"
|
||||
"does not support image transport, and there exists a step with\n"
|
||||
"priority n + 101, then execution will continue at that step.\n"
|
||||
"Otherwise, execution will continue at the next priority level.\n"
|
||||
"SendImage only returns 0 if the image was sent correctly or if\n"
|
||||
"the channel does not support image transport, and -1 otherwise.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int sendimage_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_module_user *u;
|
||||
char *parse;
|
||||
int priority_jump = 0;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(filename);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
parse = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (ast_strlen_zero(args.filename)) {
|
||||
ast_log(LOG_WARNING, "SendImage requires an argument (filename[|options])\n");
|
||||
struct localuser *u;
|
||||
if (!data || !strlen((char *)data)) {
|
||||
ast_log(LOG_WARNING, "SendImage requires an argument (filename)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (args.options) {
|
||||
if (strchr(args.options, 'j'))
|
||||
priority_jump = 1;
|
||||
}
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
if (!ast_supports_images(chan)) {
|
||||
/* Does not support transport */
|
||||
if (priority_jump || ast_opt_priority_jumping)
|
||||
ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
|
||||
pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "NOSUPPORT");
|
||||
ast_module_user_remove(u);
|
||||
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
|
||||
chan->priority += 100;
|
||||
return 0;
|
||||
}
|
||||
|
||||
res = ast_send_image(chan, args.filename);
|
||||
|
||||
if (!res)
|
||||
pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "OK");
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
res = ast_send_image(chan, data);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, sendimage_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Image Transmission Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
207
apps/app_intercom.c
Normal file
207
apps/app_intercom.c
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Use /dev/dsp as an intercom.
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/frame.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/soundcard.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <sys/soundcard.h>
|
||||
#else
|
||||
#include <soundcard.h>
|
||||
#endif
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#define DEV_DSP "/dev/audio"
|
||||
#else
|
||||
#define DEV_DSP "/dev/dsp"
|
||||
#endif
|
||||
|
||||
/* Number of 32 byte buffers -- each buffer is 2 ms */
|
||||
#define BUFFER_SIZE 32
|
||||
|
||||
static char *tdesc = "Intercom using /dev/dsp for output";
|
||||
|
||||
static char *app = "Intercom";
|
||||
|
||||
static char *synopsis = "(Obsolete) Send to Intercom";
|
||||
static char *descrip =
|
||||
" Intercom(): Sends the user to the intercom (i.e. /dev/dsp). This program\n"
|
||||
"is generally considered obselete by the chan_oss module. Returns 0 if the\n"
|
||||
"user exits with a DTMF tone, or -1 if they hangup.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
AST_MUTEX_DEFINE_STATIC(sound_lock);
|
||||
static int sound = -1;
|
||||
|
||||
static int write_audio(short *data, int len)
|
||||
{
|
||||
int res;
|
||||
struct audio_buf_info info;
|
||||
ast_mutex_lock(&sound_lock);
|
||||
if (sound < 0) {
|
||||
ast_log(LOG_WARNING, "Sound device closed?\n");
|
||||
ast_mutex_unlock(&sound_lock);
|
||||
return -1;
|
||||
}
|
||||
if (ioctl(sound, SNDCTL_DSP_GETOSPACE, &info)) {
|
||||
ast_log(LOG_WARNING, "Unable to read output space\n");
|
||||
ast_mutex_unlock(&sound_lock);
|
||||
return -1;
|
||||
}
|
||||
res = write(sound, data, len);
|
||||
ast_mutex_unlock(&sound_lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int create_audio(void)
|
||||
{
|
||||
int fmt, desired, res, fd;
|
||||
fd = open(DEV_DSP, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to open %s: %s\n", DEV_DSP, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
fmt = AFMT_S16_LE;
|
||||
res = ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set format to 16-bit signed\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
fmt = 0;
|
||||
res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
/* 8000 Hz desired */
|
||||
desired = 8000;
|
||||
fmt = desired;
|
||||
res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (fmt != desired) {
|
||||
ast_log(LOG_WARNING, "Requested %d Hz, got %d Hz -- sound may be choppy\n", desired, fmt);
|
||||
}
|
||||
#if 1
|
||||
/* 2 bytes * 15 units of 2^5 = 32 bytes per buffer */
|
||||
fmt = ((BUFFER_SIZE) << 16) | (0x0005);
|
||||
res = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fmt);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set fragment size -- sound may be choppy\n");
|
||||
}
|
||||
#endif
|
||||
sound = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intercom_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct localuser *u;
|
||||
struct ast_frame *f;
|
||||
int oreadformat;
|
||||
LOCAL_USER_ADD(u);
|
||||
/* Remember original read format */
|
||||
oreadformat = chan->readformat;
|
||||
/* Set mode to signed linear */
|
||||
res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set format to signed linear on channel %s\n", chan->name);
|
||||
return -1;
|
||||
}
|
||||
/* Read packets from the channel */
|
||||
while(!res) {
|
||||
res = ast_waitfor(chan, -1);
|
||||
if (res > 0) {
|
||||
res = 0;
|
||||
f = ast_read(chan);
|
||||
if (f) {
|
||||
if (f->frametype == AST_FRAME_DTMF) {
|
||||
ast_frfree(f);
|
||||
break;
|
||||
} else {
|
||||
if (f->frametype == AST_FRAME_VOICE) {
|
||||
if (f->subclass == AST_FORMAT_SLINEAR) {
|
||||
res = write_audio(f->data, f->datalen);
|
||||
if (res > 0)
|
||||
res = 0;
|
||||
} else
|
||||
ast_log(LOG_DEBUG, "Unable to handle non-signed linear frame (%d)\n", f->subclass);
|
||||
}
|
||||
}
|
||||
ast_frfree(f);
|
||||
} else
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
LOCAL_USER_REMOVE(u);
|
||||
if (!res)
|
||||
ast_set_read_format(chan, oreadformat);
|
||||
return res;
|
||||
}
|
||||
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
if (sound > -1)
|
||||
close(sound);
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
if (create_audio())
|
||||
return -1;
|
||||
return ast_register_application(app, intercom_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief IVR Demo application
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<defaultenabled>no</defaultenabled>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/app.h"
|
||||
|
||||
static char *tdesc = "IVR Demo Application";
|
||||
static char *app = "IVRDemo";
|
||||
static char *synopsis =
|
||||
" This is a skeleton application that shows you the basic structure to create your\n"
|
||||
"own asterisk applications and demonstrates the IVR demo.\n";
|
||||
|
||||
static int ivr_demo_func(struct ast_channel *chan, void *data)
|
||||
{
|
||||
ast_verbose("IVR Demo, data is %s!\n", (char *)data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_IVR_DECLARE_MENU(ivr_submenu, "IVR Demo Sub Menu", 0,
|
||||
{
|
||||
{ "s", AST_ACTION_BACKGROUND, "demo-abouttotry" },
|
||||
{ "s", AST_ACTION_WAITOPTION },
|
||||
{ "1", AST_ACTION_PLAYBACK, "digits/1" },
|
||||
{ "1", AST_ACTION_PLAYBACK, "digits/1" },
|
||||
{ "1", AST_ACTION_RESTART },
|
||||
{ "2", AST_ACTION_PLAYLIST, "digits/2;digits/3" },
|
||||
{ "3", AST_ACTION_CALLBACK, ivr_demo_func },
|
||||
{ "4", AST_ACTION_TRANSFER, "demo|s|1" },
|
||||
{ "*", AST_ACTION_REPEAT },
|
||||
{ "#", AST_ACTION_UPONE },
|
||||
{ NULL }
|
||||
});
|
||||
|
||||
AST_IVR_DECLARE_MENU(ivr_demo, "IVR Demo Main Menu", 0,
|
||||
{
|
||||
{ "s", AST_ACTION_BACKGROUND, "demo-congrats" },
|
||||
{ "g", AST_ACTION_BACKGROUND, "demo-instruct" },
|
||||
{ "g", AST_ACTION_WAITOPTION },
|
||||
{ "1", AST_ACTION_PLAYBACK, "digits/1" },
|
||||
{ "1", AST_ACTION_RESTART },
|
||||
{ "2", AST_ACTION_MENU, &ivr_submenu },
|
||||
{ "2", AST_ACTION_RESTART },
|
||||
{ "i", AST_ACTION_PLAYBACK, "invalid" },
|
||||
{ "i", AST_ACTION_REPEAT, (void *)(unsigned long)2 },
|
||||
{ "#", AST_ACTION_EXIT },
|
||||
{ NULL },
|
||||
});
|
||||
|
||||
|
||||
static int skel_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
struct ast_module_user *u;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "skel requires an argument (filename)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
/* Do our thing here */
|
||||
|
||||
if (chan->_state != AST_STATE_UP)
|
||||
res = ast_answer(chan);
|
||||
if (!res)
|
||||
res = ast_ivr_menu_run(chan, &ivr_demo, data);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, skel_exec, tdesc, synopsis);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "IVR Demo Application");
|
||||
@@ -1,160 +1,114 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* App to lookup the callerid number, and see if it is blacklisted
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief App to lookup the callerid number, and see if it is blacklisted
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/image.h>
|
||||
#include <asterisk/callerid.h>
|
||||
#include <asterisk/astdb.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/image.h"
|
||||
#include "asterisk/callerid.h"
|
||||
#include "asterisk/astdb.h"
|
||||
#include "asterisk/options.h"
|
||||
static char *tdesc = "Look up Caller*ID name/number from blacklist database";
|
||||
|
||||
static char *app = "LookupBlacklist";
|
||||
|
||||
static char *synopsis = "Look up Caller*ID name/number from blacklist database";
|
||||
|
||||
static char *descrip =
|
||||
" LookupBlacklist(options): Looks up the Caller*ID number on the active\n"
|
||||
"channel in the Asterisk database (family 'blacklist'). \n"
|
||||
"The option string may contain the following character:\n"
|
||||
" 'j' -- jump to n+101 priority if the number/name is found in the blacklist\n"
|
||||
"This application sets the following channel variable upon completion:\n"
|
||||
" LOOKUPBLSTATUS The status of the Blacklist lookup as a text string, one of\n"
|
||||
" FOUND | NOTFOUND\n"
|
||||
"Example: exten => 1234,1,LookupBlacklist()\n\n"
|
||||
"This application is deprecated and may be removed from a future release.\n"
|
||||
"Please use the dialplan function BLACKLIST() instead.\n";
|
||||
" LookupBlacklist: Looks up the Caller*ID number on the active\n"
|
||||
"channel in the Asterisk database (family 'blacklist'). If the\n"
|
||||
"number is found, and if there exists a priority n + 101,\n"
|
||||
"where 'n' is the priority of the current instance, then the\n"
|
||||
"channel will be setup to continue at that priority level.\n"
|
||||
"Otherwise, it returns 0. Does nothing if no Caller*ID was received on the\n"
|
||||
"channel.\n"
|
||||
"Example: database put blacklist <name/number> 1\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
static int blacklist_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
char blacklist[1];
|
||||
int bl = 0;
|
||||
|
||||
if (chan->cid.cid_num) {
|
||||
if (!ast_db_get("blacklist", chan->cid.cid_num, blacklist, sizeof (blacklist)))
|
||||
bl = 1;
|
||||
}
|
||||
if (chan->cid.cid_name) {
|
||||
if (!ast_db_get("blacklist", chan->cid.cid_name, blacklist, sizeof (blacklist)))
|
||||
bl = 1;
|
||||
}
|
||||
|
||||
snprintf(buf, len, "%d", bl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ast_custom_function blacklist_function = {
|
||||
.name = "BLACKLIST",
|
||||
.synopsis = "Check if the callerid is on the blacklist",
|
||||
.desc = "Uses astdb to check if the Caller*ID is in family 'blacklist'. Returns 1 or 0.\n",
|
||||
.syntax = "BLACKLIST()",
|
||||
.read = blacklist_read,
|
||||
};
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int
|
||||
lookupblacklist_exec (struct ast_channel *chan, void *data)
|
||||
{
|
||||
char old_cid[144] = "", *num, *name;
|
||||
char blacklist[1];
|
||||
struct ast_module_user *u;
|
||||
char shrunknum[64] = "";
|
||||
struct localuser *u;
|
||||
int bl = 0;
|
||||
int priority_jump = 0;
|
||||
static int dep_warning = 0;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (!dep_warning) {
|
||||
dep_warning = 1;
|
||||
ast_log(LOG_WARNING, "LookupBlacklist is deprecated. Please use ${BLACKLIST()} instead.\n");
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(data)) {
|
||||
if (strchr(data, 'j'))
|
||||
priority_jump = 1;
|
||||
}
|
||||
|
||||
if (chan->cid.cid_num) {
|
||||
if (!ast_db_get("blacklist", chan->cid.cid_num, blacklist, sizeof (blacklist))) {
|
||||
LOCAL_USER_ADD (u);
|
||||
if (chan->callerid)
|
||||
{
|
||||
strncpy (old_cid, chan->callerid, sizeof (old_cid) - 1);
|
||||
ast_callerid_parse (old_cid, &name, &num);
|
||||
if (num)
|
||||
strncpy (shrunknum, num, sizeof (shrunknum) - 1);
|
||||
else
|
||||
num = shrunknum;
|
||||
|
||||
ast_shrink_phone_number (shrunknum);
|
||||
if (!ast_db_get ("blacklist", shrunknum, blacklist, sizeof (blacklist)))
|
||||
{
|
||||
if (option_verbose > 2)
|
||||
ast_log(LOG_NOTICE, "Blacklisted number %s found\n",chan->cid.cid_num);
|
||||
ast_log(LOG_NOTICE, "Blacklisted number %s found\n",shrunknum);
|
||||
bl = 1;
|
||||
}
|
||||
else if (!ast_db_get ("blacklist", name, blacklist, sizeof (blacklist)))
|
||||
{
|
||||
if (option_verbose > 2)
|
||||
ast_log (LOG_NOTICE,"Blacklisted name \"%s\" found\n",name);
|
||||
bl = 1;
|
||||
}
|
||||
}
|
||||
if (chan->cid.cid_name) {
|
||||
if (!ast_db_get("blacklist", chan->cid.cid_name, blacklist, sizeof (blacklist))) {
|
||||
if (option_verbose > 2)
|
||||
ast_log (LOG_NOTICE,"Blacklisted name \"%s\" found\n",chan->cid.cid_name);
|
||||
bl = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (bl) {
|
||||
if (priority_jump || ast_opt_priority_jumping)
|
||||
ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
|
||||
pbx_builtin_setvar_helper(chan, "LOOKUPBLSTATUS", "FOUND");
|
||||
} else
|
||||
pbx_builtin_setvar_helper(chan, "LOOKUPBLSTATUS", "NOTFOUND");
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
|
||||
if (bl && ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
|
||||
chan->priority+=100;
|
||||
LOCAL_USER_REMOVE (u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module (void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
res |= ast_custom_function_unregister(&blacklist_function);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application (app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module (void)
|
||||
{
|
||||
int res = ast_custom_function_register(&blacklist_function);
|
||||
res |= ast_register_application (app, lookupblacklist_exec, synopsis,descrip);
|
||||
return ast_register_application (app, lookupblacklist_exec, synopsis,descrip);
|
||||
}
|
||||
|
||||
char *description (void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount (void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT (res);
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Look up Caller*ID name/number from blacklist database");
|
||||
char *key ()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,49 +1,31 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* App to set callerid name from database, based on directory number
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief App to set callerid name from database, based on directory number
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/image.h>
|
||||
#include <asterisk/callerid.h>
|
||||
#include <asterisk/astdb.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/image.h"
|
||||
#include "asterisk/callerid.h"
|
||||
#include "asterisk/astdb.h"
|
||||
static char *tdesc = "Look up CallerID Name from local database";
|
||||
|
||||
static char *app = "LookupCIDName";
|
||||
|
||||
@@ -55,49 +37,75 @@ static char *descrip =
|
||||
"Caller*ID name. Does nothing if no Caller*ID was received on the\n"
|
||||
"channel. This is useful if you do not subscribe to Caller*ID\n"
|
||||
"name delivery, or if you want to change the names on some incoming\n"
|
||||
"calls.\n\n"
|
||||
"LookupCIDName is deprecated. Please use ${DB(cidname/${CALLERID(num)})}\n"
|
||||
"instead.\n";
|
||||
"calls. Always returns 0.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
static int lookupcidname_exec (struct ast_channel *chan, void *data)
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int
|
||||
lookupcidname_exec (struct ast_channel *chan, void *data)
|
||||
{
|
||||
char dbname[64];
|
||||
struct ast_module_user *u;
|
||||
static int dep_warning = 0;
|
||||
char old_cid[144] = "", *num, *name;
|
||||
char new_cid[144];
|
||||
char dbname[64];
|
||||
char shrunknum[64] = "";
|
||||
struct localuser *u;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
if (!dep_warning) {
|
||||
dep_warning = 1;
|
||||
ast_log(LOG_WARNING, "LookupCIDName is deprecated. Please use ${DB(cidname/${CALLERID(num)})} instead.\n");
|
||||
LOCAL_USER_ADD (u);
|
||||
if (chan->callerid)
|
||||
{
|
||||
strncpy (old_cid, chan->callerid, sizeof (old_cid) - 1);
|
||||
ast_callerid_parse (old_cid, &name, &num); /* this destroys the original string */
|
||||
if (num) /* It's possible to get an empty number */
|
||||
strncpy (shrunknum, num, sizeof (shrunknum) - 1);
|
||||
else
|
||||
num = shrunknum;
|
||||
ast_shrink_phone_number (shrunknum);
|
||||
if (!ast_db_get ("cidname", shrunknum, dbname, sizeof (dbname)))
|
||||
{
|
||||
snprintf (new_cid, sizeof (new_cid), "\"%s\" <%s>", dbname, num);
|
||||
ast_set_callerid (chan, new_cid, 0);
|
||||
if (option_verbose > 2)
|
||||
ast_verbose (VERBOSE_PREFIX_3 "Changed Caller*ID to %s\n",
|
||||
new_cid);
|
||||
}
|
||||
if (chan->cid.cid_num) {
|
||||
if (!ast_db_get ("cidname", chan->cid.cid_num, dbname, sizeof (dbname))) {
|
||||
ast_set_callerid (chan, NULL, dbname, NULL);
|
||||
if (option_verbose > 2)
|
||||
ast_verbose (VERBOSE_PREFIX_3 "Changed Caller*ID name to %s\n",
|
||||
dbname);
|
||||
}
|
||||
}
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
LOCAL_USER_REMOVE (u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int
|
||||
unload_module (void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application (app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application (app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int
|
||||
load_module (void)
|
||||
{
|
||||
return ast_register_application (app, lookupcidname_exec, synopsis, descrip);
|
||||
return ast_register_application (app, lookupcidname_exec, synopsis,
|
||||
descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Look up CallerID Name from local database");
|
||||
char *
|
||||
description (void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int
|
||||
usecount (void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT (res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *
|
||||
key ()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
738
apps/app_macro.c
738
apps/app_macro.c
@@ -1,615 +1,253 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Macro Implementation
|
||||
*
|
||||
* Copyright (C) 2003, Digium
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Dial plan macro Implementation
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_ARGS 80
|
||||
|
||||
/* special result value used to force macro exit */
|
||||
#define MACRO_EXIT_RESULT 1024
|
||||
static char *tdesc = "Extension Macros";
|
||||
|
||||
static char *descrip =
|
||||
" Macro(macroname|arg1|arg2...): Executes a macro using the context\n"
|
||||
"'macro-<macroname>', jumping to the 's' extension of that context and\n"
|
||||
"executing each step, then returning when the steps end. \n"
|
||||
"The calling extension, context, and priority are stored in ${MACRO_EXTEN}, \n"
|
||||
"executing each step, then returning when the steps end. The calling\n"
|
||||
"extension, context, and priority are stored in ${MACRO_EXTEN}, \n"
|
||||
"${MACRO_CONTEXT} and ${MACRO_PRIORITY} respectively. Arguments become\n"
|
||||
"${ARG1}, ${ARG2}, etc in the macro context.\n"
|
||||
"If you Goto out of the Macro context, the Macro will terminate and control\n"
|
||||
"will be returned at the location of the Goto.\n"
|
||||
"If ${MACRO_OFFSET} is set at termination, Macro will attempt to continue\n"
|
||||
"at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n"
|
||||
"WARNING: Because of the way Macro is implemented (it executes the priorities\n"
|
||||
" contained within it via sub-engine), and a fixed per-thread\n"
|
||||
" memory stack allowance, macros are limited to 7 levels\n"
|
||||
" of nesting (macro calling macro calling macro, etc.); It\n"
|
||||
" may be possible that stack-intensive applications in deeply nested\n"
|
||||
" macros could cause asterisk to crash earlier than this limit.\n"
|
||||
"NOTE: a bug existed in earlier versions of Asterisk that caused Macro not\n"
|
||||
"to reset its context and extension correctly upon exit. This meant that\n"
|
||||
"the 'h' extension within a Macro sometimes would execute, when the dialplan\n"
|
||||
"exited while that Macro was running. However, since this bug has been in\n"
|
||||
"Asterisk for so long, users started to depend upon this behavior. Therefore,\n"
|
||||
"when a channel hangs up when in the midst of executing a Macro, the macro\n"
|
||||
"context will first be checked for an 'h' extension, followed by the main\n"
|
||||
"context from which the Macro was originally called. This behavior in 1.4\n"
|
||||
"exists only for compatibility with earlier versions. You are strongly\n"
|
||||
"encouraged to make use of the 'h' extension only in the context from which\n"
|
||||
"Macro was originally called.\n";
|
||||
|
||||
static char *if_descrip =
|
||||
" MacroIf(<expr>?macroname_a[|arg1][:macroname_b[|arg1]])\n"
|
||||
"Executes macro defined in <macroname_a> if <expr> is true\n"
|
||||
"(otherwise <macroname_b> if provided)\n"
|
||||
"Arguments and return values as in application macro()\n";
|
||||
|
||||
static char *exclusive_descrip =
|
||||
" MacroExclusive(macroname|arg1|arg2...):\n"
|
||||
"Executes macro defined in the context 'macro-macroname'\n"
|
||||
"Only one call at a time may run the macro.\n"
|
||||
"(we'll wait if another call is busy executing in the Macro)\n"
|
||||
"Arguments and return values as in application Macro()\n";
|
||||
|
||||
static char *exit_descrip =
|
||||
" MacroExit():\n"
|
||||
"Causes the currently running macro to exit as if it had\n"
|
||||
"ended normally by running out of priorities to execute.\n"
|
||||
"If used outside a macro, will likely cause unexpected\n"
|
||||
"behavior.\n";
|
||||
"${ARG1}, ${ARG2}, etc in the macro context. Macro returns -1 if\n"
|
||||
"any step in the macro returns -1, and 0 otherwise. If you Goto out\n"
|
||||
"of the Macro context, the Macro will terminate and control will be return\n"
|
||||
"at the location of the Goto. Otherwise if ${MACRO_OFFSET} is set at\n"
|
||||
"termination, Macro will attempt to continue at priority\n"
|
||||
"MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n";
|
||||
|
||||
static char *app = "Macro";
|
||||
static char *if_app = "MacroIf";
|
||||
static char *exclusive_app = "MacroExclusive";
|
||||
static char *exit_app = "MacroExit";
|
||||
|
||||
static char *synopsis = "Macro Implementation";
|
||||
static char *if_synopsis = "Conditional Macro Implementation";
|
||||
static char *exclusive_synopsis = "Exclusive Macro Implementation";
|
||||
static char *exit_synopsis = "Exit From Macro";
|
||||
|
||||
static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
struct ast_datastore_info macro_ds_info = {
|
||||
.type = "MACRO",
|
||||
.chan_fixup = macro_fixup,
|
||||
};
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
|
||||
static int macro_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int i;
|
||||
char varname[10];
|
||||
pbx_builtin_setvar_helper(new_chan, "MACRO_DEPTH", "0");
|
||||
pbx_builtin_setvar_helper(new_chan, "MACRO_CONTEXT", NULL);
|
||||
pbx_builtin_setvar_helper(new_chan, "MACRO_EXTEN", NULL);
|
||||
pbx_builtin_setvar_helper(new_chan, "MACRO_PRIORITY", NULL);
|
||||
pbx_builtin_setvar_helper(new_chan, "MACRO_OFFSET", NULL);
|
||||
for (i = 1; i < 100; i++) {
|
||||
snprintf(varname, sizeof(varname), "ARG%d", i);
|
||||
while (pbx_builtin_getvar_helper(new_chan, varname)) {
|
||||
/* Kill all levels of arguments */
|
||||
pbx_builtin_setvar_helper(new_chan, varname, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
|
||||
{
|
||||
struct ast_exten *e;
|
||||
struct ast_include *i;
|
||||
struct ast_context *c2;
|
||||
|
||||
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 (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
|
||||
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 _macro_exec(struct ast_channel *chan, void *data, int exclusive)
|
||||
{
|
||||
const char *s;
|
||||
char *tmp;
|
||||
char *cur, *rest;
|
||||
char *macro;
|
||||
char fullmacro[80];
|
||||
char varname[80];
|
||||
char runningapp[80], runningdata[1024];
|
||||
char *oldargs[MAX_ARGS + 1] = { NULL, };
|
||||
int argc, x;
|
||||
int res=0;
|
||||
char oldexten[256]="";
|
||||
int oldpriority, gosub_level = 0;
|
||||
char pc[80], depthc[12];
|
||||
char oldcontext[AST_MAX_CONTEXT] = "";
|
||||
const char *inhangupc;
|
||||
int offset, depth = 0, maxdepth = 7;
|
||||
int setmacrocontext=0;
|
||||
int autoloopflag, inhangup = 0;
|
||||
char tmp[256] = "";
|
||||
char *cur, *rest;
|
||||
char *macro;
|
||||
char fullmacro[80];
|
||||
char varname[80];
|
||||
char *oldargs[MAX_ARGS + 1] = { NULL, };
|
||||
int argc, x;
|
||||
int res=0;
|
||||
char oldexten[256]="";
|
||||
int oldpriority;
|
||||
char pc[80];
|
||||
char oldcontext[256] = "";
|
||||
char *offsets;
|
||||
int offset;
|
||||
int setmacrocontext=0;
|
||||
|
||||
char *save_macro_exten;
|
||||
char *save_macro_context;
|
||||
char *save_macro_priority;
|
||||
char *save_macro_offset;
|
||||
struct ast_module_user *u;
|
||||
struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Macro() requires arguments. See \"show application macro\" for help.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
do {
|
||||
if (macro_store) {
|
||||
break;
|
||||
}
|
||||
if (!(macro_store = ast_channel_datastore_alloc(¯o_ds_info, NULL))) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
|
||||
break;
|
||||
}
|
||||
/* Just the existence of this datastore is enough. */
|
||||
macro_store->inheritance = DATASTORE_INHERIT_FOREVER;
|
||||
ast_channel_datastore_add(chan, macro_store);
|
||||
} while (0);
|
||||
|
||||
/* does the user want a deeper rabbit hole? */
|
||||
s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
|
||||
if (s)
|
||||
sscanf(s, "%30d", &maxdepth);
|
||||
|
||||
/* Count how many levels deep the rabbit hole goes */
|
||||
s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
|
||||
if (s)
|
||||
sscanf(s, "%30d", &depth);
|
||||
/* Used for detecting whether to return when a Macro is called from another Macro after hangup */
|
||||
if (strcmp(chan->exten, "h") == 0)
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
|
||||
inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP");
|
||||
if (!ast_strlen_zero(inhangupc))
|
||||
sscanf(inhangupc, "%30d", &inhangup);
|
||||
|
||||
if (depth >= maxdepth) {
|
||||
ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
|
||||
ast_module_user_remove(u);
|
||||
return 0;
|
||||
}
|
||||
snprintf(depthc, sizeof(depthc), "%d", depth + 1);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
|
||||
|
||||
tmp = ast_strdupa(data);
|
||||
rest = tmp;
|
||||
macro = strsep(&rest, "|");
|
||||
if (ast_strlen_zero(macro)) {
|
||||
ast_log(LOG_WARNING, "Invalid macro name specified\n");
|
||||
ast_module_user_remove(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
|
||||
if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
|
||||
if (!ast_context_find(fullmacro))
|
||||
ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
|
||||
else
|
||||
ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
|
||||
ast_module_user_remove(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we are to run the macro exclusively, take the mutex */
|
||||
if (exclusive) {
|
||||
ast_log(LOG_DEBUG, "Locking macrolock for '%s'\n", fullmacro);
|
||||
ast_autoservice_start(chan);
|
||||
if (ast_context_lockmacro(fullmacro)) {
|
||||
ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
|
||||
ast_autoservice_stop(chan);
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
ast_autoservice_stop(chan);
|
||||
}
|
||||
|
||||
/* Save old info */
|
||||
oldpriority = chan->priority;
|
||||
ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
|
||||
ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
|
||||
if (ast_strlen_zero(chan->macrocontext)) {
|
||||
ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
|
||||
ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
|
||||
chan->macropriority = chan->priority;
|
||||
setmacrocontext=1;
|
||||
}
|
||||
argc = 1;
|
||||
/* Save old macro variables */
|
||||
save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
|
||||
|
||||
save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
|
||||
|
||||
save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
|
||||
snprintf(pc, sizeof(pc), "%d", oldpriority);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
|
||||
char *save_macro_exten;
|
||||
char *save_macro_context;
|
||||
char *save_macro_priority;
|
||||
char *save_macro_offset;
|
||||
|
||||
save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
|
||||
if (!data || ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Invalid Macro incantation\n");
|
||||
return 0;
|
||||
}
|
||||
strncpy(tmp, data, sizeof(tmp) - 1);
|
||||
rest = tmp;
|
||||
macro = strsep(&rest, "|");
|
||||
if (!macro || ast_strlen_zero(macro)) {
|
||||
ast_log(LOG_WARNING, "Invalid macro name specified\n");
|
||||
return 0;
|
||||
}
|
||||
snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
|
||||
if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->callerid)) {
|
||||
if (!ast_context_find(fullmacro))
|
||||
ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
|
||||
else
|
||||
ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
|
||||
return 0;
|
||||
}
|
||||
/* Save old info */
|
||||
oldpriority = chan->priority;
|
||||
strncpy(oldexten, chan->exten, sizeof(oldexten) - 1);
|
||||
strncpy(oldcontext, chan->context, sizeof(oldcontext) - 1);
|
||||
if (ast_strlen_zero(chan->macrocontext)) {
|
||||
strncpy(chan->macrocontext, chan->context, sizeof(chan->macrocontext) - 1);
|
||||
strncpy(chan->macroexten, chan->exten, sizeof(chan->macroexten) - 1);
|
||||
chan->macropriority = chan->priority;
|
||||
setmacrocontext=1;
|
||||
}
|
||||
argc = 1;
|
||||
/* Save old macro variables */
|
||||
save_macro_exten = pbx_builtin_getvar_helper(chan, "MACRO_EXTEN");
|
||||
if (save_macro_exten) save_macro_exten = strdup(save_macro_exten);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
|
||||
|
||||
/* Setup environment for new run */
|
||||
chan->exten[0] = 's';
|
||||
chan->exten[1] = '\0';
|
||||
ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
|
||||
chan->priority = 1;
|
||||
save_macro_context = pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT");
|
||||
if (save_macro_context) save_macro_context = strdup(save_macro_context);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
|
||||
|
||||
while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
|
||||
const char *s;
|
||||
/* Save copy of old arguments if we're overwriting some, otherwise
|
||||
let them pass through to the other macro */
|
||||
snprintf(varname, sizeof(varname), "ARG%d", argc);
|
||||
s = pbx_builtin_getvar_helper(chan, varname);
|
||||
if (s)
|
||||
oldargs[argc] = ast_strdup(s);
|
||||
pbx_builtin_setvar_helper(chan, varname, cur);
|
||||
argc++;
|
||||
}
|
||||
autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
|
||||
ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
|
||||
while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
|
||||
struct ast_context *c;
|
||||
struct ast_exten *e;
|
||||
runningapp[0] = '\0';
|
||||
runningdata[0] = '\0';
|
||||
save_macro_priority = pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY");
|
||||
if (save_macro_priority) save_macro_priority = strdup(save_macro_priority);
|
||||
snprintf(pc, sizeof(pc), "%d", oldpriority);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
|
||||
|
||||
save_macro_offset = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET");
|
||||
if (save_macro_offset) save_macro_offset = strdup(save_macro_offset);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
|
||||
|
||||
/* What application will execute? */
|
||||
if (ast_rdlock_contexts()) {
|
||||
ast_log(LOG_WARNING, "Failed to lock contexts list\n");
|
||||
} else {
|
||||
for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
|
||||
if (!strcmp(ast_get_context_name(c), chan->context)) {
|
||||
if (ast_lock_context(c)) {
|
||||
ast_log(LOG_WARNING, "Unable to lock context?\n");
|
||||
} else {
|
||||
e = find_matching_priority(c, chan->exten, chan->priority, chan->cid.cid_num);
|
||||
if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
|
||||
ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
|
||||
ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
|
||||
}
|
||||
ast_unlock_context(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_unlock_contexts();
|
||||
/* Setup environment for new run */
|
||||
chan->exten[0] = 's';
|
||||
chan->exten[1] = '\0';
|
||||
strncpy(chan->context, fullmacro, sizeof(chan->context) - 1);
|
||||
chan->priority = 1;
|
||||
|
||||
/* Reset the macro depth, if it was changed in the last iteration */
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
|
||||
|
||||
if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
|
||||
/* Something bad happened, or a hangup has been requested. */
|
||||
if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
|
||||
(res == '*') || (res == '#')) {
|
||||
/* Just return result as to the previous application as if it had been dialed */
|
||||
ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
|
||||
break;
|
||||
}
|
||||
switch(res) {
|
||||
case MACRO_EXIT_RESULT:
|
||||
res = 0;
|
||||
goto out;
|
||||
case AST_PBX_KEEPALIVE:
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE in macro %s on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
|
||||
else if (option_verbose > 1)
|
||||
ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE in macro '%s' on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
|
||||
goto out;
|
||||
default:
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
|
||||
else if (option_verbose > 1)
|
||||
ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ast_log(LOG_DEBUG, "Executed application: %s\n", runningapp);
|
||||
|
||||
if (!strcasecmp(runningapp, "GOSUB")) {
|
||||
gosub_level++;
|
||||
ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
|
||||
} else if (!strcasecmp(runningapp, "GOSUBIF")) {
|
||||
char tmp2[1024] = "", *cond, *app, *app2 = tmp2;
|
||||
pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
|
||||
cond = strsep(&app2, "?");
|
||||
app = strsep(&app2, ":");
|
||||
if (pbx_checkcondition(cond)) {
|
||||
if (!ast_strlen_zero(app)) {
|
||||
gosub_level++;
|
||||
ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
|
||||
}
|
||||
} else {
|
||||
if (!ast_strlen_zero(app2)) {
|
||||
gosub_level++;
|
||||
ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
|
||||
}
|
||||
}
|
||||
} else if (!strcasecmp(runningapp, "RETURN")) {
|
||||
gosub_level--;
|
||||
ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
|
||||
} else if (!strcasecmp(runningapp, "STACKPOP")) {
|
||||
gosub_level--;
|
||||
ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
|
||||
} else if (!strncasecmp(runningapp, "EXEC", 4)) {
|
||||
/* Must evaluate args to find actual app */
|
||||
char tmp2[1024] = "", *tmp3 = NULL;
|
||||
pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
|
||||
if (!strcasecmp(runningapp, "EXECIF")) {
|
||||
tmp3 = strchr(tmp2, '|');
|
||||
if (tmp3)
|
||||
*tmp3++ = '\0';
|
||||
if (!pbx_checkcondition(tmp2))
|
||||
tmp3 = NULL;
|
||||
} else
|
||||
tmp3 = tmp2;
|
||||
|
||||
if (tmp3)
|
||||
ast_log(LOG_DEBUG, "Last app: %s\n", tmp3);
|
||||
|
||||
if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
|
||||
gosub_level++;
|
||||
ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
|
||||
} else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
|
||||
gosub_level--;
|
||||
ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
|
||||
} else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
|
||||
gosub_level--;
|
||||
ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
|
||||
if (option_verbose > 1)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
|
||||
while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
|
||||
/* Save copy of old arguments if we're overwriting some, otherwise
|
||||
let them pass through to the other macro */
|
||||
snprintf(varname, sizeof(varname), "ARG%d", argc);
|
||||
oldargs[argc] = pbx_builtin_getvar_helper(chan, varname);
|
||||
if (oldargs[argc])
|
||||
oldargs[argc] = strdup(oldargs[argc]);
|
||||
pbx_builtin_setvar_helper(chan, varname, cur);
|
||||
argc++;
|
||||
}
|
||||
while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->callerid)) {
|
||||
if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->callerid))) {
|
||||
/* Something bad happened, or a hangup has been requested. */
|
||||
if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
|
||||
(res == '*') || (res == '#')) {
|
||||
/* Just return result as to the previous application as if it had been dialed */
|
||||
ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
|
||||
break;
|
||||
}
|
||||
|
||||
/* don't stop executing extensions when we're in "h" */
|
||||
if (chan->_softhangup && !inhangup) {
|
||||
ast_log(LOG_DEBUG, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
|
||||
chan->exten, chan->macroexten, chan->priority);
|
||||
switch(res) {
|
||||
case AST_PBX_KEEPALIVE:
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE in macro %s on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
|
||||
else if (option_verbose > 1)
|
||||
ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE in macro '%s' on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
|
||||
goto out;
|
||||
break;
|
||||
default:
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
|
||||
else if (option_verbose > 1)
|
||||
ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
|
||||
goto out;
|
||||
}
|
||||
chan->priority++;
|
||||
}
|
||||
out:
|
||||
|
||||
/* Don't let the channel change now. */
|
||||
ast_channel_lock(chan);
|
||||
|
||||
/* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
|
||||
snprintf(depthc, sizeof(depthc), "%d", depth);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
|
||||
ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
|
||||
|
||||
for (x = 1; x < argc; x++) {
|
||||
/* Restore old arguments and delete ours */
|
||||
snprintf(varname, sizeof(varname), "ARG%d", x);
|
||||
if (oldargs[x]) {
|
||||
pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
|
||||
free(oldargs[x]);
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, varname, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore macro variables */
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
|
||||
if (save_macro_exten)
|
||||
free(save_macro_exten);
|
||||
if (save_macro_context)
|
||||
free(save_macro_context);
|
||||
if (save_macro_priority)
|
||||
free(save_macro_priority);
|
||||
|
||||
if (setmacrocontext) {
|
||||
chan->macrocontext[0] = '\0';
|
||||
chan->macroexten[0] = '\0';
|
||||
chan->macropriority = 0;
|
||||
}
|
||||
|
||||
/*!\note
|
||||
* This section is used to restore a behavior that we mistakenly
|
||||
* changed in issue #6176, then mistakenly reverted in #13962 and
|
||||
* #13363. A corresponding change is made in main/pbx.c, where we
|
||||
* check this variable for existence, then look for the "h" extension
|
||||
* in that context.
|
||||
*/
|
||||
if (ast_check_hangup(chan) || res < 0) {
|
||||
/* Don't need to lock the channel, as we aren't dereferencing emc.
|
||||
* The intent here is to grab the deepest context, without overwriting
|
||||
* in any above context. */
|
||||
const char *emc = pbx_builtin_getvar_helper(chan, "EXIT_MACRO_CONTEXT");
|
||||
if (!emc) {
|
||||
pbx_builtin_setvar_helper(chan, "EXIT_MACRO_CONTEXT", fullmacro);
|
||||
}
|
||||
if (strcasecmp(chan->context, fullmacro)) {
|
||||
if (option_verbose > 1)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
|
||||
break;
|
||||
}
|
||||
/* don't stop executing extensions when we're in "h" */
|
||||
if (chan->_softhangup && strcasecmp(oldexten,"h")) {
|
||||
ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
|
||||
chan->exten, chan->priority);
|
||||
goto out;
|
||||
}
|
||||
chan->priority++;
|
||||
}
|
||||
out:
|
||||
for (x=1;x<argc;x++) {
|
||||
/* Restore old arguments and delete ours */
|
||||
snprintf(varname, sizeof(varname), "ARG%d", x);
|
||||
if (oldargs[x]) {
|
||||
pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
|
||||
free(oldargs[x]);
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, varname, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcasecmp(chan->context, fullmacro)) {
|
||||
const char *offsets;
|
||||
/* Restore macro variables */
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
|
||||
if (save_macro_exten) free(save_macro_exten);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
|
||||
if (save_macro_context) free(save_macro_context);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
|
||||
if (save_macro_priority) free(save_macro_priority);
|
||||
if (setmacrocontext) {
|
||||
chan->macrocontext[0] = '\0';
|
||||
chan->macroexten[0] = '\0';
|
||||
chan->macropriority = 0;
|
||||
}
|
||||
|
||||
/* If we're leaving the macro normally, restore original information */
|
||||
chan->priority = oldpriority;
|
||||
ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
|
||||
ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
|
||||
if (!strcasecmp(chan->context, fullmacro)) {
|
||||
/* If we're leaving the macro normally, restore original information */
|
||||
chan->priority = oldpriority;
|
||||
strncpy(chan->context, oldcontext, sizeof(chan->context) - 1);
|
||||
if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
|
||||
/* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
|
||||
strncpy(chan->exten, oldexten, sizeof(chan->exten) - 1);
|
||||
if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
|
||||
/* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
|
||||
normally if there is any problem */
|
||||
if (sscanf(offsets, "%30d", &offset) == 1) {
|
||||
if (ast_exists_extension(chan, chan->context, chan->exten,
|
||||
chan->priority + offset + 1,
|
||||
chan->cid.cid_num)) {
|
||||
normally if there is any problem */
|
||||
if (sscanf(offsets, "%d", &offset) == 1) {
|
||||
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->callerid)) {
|
||||
chan->priority += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
|
||||
if (save_macro_offset)
|
||||
free(save_macro_offset);
|
||||
|
||||
/* Unlock the macro */
|
||||
if (exclusive) {
|
||||
ast_log(LOG_DEBUG, "Unlocking macrolock for '%s'\n", fullmacro);
|
||||
if (ast_context_unlockmacro(fullmacro)) {
|
||||
ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
|
||||
if (save_macro_offset) free(save_macro_offset);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int macro_exec(struct ast_channel *chan, void *data)
|
||||
int unload_module(void)
|
||||
{
|
||||
return _macro_exec(chan, data, 0);
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int macroexclusive_exec(struct ast_channel *chan, void *data)
|
||||
int load_module(void)
|
||||
{
|
||||
return _macro_exec(chan, data, 1);
|
||||
return ast_register_application(app, macro_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
static int macroif_exec(struct ast_channel *chan, void *data)
|
||||
char *description(void)
|
||||
{
|
||||
char *expr = NULL, *label_a = NULL, *label_b = NULL;
|
||||
int res = 0;
|
||||
struct ast_module_user *u;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (!(expr = ast_strdupa(data))) {
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((label_a = strchr(expr, '?'))) {
|
||||
*label_a = '\0';
|
||||
label_a++;
|
||||
if ((label_b = strchr(label_a, ':'))) {
|
||||
*label_b = '\0';
|
||||
label_b++;
|
||||
}
|
||||
if (pbx_checkcondition(expr))
|
||||
res = macro_exec(chan, label_a);
|
||||
else if (label_b)
|
||||
res = macro_exec(chan, label_b);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Invalid Syntax.\n");
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int macro_exit_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
return MACRO_EXIT_RESULT;
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(if_app);
|
||||
res |= ast_unregister_application(exit_app);
|
||||
res |= ast_unregister_application(app);
|
||||
res |= ast_unregister_application(exclusive_app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
char *key()
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
|
||||
res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
|
||||
res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
|
||||
res |= ast_register_application(app, macro_exec, synopsis, descrip);
|
||||
|
||||
return res;
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");
|
||||
|
||||
5372
apps/app_meetme.c
5372
apps/app_meetme.c
File diff suppressed because it is too large
Load Diff
@@ -1,71 +1,50 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Digital Milliwatt Test
|
||||
*
|
||||
* Copyright (C) 2002, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Digital Milliwatt Test
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>res_indications</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/indications.h"
|
||||
static char *tdesc = "Digital Milliwatt (mu-law) Test Application";
|
||||
|
||||
static char *app = "Milliwatt";
|
||||
|
||||
static char *synopsis = "Generate a Constant 1004Hz tone at 0dbm (mu-law)";
|
||||
static char *synopsis = "Generate a Constant 1000Hz tone at 0dbm (mu-law)";
|
||||
|
||||
static char *descrip =
|
||||
" Milliwatt([options]): Generate a Constant 1004Hz tone at 0dbm.\n"
|
||||
"Previous versions of this application generated the tone at 1000Hz. If for\n"
|
||||
"some reason you would prefer that behavior, supply the 'o' option to get the\n"
|
||||
"old behavior.\n"
|
||||
"";
|
||||
"Milliwatt(): Generate a Constant 1000Hz tone at 0dbm (mu-law)\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static char digital_milliwatt[] = {0x1e,0x0b,0x0b,0x1e,0x9e,0x8b,0x8b,0x9e} ;
|
||||
|
||||
static void *milliwatt_alloc(struct ast_channel *chan, void *params)
|
||||
{
|
||||
return ast_calloc(1, sizeof(int));
|
||||
int *indexp;
|
||||
indexp = malloc(sizeof(int));
|
||||
if (indexp == NULL) return(NULL);
|
||||
*indexp = 0;
|
||||
return(indexp);
|
||||
}
|
||||
|
||||
static void milliwatt_release(struct ast_channel *chan, void *data)
|
||||
@@ -76,115 +55,94 @@ static void milliwatt_release(struct ast_channel *chan, void *data)
|
||||
|
||||
static int milliwatt_generate(struct ast_channel *chan, void *data, int len, int samples)
|
||||
{
|
||||
unsigned char buf[AST_FRIENDLY_OFFSET + 640];
|
||||
const int maxsamples = sizeof (buf) / sizeof (buf[0]);
|
||||
int i, *indexp = (int *) data;
|
||||
struct ast_frame wf = {
|
||||
.frametype = AST_FRAME_VOICE,
|
||||
.subclass = AST_FORMAT_ULAW,
|
||||
.offset = AST_FRIENDLY_OFFSET,
|
||||
.data = buf + AST_FRIENDLY_OFFSET,
|
||||
.src = __FUNCTION__,
|
||||
};
|
||||
struct ast_frame wf;
|
||||
unsigned char waste[AST_FRIENDLY_OFFSET];
|
||||
unsigned char buf[640];
|
||||
int i,*indexp = (int *) data;
|
||||
|
||||
/* Instead of len, use samples, because channel.c generator_force
|
||||
* generate(chan, tmp, 0, 160) ignores len. In any case, len is
|
||||
* a multiple of samples, given by number of samples times bytes per
|
||||
* sample. In the case of ulaw, len = samples. for signed linear
|
||||
* len = 2 * samples */
|
||||
|
||||
if (samples > maxsamples) {
|
||||
ast_log(LOG_WARNING, "Only doing %d samples (%d requested)\n", maxsamples, samples);
|
||||
samples = maxsamples;
|
||||
if (len > sizeof(buf))
|
||||
{
|
||||
ast_log(LOG_WARNING,"Only doing %d bytes (%d bytes requested)\n",(int)sizeof(buf),len);
|
||||
len = sizeof(buf);
|
||||
}
|
||||
|
||||
len = samples * sizeof (buf[0]);
|
||||
waste[0] = 0; /* make compiler happy */
|
||||
wf.frametype = AST_FRAME_VOICE;
|
||||
wf.subclass = AST_FORMAT_ULAW;
|
||||
wf.offset = AST_FRIENDLY_OFFSET;
|
||||
wf.mallocd = 0;
|
||||
wf.data = buf;
|
||||
wf.datalen = len;
|
||||
wf.samples = samples;
|
||||
|
||||
wf.samples = wf.datalen;
|
||||
wf.src = "app_milliwatt";
|
||||
wf.delivery.tv_sec = 0;
|
||||
wf.delivery.tv_usec = 0;
|
||||
/* create a buffer containing the digital milliwatt pattern */
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[AST_FRIENDLY_OFFSET + i] = digital_milliwatt[(*indexp)++];
|
||||
for(i = 0; i < len; i++)
|
||||
{
|
||||
buf[i] = digital_milliwatt[(*indexp)++];
|
||||
*indexp &= 7;
|
||||
}
|
||||
|
||||
if (ast_write(chan,&wf) < 0) {
|
||||
if (ast_write(chan,&wf) < 0)
|
||||
{
|
||||
ast_log(LOG_WARNING,"Failed to write frame to '%s': %s\n",chan->name,strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ast_generator milliwattgen = {
|
||||
static struct ast_generator milliwattgen =
|
||||
{
|
||||
alloc: milliwatt_alloc,
|
||||
release: milliwatt_release,
|
||||
generate: milliwatt_generate,
|
||||
};
|
||||
|
||||
static int old_milliwatt_exec(struct ast_channel *chan)
|
||||
{
|
||||
ast_set_write_format(chan, AST_FORMAT_ULAW);
|
||||
ast_set_read_format(chan, AST_FORMAT_ULAW);
|
||||
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
ast_answer(chan);
|
||||
}
|
||||
|
||||
if (ast_activate_generator(chan,&milliwattgen,"milliwatt") < 0) {
|
||||
ast_log(LOG_WARNING,"Failed to activate generator on '%s'\n",chan->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!ast_safe_sleep(chan, 10000))
|
||||
;
|
||||
|
||||
ast_deactivate_generator(chan);
|
||||
|
||||
return -1;
|
||||
}
|
||||
} ;
|
||||
|
||||
static int milliwatt_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
const char *options = data;
|
||||
struct ast_module_user *u;
|
||||
int res = -1;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (!ast_strlen_zero(options) && strchr(options, 'o')) {
|
||||
res = old_milliwatt_exec(chan);
|
||||
goto exit_app;
|
||||
struct localuser *u;
|
||||
LOCAL_USER_ADD(u);
|
||||
ast_set_write_format(chan, AST_FORMAT_ULAW);
|
||||
ast_set_read_format(chan, AST_FORMAT_ULAW);
|
||||
if (chan->_state != AST_STATE_UP)
|
||||
{
|
||||
ast_answer(chan);
|
||||
}
|
||||
|
||||
res = ast_playtones_start(chan, 23255, "1004/1000", 0);
|
||||
|
||||
while (!res) {
|
||||
res = ast_safe_sleep(chan, 10000);
|
||||
if (ast_activate_generator(chan,&milliwattgen,"milliwatt") < 0)
|
||||
{
|
||||
ast_log(LOG_WARNING,"Failed to activate generator on '%s'\n",chan->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = 0;
|
||||
|
||||
exit_app:
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
while(!ast_safe_sleep(chan, 10000));
|
||||
ast_deactivate_generator(chan);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, milliwatt_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Digital Milliwatt (mu-law) Test Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,622 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2005, Anthony Minessale II
|
||||
* Copyright (C) 2005 - 2006, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
* Kevin P. Fleming <kpfleming@digium.com>
|
||||
*
|
||||
* Based on app_muxmon.c provided by
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief MixMonitor() - Record a call and mix the audio during the recording
|
||||
* \ingroup applications
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
* \author Kevin P. Fleming <kpfleming@digium.com>
|
||||
*
|
||||
* \note Based on app_muxmon.c provided by
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/audiohook.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
#include "asterisk/utils.h"
|
||||
|
||||
#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
|
||||
|
||||
static const char *app = "MixMonitor";
|
||||
static const char *synopsis = "Record a call and mix the audio during the recording";
|
||||
static const char *desc = ""
|
||||
" MixMonitor(<file>.<ext>[|<options>[|<command>]])\n\n"
|
||||
"Records the audio on the current channel to the specified file.\n"
|
||||
"If the filename is an absolute path, uses that path, otherwise\n"
|
||||
"creates the file in the configured monitoring directory from\n"
|
||||
"asterisk.conf. Use of StopMixMonitor is required to guarantee\n"
|
||||
"the audio file is available for processing during dialplan execution.\n\n"
|
||||
"Valid options:\n"
|
||||
" a - Append to the file instead of overwriting it.\n"
|
||||
" b - Only save audio to the file while the channel is bridged.\n"
|
||||
" Note: Does not include conferences or sounds played to each bridged\n"
|
||||
" party.\n"
|
||||
" Note: If you utilize this option inside a Local channel, you must\n"
|
||||
" make sure the Local channel is not optimized away. To do this,\n"
|
||||
" be sure to call your Local channel with the '/n' option.\n"
|
||||
" For example: Dial(Local/start@mycontext/n)\n"
|
||||
" v(<x>) - Adjust the heard volume by a factor of <x> (range -4 to 4)\n"
|
||||
" V(<x>) - Adjust the spoken volume by a factor of <x> (range -4 to 4)\n"
|
||||
" W(<x>) - Adjust the both heard and spoken volumes by a factor of <x>\n"
|
||||
" (range -4 to 4)\n\n"
|
||||
"<command> will be executed when the recording is over\n"
|
||||
"Any strings matching ^{X} will be unescaped to ${X}.\n"
|
||||
"All variables will be evaluated at the time MixMonitor is called.\n"
|
||||
"The variable MIXMONITOR_FILENAME will contain the filename used to record.\n"
|
||||
"";
|
||||
|
||||
static const char *stop_app = "StopMixMonitor";
|
||||
static const char *stop_synopsis = "Stop recording a call through MixMonitor";
|
||||
static const char *stop_desc = ""
|
||||
" StopMixMonitor()\n\n"
|
||||
"Stop recording a call through MixMonitor, and free the recording's file handle.\n"
|
||||
"";
|
||||
|
||||
struct module_symbols *me;
|
||||
|
||||
static const char *mixmonitor_spy_type = "MixMonitor";
|
||||
|
||||
struct mixmonitor {
|
||||
struct ast_audiohook audiohook;
|
||||
char *filename;
|
||||
char *post_process;
|
||||
char *name;
|
||||
unsigned int flags;
|
||||
struct mixmonitor_ds *mixmonitor_ds;
|
||||
};
|
||||
|
||||
enum {
|
||||
MUXFLAG_APPEND = (1 << 1),
|
||||
MUXFLAG_BRIDGED = (1 << 2),
|
||||
MUXFLAG_VOLUME = (1 << 3),
|
||||
MUXFLAG_READVOLUME = (1 << 4),
|
||||
MUXFLAG_WRITEVOLUME = (1 << 5),
|
||||
} mixmonitor_flags;
|
||||
|
||||
enum {
|
||||
OPT_ARG_READVOLUME = 0,
|
||||
OPT_ARG_WRITEVOLUME,
|
||||
OPT_ARG_VOLUME,
|
||||
OPT_ARG_ARRAY_SIZE,
|
||||
} mixmonitor_args;
|
||||
|
||||
AST_APP_OPTIONS(mixmonitor_opts, {
|
||||
AST_APP_OPTION('a', MUXFLAG_APPEND),
|
||||
AST_APP_OPTION('b', MUXFLAG_BRIDGED),
|
||||
AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
|
||||
AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
|
||||
AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
|
||||
});
|
||||
|
||||
/* This structure is used as a means of making sure that our pointer to
|
||||
* the channel we are monitoring remains valid. This is very similar to
|
||||
* what is used in app_chanspy.c.
|
||||
*/
|
||||
struct mixmonitor_ds {
|
||||
struct ast_channel *chan;
|
||||
/* These condition variables are used to be sure that the channel
|
||||
* hangup code completes before the mixmonitor thread attempts to
|
||||
* free this structure. The combination of a bookean flag and a
|
||||
* ast_cond_t ensure that no matter what order the threads run in,
|
||||
* we are guaranteed to never have the waiting thread block forever
|
||||
* in the case that the signaling thread runs first.
|
||||
*/
|
||||
unsigned int destruction_ok;
|
||||
ast_cond_t destruction_condition;
|
||||
ast_mutex_t lock;
|
||||
|
||||
/* The filestream is held in the datastore so it can be stopped
|
||||
* immediately during stop_mixmonitor or channel destruction. */
|
||||
int fs_quit;
|
||||
struct ast_filestream *fs;
|
||||
struct ast_audiohook *audiohook;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \pre mixmonitor_ds must be locked before calling this function
|
||||
*/
|
||||
static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
|
||||
{
|
||||
if (mixmonitor_ds->fs) {
|
||||
ast_closestream(mixmonitor_ds->fs);
|
||||
mixmonitor_ds->fs = NULL;
|
||||
mixmonitor_ds->fs_quit = 1;
|
||||
if (option_verbose > 1)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "MixMonitor close filestream\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void mixmonitor_ds_destroy(void *data)
|
||||
{
|
||||
struct mixmonitor_ds *mixmonitor_ds = data;
|
||||
|
||||
ast_mutex_lock(&mixmonitor_ds->lock);
|
||||
mixmonitor_ds->audiohook = NULL;
|
||||
mixmonitor_ds->chan = NULL;
|
||||
mixmonitor_ds->destruction_ok = 1;
|
||||
ast_cond_signal(&mixmonitor_ds->destruction_condition);
|
||||
ast_mutex_unlock(&mixmonitor_ds->lock);
|
||||
}
|
||||
|
||||
static void mixmonitor_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
|
||||
{
|
||||
struct mixmonitor_ds *mixmonitor_ds = data;
|
||||
|
||||
ast_mutex_lock(&mixmonitor_ds->lock);
|
||||
mixmonitor_ds->chan = new_chan;
|
||||
ast_mutex_unlock(&mixmonitor_ds->lock);
|
||||
}
|
||||
|
||||
static struct ast_datastore_info mixmonitor_ds_info = {
|
||||
.type = "mixmonitor",
|
||||
.destroy = mixmonitor_ds_destroy,
|
||||
.chan_fixup = mixmonitor_ds_chan_fixup,
|
||||
};
|
||||
|
||||
static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
|
||||
{
|
||||
if (mixmonitor->mixmonitor_ds) {
|
||||
ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
|
||||
mixmonitor->mixmonitor_ds->audiohook = NULL;
|
||||
ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
|
||||
}
|
||||
/* kill the audiohook.*/
|
||||
ast_audiohook_lock(&mixmonitor->audiohook);
|
||||
ast_audiohook_detach(&mixmonitor->audiohook);
|
||||
ast_audiohook_unlock(&mixmonitor->audiohook);
|
||||
ast_audiohook_destroy(&mixmonitor->audiohook);
|
||||
}
|
||||
|
||||
static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
|
||||
{
|
||||
struct ast_channel *peer;
|
||||
int res;
|
||||
|
||||
if (!chan)
|
||||
return -1;
|
||||
|
||||
res = ast_audiohook_attach(chan, audiohook);
|
||||
|
||||
if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
|
||||
ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define SAMPLES_PER_FRAME 160
|
||||
|
||||
static void mixmonitor_free(struct mixmonitor *mixmonitor)
|
||||
{
|
||||
if (mixmonitor) {
|
||||
if (mixmonitor->mixmonitor_ds) {
|
||||
ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
|
||||
ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
|
||||
ast_free(mixmonitor->mixmonitor_ds);
|
||||
}
|
||||
ast_free(mixmonitor);
|
||||
}
|
||||
}
|
||||
|
||||
static void *mixmonitor_thread(void *obj)
|
||||
{
|
||||
struct mixmonitor *mixmonitor = obj;
|
||||
struct ast_filestream **fs = NULL;
|
||||
unsigned int oflags;
|
||||
char *ext;
|
||||
int errflag = 0;
|
||||
|
||||
if (option_verbose > 1)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name);
|
||||
|
||||
fs = &mixmonitor->mixmonitor_ds->fs;
|
||||
|
||||
/* The audiohook must enter and exit the loop locked */
|
||||
ast_audiohook_lock(&mixmonitor->audiohook);
|
||||
while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
|
||||
struct ast_frame *fr = NULL;
|
||||
|
||||
if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) {
|
||||
ast_audiohook_trigger_wait(&mixmonitor->audiohook);
|
||||
|
||||
if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* audiohook lock is not required for the next block.
|
||||
* Unlock it, but remember to lock it before looping or exiting */
|
||||
ast_audiohook_unlock(&mixmonitor->audiohook);
|
||||
|
||||
ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
|
||||
if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) {
|
||||
ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
|
||||
/* Initialize the file if not already done so */
|
||||
if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
|
||||
oflags = O_CREAT | O_WRONLY;
|
||||
oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
|
||||
|
||||
if ((ext = strrchr(mixmonitor->filename, '.')))
|
||||
*(ext++) = '\0';
|
||||
else
|
||||
ext = "raw";
|
||||
|
||||
if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) {
|
||||
ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
|
||||
errflag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write out the frame(s) */
|
||||
if (*fs) {
|
||||
struct ast_frame *cur;
|
||||
|
||||
for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
|
||||
ast_writestream(*fs, cur);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
|
||||
}
|
||||
|
||||
/* All done! free it. */
|
||||
ast_frame_free(fr, 0);
|
||||
|
||||
ast_audiohook_lock(&mixmonitor->audiohook);
|
||||
}
|
||||
ast_audiohook_unlock(&mixmonitor->audiohook);
|
||||
|
||||
/* Datastore cleanup. close the filestream and wait for ds destruction */
|
||||
ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
|
||||
mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
|
||||
if (!mixmonitor->mixmonitor_ds->destruction_ok) {
|
||||
ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
|
||||
}
|
||||
ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
|
||||
|
||||
/* kill the audiohook */
|
||||
destroy_monitor_audiohook(mixmonitor);
|
||||
|
||||
if (mixmonitor->post_process) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process);
|
||||
ast_safe_system(mixmonitor->post_process);
|
||||
}
|
||||
|
||||
if (option_verbose > 1)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name);
|
||||
|
||||
mixmonitor_free(mixmonitor);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
|
||||
{
|
||||
struct ast_datastore *datastore = NULL;
|
||||
struct mixmonitor_ds *mixmonitor_ds;
|
||||
|
||||
if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_mutex_init(&mixmonitor_ds->lock);
|
||||
ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
|
||||
|
||||
if (!(datastore = ast_channel_datastore_alloc(&mixmonitor_ds_info, NULL))) {
|
||||
ast_mutex_destroy(&mixmonitor_ds->lock);
|
||||
ast_cond_destroy(&mixmonitor_ds->destruction_condition);
|
||||
ast_free(mixmonitor_ds);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* No need to lock mixmonitor_ds since this is still operating in the channel's thread */
|
||||
mixmonitor_ds->chan = chan;
|
||||
mixmonitor_ds->audiohook = &mixmonitor->audiohook;
|
||||
datastore->data = mixmonitor_ds;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
ast_channel_datastore_add(chan, datastore);
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
mixmonitor->mixmonitor_ds = mixmonitor_ds;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
|
||||
int readvol, int writevol, const char *post_process)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
pthread_t thread;
|
||||
struct mixmonitor *mixmonitor;
|
||||
char postprocess2[1024] = "";
|
||||
size_t len;
|
||||
|
||||
len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
|
||||
|
||||
/* If a post process system command is given attach it to the structure */
|
||||
if (!ast_strlen_zero(post_process)) {
|
||||
char *p1, *p2;
|
||||
|
||||
p1 = ast_strdupa(post_process);
|
||||
for (p2 = p1; *p2 ; p2++) {
|
||||
if (*p2 == '^' && *(p2+1) == '{') {
|
||||
*p2 = '$';
|
||||
}
|
||||
}
|
||||
|
||||
pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
|
||||
if (!ast_strlen_zero(postprocess2))
|
||||
len += strlen(postprocess2) + 1;
|
||||
}
|
||||
|
||||
/* Pre-allocate mixmonitor structure and spy */
|
||||
if (!(mixmonitor = calloc(1, len))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Setup the actual spy before creating our thread */
|
||||
if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
|
||||
mixmonitor_free(mixmonitor);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy over flags and channel name */
|
||||
mixmonitor->flags = flags;
|
||||
if (setup_mixmonitor_ds(mixmonitor, chan)) {
|
||||
mixmonitor_free(mixmonitor);
|
||||
return;
|
||||
}
|
||||
mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
|
||||
strcpy(mixmonitor->name, chan->name);
|
||||
if (!ast_strlen_zero(postprocess2)) {
|
||||
mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
|
||||
strcpy(mixmonitor->post_process, postprocess2);
|
||||
}
|
||||
|
||||
mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
|
||||
strcpy(mixmonitor->filename, filename);
|
||||
|
||||
ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
|
||||
|
||||
if (readvol)
|
||||
mixmonitor->audiohook.options.read_volume = readvol;
|
||||
if (writevol)
|
||||
mixmonitor->audiohook.options.write_volume = writevol;
|
||||
|
||||
if (startmon(chan, &mixmonitor->audiohook)) {
|
||||
ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
|
||||
mixmonitor_spy_type, chan->name);
|
||||
/* Since we couldn't add ourselves - bail out! */
|
||||
ast_audiohook_destroy(&mixmonitor->audiohook);
|
||||
mixmonitor_free(mixmonitor);
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
ast_pthread_create_background(&thread, &attr, mixmonitor_thread, mixmonitor);
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
||||
static int mixmonitor_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int x, readvol = 0, writevol = 0;
|
||||
struct ast_module_user *u;
|
||||
struct ast_flags flags = {0};
|
||||
char *parse;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(filename);
|
||||
AST_APP_ARG(options);
|
||||
AST_APP_ARG(post_process);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
parse = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (ast_strlen_zero(args.filename)) {
|
||||
ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (args.options) {
|
||||
char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
|
||||
|
||||
ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
|
||||
|
||||
if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
|
||||
if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
|
||||
ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
|
||||
} else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
|
||||
ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
|
||||
} else {
|
||||
readvol = get_volfactor(x);
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
|
||||
if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
|
||||
ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
|
||||
} else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
|
||||
ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
|
||||
} else {
|
||||
writevol = get_volfactor(x);
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
|
||||
if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
|
||||
ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
|
||||
} else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
|
||||
ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
|
||||
} else {
|
||||
readvol = writevol = get_volfactor(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if not provided an absolute path, use the system-configured monitoring directory */
|
||||
if (args.filename[0] != '/') {
|
||||
char *build;
|
||||
|
||||
build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
|
||||
sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
|
||||
args.filename = build;
|
||||
}
|
||||
|
||||
pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
|
||||
launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_datastore *datastore = NULL;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
ast_audiohook_detach_source(chan, mixmonitor_spy_type);
|
||||
if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) {
|
||||
struct mixmonitor_ds *mixmonitor_ds = datastore->data;
|
||||
|
||||
ast_mutex_lock(&mixmonitor_ds->lock);
|
||||
|
||||
/* closing the filestream here guarantees the file is avaliable to the dialplan
|
||||
* after calling StopMixMonitor */
|
||||
mixmonitor_ds_close_fs(mixmonitor_ds);
|
||||
|
||||
/* The mixmonitor thread may be waiting on the audiohook trigger.
|
||||
* In order to exit from the mixmonitor loop before waiting on channel
|
||||
* destruction, poke the audiohook trigger. */
|
||||
if (mixmonitor_ds->audiohook) {
|
||||
ast_audiohook_lock(mixmonitor_ds->audiohook);
|
||||
ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
|
||||
ast_audiohook_unlock(mixmonitor_ds->audiohook);
|
||||
mixmonitor_ds->audiohook = NULL;
|
||||
}
|
||||
|
||||
ast_mutex_unlock(&mixmonitor_ds->lock);
|
||||
|
||||
/* Remove the datastore so the monitor thread can exit */
|
||||
if (!ast_channel_datastore_remove(chan, datastore)) {
|
||||
ast_channel_datastore_free(datastore);
|
||||
}
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mixmonitor_cli(int fd, int argc, char **argv)
|
||||
{
|
||||
struct ast_channel *chan;
|
||||
|
||||
if (argc < 3)
|
||||
return RESULT_SHOWUSAGE;
|
||||
|
||||
if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) {
|
||||
ast_cli(fd, "No channel matching '%s' found.\n", argv[2]);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
if (!strcasecmp(argv[1], "start"))
|
||||
mixmonitor_exec(chan, argv[3]);
|
||||
else if (!strcasecmp(argv[1], "stop"))
|
||||
ast_audiohook_detach_source(chan, mixmonitor_spy_type);
|
||||
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static char *complete_mixmonitor_cli(const char *line, const char *word, int pos, int state)
|
||||
{
|
||||
return ast_complete_channels(line, word, pos, state, 2);
|
||||
}
|
||||
|
||||
static struct ast_cli_entry cli_mixmonitor[] = {
|
||||
{ { "mixmonitor", NULL, NULL },
|
||||
mixmonitor_cli, "Execute a MixMonitor command.",
|
||||
"mixmonitor <start|stop> <chan_name> [args]\n\n"
|
||||
"The optional arguments are passed to the\n"
|
||||
"MixMonitor application when the 'start' command is used.\n",
|
||||
complete_mixmonitor_cli },
|
||||
};
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
|
||||
res = ast_unregister_application(stop_app);
|
||||
res |= ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry));
|
||||
res = ast_register_application(app, mixmonitor_exec, synopsis, desc);
|
||||
res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");
|
||||
@@ -1,179 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (c) 2006, Tilghman Lesher. All rights reserved.
|
||||
*
|
||||
* Tilghman Lesher <app_morsecode__v001@the-tilghman.com>
|
||||
*
|
||||
* This code is released by the author with no restrictions on usage.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Morsecode application
|
||||
*
|
||||
* \author Tilghman Lesher <app_morsecode__v001@the-tilghman.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/indications.h"
|
||||
|
||||
static char *app_morsecode = "Morsecode";
|
||||
|
||||
static char *morsecode_synopsis = "Plays morse code";
|
||||
|
||||
static char *morsecode_descrip =
|
||||
"Usage: Morsecode(<string>)\n"
|
||||
"Plays the Morse code equivalent of the passed string. If the variable\n"
|
||||
"MORSEDITLEN is set, it will use that value for the length (in ms) of the dit\n"
|
||||
"(defaults to 80). Additionally, if MORSETONE is set, it will use that tone\n"
|
||||
"(in Hz). The tone default is 800.\n";
|
||||
|
||||
|
||||
static char *morsecode[] = {
|
||||
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 0-15 */
|
||||
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 16-31 */
|
||||
" ", /* 32 - <space> */
|
||||
".-.-.-", /* 33 - ! */
|
||||
".-..-.", /* 34 - " */
|
||||
"", /* 35 - # */
|
||||
"", /* 36 - $ */
|
||||
"", /* 37 - % */
|
||||
"", /* 38 - & */
|
||||
".----.", /* 39 - ' */
|
||||
"-.--.-", /* 40 - ( */
|
||||
"-.--.-", /* 41 - ) */
|
||||
"", /* 42 - * */
|
||||
"", /* 43 - + */
|
||||
"--..--", /* 44 - , */
|
||||
"-....-", /* 45 - - */
|
||||
".-.-.-", /* 46 - . */
|
||||
"-..-.", /* 47 - / */
|
||||
"-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", "---..", "----.", /* 48-57 - 0-9 */
|
||||
"---...", /* 58 - : */
|
||||
"-.-.-.", /* 59 - ; */
|
||||
"", /* 60 - < */
|
||||
"-...-", /* 61 - = */
|
||||
"", /* 62 - > */
|
||||
"..--..", /* 63 - ? */
|
||||
".--.-.", /* 64 - @ */
|
||||
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--",
|
||||
"-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..",
|
||||
"-.--.-", /* 91 - [ (really '(') */
|
||||
"-..-.", /* 92 - \ (really '/') */
|
||||
"-.--.-", /* 93 - ] (really ')') */
|
||||
"", /* 94 - ^ */
|
||||
"..--.-", /* 95 - _ */
|
||||
".----.", /* 96 - ` */
|
||||
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--",
|
||||
"-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..",
|
||||
"-.--.-", /* 123 - { (really '(') */
|
||||
"", /* 124 - | */
|
||||
"-.--.-", /* 125 - } (really ')') */
|
||||
"-..-.", /* 126 - ~ (really bar) */
|
||||
". . .", /* 127 - <del> (error) */
|
||||
};
|
||||
|
||||
static void playtone(struct ast_channel *chan, int tone, int len)
|
||||
{
|
||||
char dtmf[20];
|
||||
snprintf(dtmf, sizeof(dtmf), "%d/%d", tone, len);
|
||||
ast_playtones_start(chan, 0, dtmf, 0);
|
||||
ast_safe_sleep(chan, len);
|
||||
ast_playtones_stop(chan);
|
||||
}
|
||||
|
||||
static int morsecode_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0, ditlen, tone;
|
||||
char *digit;
|
||||
const char *ditlenc, *tonec;
|
||||
struct ast_module_user *u;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Syntax: Morsecode(<string>) - no argument found\n");
|
||||
ast_module_user_remove(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Use variable MORESEDITLEN, if set (else 80) */
|
||||
ditlenc = pbx_builtin_getvar_helper(chan, "MORSEDITLEN");
|
||||
if (ast_strlen_zero(ditlenc) || (sscanf(ditlenc, "%30d", &ditlen) != 1)) {
|
||||
ditlen = 80;
|
||||
}
|
||||
|
||||
/* Use variable MORSETONE, if set (else 800) */
|
||||
tonec = pbx_builtin_getvar_helper(chan, "MORSETONE");
|
||||
if (ast_strlen_zero(tonec) || (sscanf(tonec, "%30d", &tone) != 1)) {
|
||||
tone = 800;
|
||||
}
|
||||
|
||||
for (digit = data; *digit; digit++) {
|
||||
int digit2 = *digit;
|
||||
char *dahdit;
|
||||
if (digit2 < 0) {
|
||||
continue;
|
||||
}
|
||||
for (dahdit = morsecode[digit2]; *dahdit; dahdit++) {
|
||||
if (*dahdit == '-') {
|
||||
playtone(chan, tone, 3 * ditlen);
|
||||
} else if (*dahdit == '.') {
|
||||
playtone(chan, tone, 1 * ditlen);
|
||||
} else {
|
||||
/* Account for ditlen of silence immediately following */
|
||||
playtone(chan, 0, 2 * ditlen);
|
||||
}
|
||||
|
||||
/* Pause slightly between each dit and dah */
|
||||
playtone(chan, 0, 1 * ditlen);
|
||||
}
|
||||
/* Pause between characters */
|
||||
playtone(chan, 0, 2 * ditlen);
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app_morsecode);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application(app_morsecode, morsecode_exec, morsecode_synopsis, morsecode_descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Morse code");
|
||||
180
apps/app_mp3.c
180
apps/app_mp3.c
@@ -1,38 +1,24 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Silly application to play an MP3 file -- uses mpg123
|
||||
*
|
||||
* Copyright (C) 1999-2004, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Silly application to play an MP3 file -- uses mpg123
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>working_fork</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/frame.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
@@ -40,69 +26,38 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#ifdef HAVE_CAP
|
||||
#include <sys/capability.h>
|
||||
#endif /* HAVE_CAP */
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/frame.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/options.h"
|
||||
|
||||
#define LOCAL_MPG_123 "/usr/local/bin/mpg123"
|
||||
#define MPG_123 "/usr/bin/mpg123"
|
||||
|
||||
static char *tdesc = "Silly MP3 Application";
|
||||
|
||||
static char *app = "MP3Player";
|
||||
|
||||
static char *synopsis = "Play an MP3 file or stream";
|
||||
|
||||
static char *descrip =
|
||||
" MP3Player(location) Executes mpg123 to play the given location,\n"
|
||||
"which typically would be a filename or a URL. User can exit by pressing\n"
|
||||
"any key on the dialpad, or by hanging up.";
|
||||
" MP3Player(location) Executes mpg123 to play the given location\n"
|
||||
"which typically would be a filename or a URL. Returns -1 on\n"
|
||||
"hangup or 0 otherwise. User can exit by pressing any key\n.";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int mp3play(char *filename, int fd)
|
||||
{
|
||||
int res;
|
||||
int x;
|
||||
sigset_t fullset, oldset;
|
||||
#ifdef HAVE_CAP
|
||||
cap_t cap;
|
||||
#endif
|
||||
|
||||
sigfillset(&fullset);
|
||||
pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
|
||||
|
||||
res = fork();
|
||||
if (res < 0)
|
||||
ast_log(LOG_WARNING, "Fork failed\n");
|
||||
if (res) {
|
||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
#ifdef HAVE_CAP
|
||||
cap = cap_from_text("cap_net_admin-eip");
|
||||
|
||||
if (cap_set_proc(cap)) {
|
||||
/* Careful with order! Logging cannot happen after we close FDs */
|
||||
ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
|
||||
}
|
||||
cap_free(cap);
|
||||
#endif
|
||||
if (ast_opt_high_priority)
|
||||
ast_set_priority(0);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
|
||||
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
for (x=STDERR_FILENO + 1;x<256;x++) {
|
||||
close(x);
|
||||
for (x=0;x<256;x++) {
|
||||
if (x != STDOUT_FILENO)
|
||||
close(x);
|
||||
}
|
||||
/* Execute mpg123, but buffer if it's a net connection */
|
||||
if (!strncasecmp(filename, "http://", 7)) {
|
||||
@@ -122,7 +77,7 @@ static int mp3play(char *filename, int fd)
|
||||
execlp("mpg123", "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
|
||||
}
|
||||
ast_log(LOG_WARNING, "Execute of mpg123 failed\n");
|
||||
_exit(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int timed_read(int fd, void *data, int datalen, int timeout)
|
||||
@@ -131,7 +86,7 @@ static int timed_read(int fd, void *data, int datalen, int timeout)
|
||||
struct pollfd fds[1];
|
||||
fds[0].fd = fd;
|
||||
fds[0].events = POLLIN;
|
||||
res = ast_poll(fds, 1, timeout);
|
||||
res = poll(fds, 1, timeout);
|
||||
if (res < 1) {
|
||||
ast_log(LOG_NOTICE, "Poll timed out/errored out with %d\n", res);
|
||||
return -1;
|
||||
@@ -143,59 +98,66 @@ static int timed_read(int fd, void *data, int datalen, int timeout)
|
||||
static int mp3_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
struct ast_module_user *u;
|
||||
struct localuser *u;
|
||||
int fds[2];
|
||||
int ms = -1;
|
||||
int pid = -1;
|
||||
int owriteformat;
|
||||
int timeout = 2000;
|
||||
struct timeval next;
|
||||
struct timeval now, next;
|
||||
struct ast_frame *f;
|
||||
struct myframe {
|
||||
struct ast_frame f;
|
||||
char offset[AST_FRIENDLY_OFFSET];
|
||||
short frdata[160];
|
||||
} myf = {
|
||||
.f = { 0, },
|
||||
};
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
} myf;
|
||||
if (!data) {
|
||||
ast_log(LOG_WARNING, "MP3 Playback requires an argument (filename)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (pipe(fds)) {
|
||||
ast_log(LOG_WARNING, "Unable to create pipe\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
ast_stopstream(chan);
|
||||
|
||||
owriteformat = chan->writeformat;
|
||||
res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
res = mp3play((char *)data, fds[1]);
|
||||
if (!strncasecmp((char *)data, "http://", 7)) {
|
||||
timeout = 10000;
|
||||
}
|
||||
/* Wait 1000 ms first */
|
||||
next = ast_tvnow();
|
||||
next = now;
|
||||
next.tv_sec += 1;
|
||||
if (res >= 0) {
|
||||
pid = res;
|
||||
/* Order is important -- there's almost always going to be mp3... we want to prioritize the
|
||||
user */
|
||||
for (;;) {
|
||||
ms = ast_tvdiff_ms(next, ast_tvnow());
|
||||
gettimeofday(&now, NULL);
|
||||
ms = (next.tv_sec - now.tv_sec) * 1000;
|
||||
ms += (next.tv_usec - now.tv_usec) / 1000;
|
||||
#if 0
|
||||
printf("ms: %d\n", ms);
|
||||
#endif
|
||||
if (ms <= 0) {
|
||||
#if 0
|
||||
{
|
||||
static struct timeval last;
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
printf("Since last: %ld\n", (tv.tv_sec - last.tv_sec) * 1000 + (tv.tv_usec - last.tv_usec) / 1000);
|
||||
last = tv;
|
||||
}
|
||||
#endif
|
||||
res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata), timeout);
|
||||
if (res > 0) {
|
||||
myf.f.frametype = AST_FRAME_VOICE;
|
||||
@@ -217,7 +179,14 @@ static int mp3_exec(struct ast_channel *chan, void *data)
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
|
||||
next.tv_usec += res / 2 * 125;
|
||||
if (next.tv_usec >= 1000000) {
|
||||
next.tv_usec -= 1000000;
|
||||
next.tv_sec++;
|
||||
}
|
||||
#if 0
|
||||
printf("Next: %d\n", ms);
|
||||
#endif
|
||||
} else {
|
||||
ms = ast_waitfor(chan, ms);
|
||||
if (ms < 0) {
|
||||
@@ -245,31 +214,38 @@ static int mp3_exec(struct ast_channel *chan, void *data)
|
||||
}
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
if (pid > -1)
|
||||
kill(pid, SIGKILL);
|
||||
if (!res && owriteformat)
|
||||
ast_set_write_format(chan, owriteformat);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, mp3_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly MP3 Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,38 +1,24 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Silly application to play an NBScat file -- uses nbscat8k
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Silly application to play an NBScat file -- uses nbscat8k
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>working_fork</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/frame.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
@@ -41,26 +27,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#ifdef HAVE_CAP
|
||||
#include <sys/capability.h>
|
||||
#endif /* HAVE_CAP */
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/frame.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/options.h"
|
||||
|
||||
#define LOCAL_NBSCAT "/usr/local/bin/nbscat8k"
|
||||
#define NBSCAT "/usr/bin/nbscat8k"
|
||||
|
||||
#ifndef AF_LOCAL
|
||||
#define AF_LOCAL AF_UNIX
|
||||
#endif
|
||||
static char *tdesc = "Silly NBS Stream Application";
|
||||
|
||||
static char *app = "NBScat";
|
||||
|
||||
@@ -68,45 +39,24 @@ static char *synopsis = "Play an NBS local stream";
|
||||
|
||||
static char *descrip =
|
||||
" NBScat: Executes nbscat to listen to the local NBS stream.\n"
|
||||
"User can exit by pressing any key\n.";
|
||||
"Returns -1 on\n hangup or 0 otherwise. User can exit by \n"
|
||||
"pressing any key\n.";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int NBScatplay(int fd)
|
||||
{
|
||||
int res;
|
||||
int x;
|
||||
sigset_t fullset, oldset;
|
||||
#ifdef HAVE_CAP
|
||||
cap_t cap;
|
||||
#endif
|
||||
|
||||
sigfillset(&fullset);
|
||||
pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
|
||||
|
||||
res = fork();
|
||||
if (res < 0)
|
||||
ast_log(LOG_WARNING, "Fork failed\n");
|
||||
if (res) {
|
||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
|
||||
|
||||
#ifdef HAVE_CAP
|
||||
cap = cap_from_text("cap_net_admin-eip");
|
||||
|
||||
if (cap_set_proc(cap)) {
|
||||
/* Careful with order! Logging cannot happen after we close FDs */
|
||||
ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
|
||||
}
|
||||
cap_free(cap);
|
||||
#endif
|
||||
if (ast_opt_high_priority)
|
||||
ast_set_priority(0);
|
||||
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
for (x = STDERR_FILENO + 1; x < 1024; x++) {
|
||||
for (x=0;x<256;x++) {
|
||||
if (x != STDOUT_FILENO)
|
||||
close(x);
|
||||
}
|
||||
@@ -114,7 +64,7 @@ static int NBScatplay(int fd)
|
||||
execl(NBSCAT, "nbscat8k", "-d", (char *)NULL);
|
||||
execl(LOCAL_NBSCAT, "nbscat8k", "-d", (char *)NULL);
|
||||
ast_log(LOG_WARNING, "Execute of nbscat8k failed\n");
|
||||
_exit(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int timed_read(int fd, void *data, int datalen)
|
||||
@@ -123,7 +73,7 @@ static int timed_read(int fd, void *data, int datalen)
|
||||
struct pollfd fds[1];
|
||||
fds[0].fd = fd;
|
||||
fds[0].events = POLLIN;
|
||||
res = ast_poll(fds, 1, 2000);
|
||||
res = poll(fds, 1, 2000);
|
||||
if (res < 1) {
|
||||
ast_log(LOG_NOTICE, "Selected timed out/errored out with %d\n", res);
|
||||
return -1;
|
||||
@@ -135,48 +85,57 @@ static int timed_read(int fd, void *data, int datalen)
|
||||
static int NBScat_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
struct ast_module_user *u;
|
||||
struct localuser *u;
|
||||
int fds[2];
|
||||
int ms = -1;
|
||||
int pid = -1;
|
||||
int owriteformat;
|
||||
struct timeval next;
|
||||
struct timeval now, next;
|
||||
struct ast_frame *f;
|
||||
struct myframe {
|
||||
struct ast_frame f;
|
||||
char offset[AST_FRIENDLY_OFFSET];
|
||||
short frdata[160];
|
||||
} myf;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds)) {
|
||||
ast_log(LOG_WARNING, "Unable to create socketpair\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
ast_stopstream(chan);
|
||||
|
||||
owriteformat = chan->writeformat;
|
||||
res = ast_set_write_format(chan, AST_FORMAT_SLINEAR);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = NBScatplay(fds[1]);
|
||||
/* Wait 1000 ms first */
|
||||
next = ast_tvnow();
|
||||
next = now;
|
||||
next.tv_sec += 1;
|
||||
if (res >= 0) {
|
||||
pid = res;
|
||||
/* Order is important -- there's almost always going to be mp3... we want to prioritize the
|
||||
user */
|
||||
for (;;) {
|
||||
ms = ast_tvdiff_ms(next, ast_tvnow());
|
||||
gettimeofday(&now, NULL);
|
||||
ms = (next.tv_sec - now.tv_sec) * 1000;
|
||||
ms += (next.tv_usec - now.tv_usec) / 1000;
|
||||
#if 0
|
||||
printf("ms: %d\n", ms);
|
||||
#endif
|
||||
if (ms <= 0) {
|
||||
#if 0
|
||||
{
|
||||
static struct timeval last;
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
printf("Since last: %ld\n", (tv.tv_sec - last.tv_sec) * 1000 + (tv.tv_usec - last.tv_usec) / 1000);
|
||||
last = tv;
|
||||
}
|
||||
#endif
|
||||
res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata));
|
||||
if (res > 0) {
|
||||
myf.f.frametype = AST_FRAME_VOICE;
|
||||
@@ -198,7 +157,14 @@ static int NBScat_exec(struct ast_channel *chan, void *data)
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
|
||||
next.tv_usec += res / 2 * 125;
|
||||
if (next.tv_usec >= 1000000) {
|
||||
next.tv_usec -= 1000000;
|
||||
next.tv_sec++;
|
||||
}
|
||||
#if 0
|
||||
printf("Next: %d\n", ms);
|
||||
#endif
|
||||
} else {
|
||||
ms = ast_waitfor(chan, ms);
|
||||
if (ms < 0) {
|
||||
@@ -226,31 +192,38 @@ static int NBScat_exec(struct ast_channel *chan, void *data)
|
||||
}
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
if (pid > -1)
|
||||
kill(pid, SIGKILL);
|
||||
if (!res && owriteformat)
|
||||
ast_set_write_format(chan, owriteformat);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, NBScat_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly NBS Stream Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
1876
apps/app_osplookup.c
1876
apps/app_osplookup.c
File diff suppressed because it is too large
Load Diff
220
apps/app_page.c
220
apps/app_page.c
@@ -1,220 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (c) 2004 - 2006 Digium, Inc. All rights reserved.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* This code is released under the GNU General Public License
|
||||
* version 2.0. See LICENSE for more information.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief page() - Paging application
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>dahdi</depend>
|
||||
<depend>app_meetme</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/chanvars.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/dial.h"
|
||||
#include "asterisk/devicestate.h"
|
||||
|
||||
static const char *app_page= "Page";
|
||||
|
||||
static const char *page_synopsis = "Pages phones";
|
||||
|
||||
static const char *page_descrip =
|
||||
"Page(Technology/Resource&Technology2/Resource2[|options])\n"
|
||||
" Places outbound calls to the given technology / resource and dumps\n"
|
||||
"them into a conference bridge as muted participants. The original\n"
|
||||
"caller is dumped into the conference as a speaker and the room is\n"
|
||||
"destroyed when the original caller leaves. Valid options are:\n"
|
||||
" d - full duplex audio\n"
|
||||
" q - quiet, do not play beep to caller\n"
|
||||
" r - record the page into a file (see 'r' for app_meetme)\n";
|
||||
|
||||
enum {
|
||||
PAGE_DUPLEX = (1 << 0),
|
||||
PAGE_QUIET = (1 << 1),
|
||||
PAGE_RECORD = (1 << 2),
|
||||
} page_opt_flags;
|
||||
|
||||
AST_APP_OPTIONS(page_opts, {
|
||||
AST_APP_OPTION('d', PAGE_DUPLEX),
|
||||
AST_APP_OPTION('q', PAGE_QUIET),
|
||||
AST_APP_OPTION('r', PAGE_RECORD),
|
||||
});
|
||||
|
||||
|
||||
static int page_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_module_user *u;
|
||||
char *options, *tech, *resource, *tmp, *tmp2;
|
||||
char meetmeopts[88], originator[AST_CHANNEL_NAME];
|
||||
struct ast_flags flags = { 0 };
|
||||
unsigned int confid = ast_random();
|
||||
struct ast_app *app;
|
||||
int res = 0, pos = 0, i = 0;
|
||||
struct ast_dial **dial_list;
|
||||
unsigned int num_dials;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "This application requires at least one argument (destination(s) to page)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (!(app = pbx_findapp("MeetMe"))) {
|
||||
ast_log(LOG_WARNING, "There is no MeetMe application available!\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
};
|
||||
|
||||
options = ast_strdupa(data);
|
||||
|
||||
ast_copy_string(originator, chan->name, sizeof(originator));
|
||||
if ((tmp = strchr(originator, '-')))
|
||||
*tmp = '\0';
|
||||
|
||||
tmp = strsep(&options, "|");
|
||||
if (options)
|
||||
ast_app_parse_options(page_opts, &flags, NULL, options);
|
||||
|
||||
snprintf(meetmeopts, sizeof(meetmeopts), "MeetMe|%ud|%s%sqxdw(5)", confid, (ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "m"),
|
||||
(ast_test_flag(&flags, PAGE_RECORD) ? "r" : "") );
|
||||
|
||||
/* Count number of extensions in list by number of ampersands + 1 */
|
||||
num_dials = 1;
|
||||
tmp2 = tmp;
|
||||
while (*tmp2) {
|
||||
if (*tmp2 == '&') {
|
||||
num_dials++;
|
||||
}
|
||||
tmp2++;
|
||||
}
|
||||
|
||||
if (!(dial_list = ast_calloc(num_dials, sizeof(struct ast_dial *)))) {
|
||||
ast_log(LOG_ERROR, "Can't allocate %ld bytes for dial list\n", (long)(sizeof(struct ast_dial *) * num_dials));
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Go through parsing/calling each device */
|
||||
while ((tech = strsep(&tmp, "&"))) {
|
||||
struct ast_dial *dial = NULL;
|
||||
|
||||
/* don't call the originating device */
|
||||
if (!strcasecmp(tech, originator))
|
||||
continue;
|
||||
|
||||
/* If no resource is available, continue on */
|
||||
if (!(resource = strchr(tech, '/'))) {
|
||||
ast_log(LOG_WARNING, "Incomplete destination '%s' supplied.\n", tech);
|
||||
continue;
|
||||
}
|
||||
|
||||
*resource++ = '\0';
|
||||
|
||||
/* Create a dialing structure */
|
||||
if (!(dial = ast_dial_create())) {
|
||||
ast_log(LOG_WARNING, "Failed to create dialing structure.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Append technology and resource */
|
||||
ast_dial_append(dial, tech, resource);
|
||||
|
||||
/* Set ANSWER_EXEC as global option */
|
||||
ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, meetmeopts);
|
||||
|
||||
/* Run this dial in async mode */
|
||||
ast_dial_run(dial, chan, 1);
|
||||
|
||||
/* Put in our dialing array */
|
||||
dial_list[pos++] = dial;
|
||||
}
|
||||
|
||||
if (!ast_test_flag(&flags, PAGE_QUIET)) {
|
||||
res = ast_streamfile(chan, "beep", chan->language);
|
||||
if (!res)
|
||||
res = ast_waitstream(chan, "");
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
snprintf(meetmeopts, sizeof(meetmeopts), "%ud|A%s%sqxd", confid, (ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "t"),
|
||||
(ast_test_flag(&flags, PAGE_RECORD) ? "r" : "") );
|
||||
pbx_exec(chan, app, meetmeopts);
|
||||
}
|
||||
|
||||
/* Go through each dial attempt cancelling, joining, and destroying */
|
||||
for (i = 0; i < pos; i++) {
|
||||
struct ast_dial *dial = dial_list[i];
|
||||
|
||||
/* We have to wait for the async thread to exit as it's possible Meetme won't throw them out immediately */
|
||||
ast_dial_join(dial);
|
||||
|
||||
/* Hangup all channels */
|
||||
ast_dial_hangup(dial);
|
||||
|
||||
/* Destroy dialing structure */
|
||||
ast_dial_destroy(dial);
|
||||
}
|
||||
|
||||
ast_free(dial_list);
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app_page);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application(app_page, page_exec, page_synopsis, page_descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Page Multiple Phones");
|
||||
|
||||
@@ -1,55 +1,38 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2006, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* ParkAndAnnounce application for Asterisk
|
||||
* Author: Ben Miller <bgmiller@dccinc.com>
|
||||
* With TONS of help from Mark!
|
||||
*
|
||||
* Asterisk is Copyrighted as follows
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* 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.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief ParkAndAnnounce application for Asterisk
|
||||
*
|
||||
* \author Ben Miller <bgmiller@dccinc.com>
|
||||
* \arg With TONS of help from Mark!
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#include <sys/types.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/channel_pvt.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/features.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/say.h>
|
||||
#include <asterisk/lock.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/features.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/say.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/utils.h"
|
||||
static char *tdesc = "Call Parking and Announce Application";
|
||||
|
||||
static char *app = "ParkAndAnnounce";
|
||||
|
||||
@@ -57,53 +40,51 @@ static char *synopsis = "Park and Announce";
|
||||
|
||||
static char *descrip =
|
||||
" ParkAndAnnounce(announce:template|timeout|dial|[return_context]):\n"
|
||||
"Park a call into the parkinglot and announce the call to another channel.\n"
|
||||
"\n"
|
||||
"announce template: Colon-separated list of files to announce. The word PARKED\n"
|
||||
" will be replaced by a say_digits of the extension in which\n"
|
||||
" the call is parked.\n"
|
||||
"timeout: Time in seconds before the call returns into the return\n"
|
||||
" context.\n"
|
||||
"dial: The app_dial style resource to call to make the\n"
|
||||
" announcement. Console/dsp calls the console.\n"
|
||||
"return_context: The goto-style label to jump the call back into after\n"
|
||||
" timeout. Default <priority+1>.\n"
|
||||
"\n"
|
||||
"The variable ${PARKEDAT} will contain the parking extension into which the\n"
|
||||
"call was placed. Use with the Local channel to allow the dialplan to make\n"
|
||||
"use of this information.\n";
|
||||
"Park a call into the parkinglot and announce the call over the console.\n"
|
||||
"announce template: colon separated list of files to announce, the word PARKED\n"
|
||||
" will be replaced by a say_digits of the ext the call is parked in\n"
|
||||
"timeout: time in seconds before the call returns into the return context.\n"
|
||||
"dial: The app_dial style resource to call to make the announcement. Console/dsp calls the console.\n"
|
||||
"return_context: the goto style label to jump the call back into after timeout. default=prio+1\n";
|
||||
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int parkandannounce_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
char *return_context;
|
||||
int lot, timeout = 0, dres;
|
||||
int l, lot, timeout = 0, dres;
|
||||
char *working, *context, *exten, *priority, *dial, *dialtech, *dialstr;
|
||||
char *template, *tpl_working, *tpl_current;
|
||||
char *tmp[100];
|
||||
char buf[13];
|
||||
int looptemp = 0,i = 0, res = 0;
|
||||
char *s;
|
||||
int looptemp=0,i=0;
|
||||
char *s,*orig_s;
|
||||
|
||||
struct ast_channel *dchan;
|
||||
struct outgoing_helper oh;
|
||||
int outstate;
|
||||
|
||||
struct ast_module_user *u;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
struct localuser *u;
|
||||
if(!data || (data && !strlen(data))) {
|
||||
ast_log(LOG_WARNING, "ParkAndAnnounce requires arguments: (announce:template|timeout|dial|[return_context])\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
l=strlen(data)+2;
|
||||
orig_s=malloc(l);
|
||||
if(!orig_s) {
|
||||
ast_log(LOG_WARNING, "Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
s=orig_s;
|
||||
strncpy(s,data,l);
|
||||
|
||||
s = ast_strdupa(data);
|
||||
|
||||
template = strsep(&s,"|");
|
||||
template=strsep(&s,"|");
|
||||
if(! template) {
|
||||
ast_log(LOG_WARNING, "PARK: An announce template must be defined\n");
|
||||
ast_module_user_remove(u);
|
||||
free(orig_s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -111,14 +92,14 @@ static int parkandannounce_exec(struct ast_channel *chan, void *data)
|
||||
timeout = atoi(strsep(&s, "|"));
|
||||
timeout *= 1000;
|
||||
}
|
||||
dial = strsep(&s, "|");
|
||||
if (ast_strlen_zero(dial)) {
|
||||
dial=strsep(&s, "|");
|
||||
if(!dial) {
|
||||
ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or Zap/g1/5551212\n");
|
||||
ast_module_user_remove(u);
|
||||
free(orig_s);
|
||||
return -1;
|
||||
} else {
|
||||
dialtech = strsep(&dial, "/");
|
||||
dialstr = dial;
|
||||
dialtech=strsep(&dial, "/");
|
||||
dialstr=dial;
|
||||
ast_verbose( VERBOSE_PREFIX_3 "Dial Tech,String: (%s,%s)\n", dialtech,dialstr);
|
||||
}
|
||||
|
||||
@@ -146,43 +127,41 @@ static int parkandannounce_exec(struct ast_channel *chan, void *data)
|
||||
}
|
||||
if(atoi(priority) < 0) {
|
||||
ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", priority);
|
||||
ast_module_user_remove(u);
|
||||
free(orig_s);
|
||||
return -1;
|
||||
}
|
||||
/* At this point we have a priority and maybe an extension and a context */
|
||||
chan->priority = atoi(priority);
|
||||
if (exten)
|
||||
ast_copy_string(chan->exten, exten, sizeof(chan->exten));
|
||||
if (context)
|
||||
ast_copy_string(chan->context, context, sizeof(chan->context));
|
||||
if(exten && strcasecmp(exten, "BYEXTENSION"))
|
||||
strncpy(chan->exten, exten, sizeof(chan->exten)-1);
|
||||
if(context)
|
||||
strncpy(chan->context, context, sizeof(chan->context)-1);
|
||||
} else { /* increment the priority by default*/
|
||||
chan->priority++;
|
||||
}
|
||||
|
||||
|
||||
if(option_verbose > 2) {
|
||||
ast_verbose( VERBOSE_PREFIX_3 "Return Context: (%s,%s,%d) ID: %s\n", chan->context,chan->exten, chan->priority, chan->cid.cid_num);
|
||||
if(!ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
|
||||
ast_verbose( VERBOSE_PREFIX_3 "Return Context: (%s,%s,%d) ID: %s\n", chan->context,chan->exten, chan->priority, chan->callerid);
|
||||
if(!ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->callerid)) {
|
||||
ast_verbose( VERBOSE_PREFIX_3 "Warning: Return Context Invalid, call will return to default|s\n");
|
||||
}
|
||||
}
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
/* we are using masq_park here to protect * from touching the channel once we park it. If the channel comes out of timeout
|
||||
before we are done announcing and the channel is messed with, Kablooeee. So we use Masq to prevent this. */
|
||||
|
||||
res = ast_masq_park_call(chan, NULL, timeout, &lot);
|
||||
if (res == -1) {
|
||||
goto finish;
|
||||
}
|
||||
ast_masq_park_call(chan, NULL, timeout, &lot);
|
||||
|
||||
res=-1;
|
||||
|
||||
ast_verbose( VERBOSE_PREFIX_3 "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, return_context);
|
||||
|
||||
/* Now place the call to the extention */
|
||||
|
||||
snprintf(buf, sizeof(buf), "%d", lot);
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
oh.parent_channel = chan;
|
||||
oh.vars = ast_variable_new("_PARKEDAT", buf);
|
||||
dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, dialstr,30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh);
|
||||
dchan = ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, dialstr,30000, &outstate, chan->callerid);
|
||||
|
||||
if(dchan) {
|
||||
if(dchan->_state == AST_STATE_UP) {
|
||||
@@ -193,12 +172,14 @@ static int parkandannounce_exec(struct ast_channel *chan, void *data)
|
||||
ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", dchan->name);
|
||||
ast_log(LOG_WARNING, "PARK: Channel %s was never answered for the announce.\n", dchan->name);
|
||||
ast_hangup(dchan);
|
||||
ast_module_user_remove(u);
|
||||
free(orig_s);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
|
||||
ast_module_user_remove(u);
|
||||
free(orig_s);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -209,15 +190,15 @@ static int parkandannounce_exec(struct ast_channel *chan, void *data)
|
||||
ast_verbose(VERBOSE_PREFIX_4 "Announce Template:%s\n", template);
|
||||
|
||||
tpl_working = template;
|
||||
tpl_current = strsep(&tpl_working, ":");
|
||||
tpl_current=strsep(&tpl_working, ":");
|
||||
|
||||
while(tpl_current && looptemp < ARRAY_LEN(tmp)) {
|
||||
while(tpl_current && looptemp < sizeof(tmp)) {
|
||||
tmp[looptemp]=tpl_current;
|
||||
looptemp++;
|
||||
tpl_current = strsep(&tpl_working,":");
|
||||
tpl_current=strsep(&tpl_working,":");
|
||||
}
|
||||
|
||||
for(i = 0; i < looptemp; i++) {
|
||||
for(i=0; i<looptemp; i++) {
|
||||
ast_verbose(VERBOSE_PREFIX_4 "Announce:%s\n", tmp[i]);
|
||||
if(!strcmp(tmp[i], "PARKED")) {
|
||||
ast_say_digits(dchan, lot, "", dchan->language);
|
||||
@@ -235,26 +216,38 @@ static int parkandannounce_exec(struct ast_channel *chan, void *data)
|
||||
ast_stopstream(dchan);
|
||||
ast_hangup(dchan);
|
||||
|
||||
finish:
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
free(orig_s);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
|
||||
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
/* return ast_register_application(app, park_exec); */
|
||||
return ast_register_application(app, parkandannounce_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Parking and Announce Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,495 +1,118 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Trivial application to playback a sound file
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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 Trivial application to playback a sound file
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/localtime.h"
|
||||
#include "asterisk/say.h"
|
||||
static char *tdesc = "Trivial Playback Application";
|
||||
|
||||
static char *app = "Playback";
|
||||
|
||||
static char *synopsis = "Play a file";
|
||||
|
||||
static char *descrip =
|
||||
" Playback(filename[&filename2...][|option]): Plays back given filenames (do not put\n"
|
||||
"extension). Options may also be included following a pipe symbol. The 'skip'\n"
|
||||
"option causes the playback of the message to be skipped if the channel\n"
|
||||
"is not in the 'up' state (i.e. it hasn't been answered yet). If 'skip' is \n"
|
||||
" Playback(filename[|option]): Plays back a given filename (do not put\n"
|
||||
"extension). Options may also be included following a pipe symbol. The 'skip'\n"
|
||||
"option causes the playback of the message to be skipped if the channel\n"
|
||||
"is not in the 'up' state (i.e. it hasn't been answered yet. If 'skip' is \n"
|
||||
"specified, the application will return immediately should the channel not be\n"
|
||||
"off hook. Otherwise, unless 'noanswer' is specified, the channel will\n"
|
||||
"off hook. Otherwise, unless 'noanswer' is specified, the channel channel will\n"
|
||||
"be answered before the sound is played. Not all channels support playing\n"
|
||||
"messages while still on hook. If 'j' is specified, the application\n"
|
||||
"will jump to priority n+101 if present when a file specified to be played\n"
|
||||
"does not exist.\n"
|
||||
"This application sets the following channel variable upon completion:\n"
|
||||
" PLAYBACKSTATUS The status of the playback attempt as a text string, one of\n"
|
||||
" SUCCESS | FAILED\n"
|
||||
"See Also: Background (application) -- for playing soundfiles that are interruptible\n"
|
||||
" WaitExten (application) -- wait for digits from caller, optionally play music on hold\n"
|
||||
;
|
||||
"messages while still hook. Returns -1 if the channel was hung up, or if the\n"
|
||||
"file does not exist. Returns 0 otherwise.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
static struct ast_config *say_cfg = NULL;
|
||||
/* save the say' api calls.
|
||||
* The first entry is NULL if we have the standard source,
|
||||
* otherwise we are sourcing from here.
|
||||
* 'say load [new|old]' will enable the new or old method, or report status
|
||||
*/
|
||||
static const void * say_api_buf[40];
|
||||
static const char *say_old = "old";
|
||||
static const char *say_new = "new";
|
||||
|
||||
static void save_say_mode(const void *arg)
|
||||
{
|
||||
int i = 0;
|
||||
say_api_buf[i++] = arg;
|
||||
|
||||
say_api_buf[i++] = ast_say_number_full;
|
||||
say_api_buf[i++] = ast_say_enumeration_full;
|
||||
say_api_buf[i++] = ast_say_digit_str_full;
|
||||
say_api_buf[i++] = ast_say_character_str_full;
|
||||
say_api_buf[i++] = ast_say_phonetic_str_full;
|
||||
say_api_buf[i++] = ast_say_datetime;
|
||||
say_api_buf[i++] = ast_say_time;
|
||||
say_api_buf[i++] = ast_say_date;
|
||||
say_api_buf[i++] = ast_say_datetime_from_now;
|
||||
say_api_buf[i++] = ast_say_date_with_format;
|
||||
}
|
||||
|
||||
static void restore_say_mode(void *arg)
|
||||
{
|
||||
int i = 0;
|
||||
say_api_buf[i++] = arg;
|
||||
|
||||
ast_say_number_full = say_api_buf[i++];
|
||||
ast_say_enumeration_full = say_api_buf[i++];
|
||||
ast_say_digit_str_full = say_api_buf[i++];
|
||||
ast_say_character_str_full = say_api_buf[i++];
|
||||
ast_say_phonetic_str_full = say_api_buf[i++];
|
||||
ast_say_datetime = say_api_buf[i++];
|
||||
ast_say_time = say_api_buf[i++];
|
||||
ast_say_date = say_api_buf[i++];
|
||||
ast_say_datetime_from_now = say_api_buf[i++];
|
||||
ast_say_date_with_format = say_api_buf[i++];
|
||||
}
|
||||
|
||||
/*
|
||||
* Typical 'say' arguments in addition to the date or number or string
|
||||
* to say. We do not include 'options' because they may be different
|
||||
* in recursive calls, and so they are better left as an external
|
||||
* parameter.
|
||||
*/
|
||||
typedef struct {
|
||||
struct ast_channel *chan;
|
||||
const char *ints;
|
||||
const char *language;
|
||||
int audiofd;
|
||||
int ctrlfd;
|
||||
} say_args_t;
|
||||
|
||||
static int s_streamwait3(const say_args_t *a, const char *fn)
|
||||
{
|
||||
int res = ast_streamfile(a->chan, fn, a->language);
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "Unable to play message %s\n", fn);
|
||||
return res;
|
||||
}
|
||||
res = (a->audiofd > -1 && a->ctrlfd > -1) ?
|
||||
ast_waitstream_full(a->chan, a->ints, a->audiofd, a->ctrlfd) :
|
||||
ast_waitstream(a->chan, a->ints);
|
||||
ast_stopstream(a->chan);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* the string is 'prefix:data' or prefix:fmt:data'
|
||||
* with ':' being invalid in strings.
|
||||
*/
|
||||
static int do_say(say_args_t *a, const char *s, const char *options, int depth)
|
||||
{
|
||||
struct ast_variable *v;
|
||||
char *lang, *x, *rule = NULL;
|
||||
int ret = 0;
|
||||
struct varshead head = { .first = NULL, .last = NULL };
|
||||
struct ast_var_t *n;
|
||||
|
||||
if (depth++ > 10) {
|
||||
ast_log(LOG_WARNING, "recursion too deep, exiting\n");
|
||||
return -1;
|
||||
} else if (!say_cfg) {
|
||||
ast_log(LOG_WARNING, "no say.conf, cannot spell '%s'\n", s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* scan languages same as in file.c */
|
||||
if (a->language == NULL)
|
||||
a->language = "en"; /* default */
|
||||
lang = ast_strdupa(a->language);
|
||||
for (;;) {
|
||||
for (v = ast_variable_browse(say_cfg, lang); v ; v = v->next) {
|
||||
if (ast_extension_match(v->name, s)) {
|
||||
rule = ast_strdupa(v->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rule)
|
||||
break;
|
||||
if ( (x = strchr(lang, '_')) )
|
||||
*x = '\0'; /* try without suffix */
|
||||
else if (strcmp(lang, "en"))
|
||||
lang = "en"; /* last resort, try 'en' if not done yet */
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (!rule)
|
||||
return 0;
|
||||
|
||||
/* skip up to two prefixes to get the value */
|
||||
if ( (x = strchr(s, ':')) )
|
||||
s = x + 1;
|
||||
if ( (x = strchr(s, ':')) )
|
||||
s = x + 1;
|
||||
n = ast_var_assign("SAY", s);
|
||||
AST_LIST_INSERT_HEAD(&head, n, entries);
|
||||
|
||||
/* scan the body, one piece at a time */
|
||||
while ( !ret && (x = strsep(&rule, ",")) ) { /* exit on key */
|
||||
char fn[128];
|
||||
const char *p, *fmt, *data; /* format and data pointers */
|
||||
|
||||
/* prepare a decent file name */
|
||||
x = ast_skip_blanks(x);
|
||||
ast_trim_blanks(x);
|
||||
|
||||
/* replace variables */
|
||||
memset(fn, 0, sizeof(fn)); /* XXX why isn't done in pbx_substitute_variables_helper! */
|
||||
pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
|
||||
|
||||
/* locate prefix and data, if any */
|
||||
fmt = strchr(fn, ':');
|
||||
if (!fmt || fmt == fn) { /* regular filename */
|
||||
ret = s_streamwait3(a, fn);
|
||||
continue;
|
||||
}
|
||||
fmt++;
|
||||
data = strchr(fmt, ':'); /* colon before data */
|
||||
if (!data || data == fmt) { /* simple prefix-fmt */
|
||||
ret = do_say(a, fn, options, depth);
|
||||
continue;
|
||||
}
|
||||
/* prefix:fmt:data */
|
||||
for (p = fmt; p < data && ret <= 0; p++) {
|
||||
char fn2[sizeof(fn)];
|
||||
if (*p == ' ' || *p == '\t') /* skip blanks */
|
||||
continue;
|
||||
if (*p == '\'') {/* file name - we trim them */
|
||||
char *y;
|
||||
strcpy(fn2, ast_skip_blanks(p+1)); /* make a full copy */
|
||||
y = strchr(fn2, '\'');
|
||||
if (!y) {
|
||||
p = data; /* invalid. prepare to end */
|
||||
break;
|
||||
}
|
||||
*y = '\0';
|
||||
ast_trim_blanks(fn2);
|
||||
p = strchr(p + 1, '\'');
|
||||
ret = s_streamwait3(a, fn2);
|
||||
} else {
|
||||
int l = fmt-fn;
|
||||
strcpy(fn2, fn); /* copy everything */
|
||||
/* after prefix, append the format */
|
||||
fn2[l++] = *p;
|
||||
strcpy(fn2 + l, data);
|
||||
ret = do_say(a, fn2, options, depth);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_var_delete(n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int say_full(struct ast_channel *chan, const char *string,
|
||||
const char *ints, const char *lang, const char *options,
|
||||
int audiofd, int ctrlfd)
|
||||
{
|
||||
say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
|
||||
return do_say(&a, string, options, 0);
|
||||
}
|
||||
|
||||
static int say_number_full(struct ast_channel *chan, int num,
|
||||
const char *ints, const char *lang, const char *options,
|
||||
int audiofd, int ctrlfd)
|
||||
{
|
||||
char buf[64];
|
||||
say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
|
||||
snprintf(buf, sizeof(buf), "num:%d", num);
|
||||
return do_say(&a, buf, options, 0);
|
||||
}
|
||||
|
||||
static int say_enumeration_full(struct ast_channel *chan, int num,
|
||||
const char *ints, const char *lang, const char *options,
|
||||
int audiofd, int ctrlfd)
|
||||
{
|
||||
char buf[64];
|
||||
say_args_t a = { chan, ints, lang, audiofd, ctrlfd };
|
||||
snprintf(buf, sizeof(buf), "enum:%d", num);
|
||||
return do_say(&a, buf, options, 0);
|
||||
}
|
||||
|
||||
static int say_date_generic(struct ast_channel *chan, time_t t,
|
||||
const char *ints, const char *lang, const char *format, const char *timezone, const char *prefix)
|
||||
{
|
||||
char buf[128];
|
||||
struct tm tm;
|
||||
say_args_t a = { chan, ints, lang, -1, -1 };
|
||||
if (format == NULL)
|
||||
format = "";
|
||||
|
||||
ast_localtime(&t, &tm, NULL);
|
||||
snprintf(buf, sizeof(buf), "%s:%s:%04d%02d%02d%02d%02d.%02d-%d-%3d",
|
||||
prefix,
|
||||
format,
|
||||
tm.tm_year+1900,
|
||||
tm.tm_mon+1,
|
||||
tm.tm_mday,
|
||||
tm.tm_hour,
|
||||
tm.tm_min,
|
||||
tm.tm_sec,
|
||||
tm.tm_wday,
|
||||
tm.tm_yday);
|
||||
return do_say(&a, buf, NULL, 0);
|
||||
}
|
||||
|
||||
static int say_date_with_format(struct ast_channel *chan, time_t t,
|
||||
const char *ints, const char *lang, const char *format, const char *timezone)
|
||||
{
|
||||
return say_date_generic(chan, t, ints, lang, format, timezone, "datetime");
|
||||
}
|
||||
|
||||
static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
|
||||
{
|
||||
return say_date_generic(chan, t, ints, lang, "", NULL, "date");
|
||||
}
|
||||
|
||||
static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
|
||||
{
|
||||
return say_date_generic(chan, t, ints, lang, "", NULL, "time");
|
||||
}
|
||||
|
||||
static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
|
||||
{
|
||||
return say_date_generic(chan, t, ints, lang, "", NULL, "datetime");
|
||||
}
|
||||
|
||||
/*
|
||||
* remap the 'say' functions to use those in this file
|
||||
*/
|
||||
static int __say_init(int fd, int argc, char *argv[])
|
||||
{
|
||||
const char *old_mode = say_api_buf[0] ? say_new : say_old;
|
||||
char *mode;
|
||||
|
||||
if (argc == 2) {
|
||||
ast_cli(fd, "say mode is [%s]\n", old_mode);
|
||||
return RESULT_SUCCESS;
|
||||
} else if (argc != 3)
|
||||
return RESULT_SHOWUSAGE;
|
||||
mode = argv[2];
|
||||
|
||||
ast_log(LOG_WARNING, "init say.c from %s to %s\n", old_mode, mode);
|
||||
|
||||
if (!strcmp(mode, old_mode)) {
|
||||
ast_log(LOG_WARNING, "say mode is %s already\n", mode);
|
||||
} else if (!strcmp(mode, say_new)) {
|
||||
if (say_cfg == NULL)
|
||||
say_cfg = ast_config_load("say.conf");
|
||||
save_say_mode(say_new);
|
||||
ast_say_number_full = say_number_full;
|
||||
|
||||
ast_say_enumeration_full = say_enumeration_full;
|
||||
#if 0
|
||||
ast_say_digits_full = say_digits_full;
|
||||
ast_say_digit_str_full = say_digit_str_full;
|
||||
ast_say_character_str_full = say_character_str_full;
|
||||
ast_say_phonetic_str_full = say_phonetic_str_full;
|
||||
ast_say_datetime_from_now = say_datetime_from_now;
|
||||
#endif
|
||||
ast_say_datetime = say_datetime;
|
||||
ast_say_time = say_time;
|
||||
ast_say_date = say_date;
|
||||
ast_say_date_with_format = say_date_with_format;
|
||||
} else if (!strcmp(mode, say_old) && say_api_buf[0] == say_new) {
|
||||
restore_say_mode(NULL);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static struct ast_cli_entry cli_playback[] = {
|
||||
{ { "say", "load", NULL },
|
||||
__say_init, "set/show the say mode",
|
||||
"Usage: say load [new|old]\n Set/show the say mode\n" },
|
||||
};
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int playback_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
int mres = 0;
|
||||
struct ast_module_user *u;
|
||||
char *tmp;
|
||||
struct localuser *u;
|
||||
char tmp[256];
|
||||
char *options;
|
||||
int option_skip=0;
|
||||
int option_say=0;
|
||||
int option_noanswer = 0;
|
||||
int priority_jump = 0;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(filenames);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
char *stringp;
|
||||
if (!data || ast_strlen_zero((char *)data)) {
|
||||
ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp = ast_strdupa(data);
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
AST_STANDARD_APP_ARGS(args, tmp);
|
||||
|
||||
if (args.options) {
|
||||
if (strcasestr(args.options, "skip"))
|
||||
option_skip = 1;
|
||||
if (strcasestr(args.options, "say"))
|
||||
option_say = 1;
|
||||
if (strcasestr(args.options, "noanswer"))
|
||||
option_noanswer = 1;
|
||||
if (strchr(args.options, 'j'))
|
||||
priority_jump = 1;
|
||||
}
|
||||
|
||||
strncpy(tmp, (char *)data, sizeof(tmp)-1);
|
||||
stringp=tmp;
|
||||
strsep(&stringp, "|");
|
||||
options = strsep(&stringp, "|");
|
||||
if (options && !strcasecmp(options, "skip"))
|
||||
option_skip = 1;
|
||||
if (options && !strcasecmp(options, "noanswer"))
|
||||
option_noanswer = 1;
|
||||
LOCAL_USER_ADD(u);
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
if (option_skip) {
|
||||
/* At the user's option, skip if the line is not up */
|
||||
goto done;
|
||||
} else if (!option_noanswer) {
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return 0;
|
||||
} else if (!option_noanswer)
|
||||
/* Otherwise answer unless we're supposed to send this while on-hook */
|
||||
res = ast_answer(chan);
|
||||
}
|
||||
}
|
||||
if (!res) {
|
||||
char *back = args.filenames;
|
||||
char *front;
|
||||
|
||||
ast_stopstream(chan);
|
||||
while (!res && (front = strsep(&back, "&"))) {
|
||||
if (option_say)
|
||||
res = say_full(chan, front, "", chan->language, NULL, -1, -1);
|
||||
else
|
||||
res = ast_streamfile(chan, front, chan->language);
|
||||
if (!res) {
|
||||
res = ast_waitstream(chan, "");
|
||||
ast_stopstream(chan);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
|
||||
if (priority_jump || ast_opt_priority_jumping)
|
||||
ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
|
||||
res = 0;
|
||||
mres = 1;
|
||||
}
|
||||
res = ast_streamfile(chan, tmp, chan->language);
|
||||
if (!res)
|
||||
res = ast_waitstream(chan, "");
|
||||
else {
|
||||
ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
|
||||
res = 0;
|
||||
}
|
||||
ast_stopstream(chan);
|
||||
}
|
||||
done:
|
||||
pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int reload(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
if (say_cfg) {
|
||||
ast_config_destroy(say_cfg);
|
||||
ast_log(LOG_NOTICE, "Reloading say.conf\n");
|
||||
}
|
||||
say_cfg = ast_config_load("say.conf");
|
||||
/*
|
||||
* XXX here we should sort rules according to the same order
|
||||
* we have in pbx.c so we have the same matching behaviour.
|
||||
*/
|
||||
return 0;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_cli_unregister_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
if (say_cfg)
|
||||
ast_config_destroy(say_cfg);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
say_cfg = ast_config_load("say.conf");
|
||||
ast_cli_register_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
|
||||
return ast_register_application(app, playback_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Sound File Playback Application",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.reload = reload,
|
||||
);
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,156 +1,85 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Block all calls without Caller*ID, require phone # to be entered
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Block all calls without Caller*ID, require phone # to be entered
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/image.h>
|
||||
#include <asterisk/callerid.h>
|
||||
#include <asterisk/app.h>
|
||||
#include <asterisk/config.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/image.h"
|
||||
#include "asterisk/callerid.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/config.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define PRIV_CONFIG "privacy.conf"
|
||||
|
||||
static char *tdesc = "Require phone number to be entered, if no CallerID sent";
|
||||
|
||||
static char *app = "PrivacyManager";
|
||||
|
||||
static char *synopsis = "Require phone number to be entered, if no CallerID sent";
|
||||
|
||||
static char *descrip =
|
||||
" PrivacyManager([maxretries[|minlength[|options]]]): If no Caller*ID \n"
|
||||
"is sent, PrivacyManager answers the channel and asks the caller to\n"
|
||||
"enter their phone number. The caller is given 3 attempts to do so.\n"
|
||||
"The application does nothing if Caller*ID was received on the channel.\n"
|
||||
" Configuration file privacy.conf contains two variables:\n"
|
||||
" maxretries default 3 -maximum number of attempts the caller is allowed \n"
|
||||
" to input a callerid.\n"
|
||||
" minlength default 10 -minimum allowable digits in the input callerid number.\n"
|
||||
"If you don't want to use the config file and have an i/o operation with\n"
|
||||
"every call, you can also specify maxretries and minlength as application\n"
|
||||
"parameters. Doing so supercedes any values set in privacy.conf.\n"
|
||||
"The option string may contain the following character: \n"
|
||||
" 'j' -- jump to n+101 priority after <maxretries> failed attempts to collect\n"
|
||||
" the minlength number of digits.\n"
|
||||
"The application sets the following channel variable upon completion: \n"
|
||||
"PRIVACYMGRSTATUS The status of the privacy manager's attempt to collect \n"
|
||||
" a phone number from the user. A text string that is either:\n"
|
||||
" SUCCESS | FAILED \n"
|
||||
;
|
||||
" PrivacyManager: If no Caller*ID is sent, PrivacyManager answers the\n"
|
||||
"channel and asks the caller to enter their 10 digit phone number.\n"
|
||||
"The caller is given 3 attempts. If after 3 attempts, they do not enter\n"
|
||||
"their 10 digit phone number, and if there exists a priority n + 101,\n"
|
||||
"where 'n' is the priority of the current instance, then the\n"
|
||||
"channel will be setup to continue at that priority level.\n"
|
||||
"Otherwise, it returns 0. Does nothing if Caller*ID was received on the\n"
|
||||
"channel.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
static int privacy_exec (struct ast_channel *chan, void *data)
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int
|
||||
privacy_exec (struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
int retries;
|
||||
int maxretries = 3;
|
||||
int minlength = 10;
|
||||
int x = 0;
|
||||
const char *s;
|
||||
char phone[30];
|
||||
struct ast_module_user *u;
|
||||
struct ast_config *cfg = NULL;
|
||||
char *parse = NULL;
|
||||
int priority_jump = 0;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(maxretries);
|
||||
AST_APP_ARG(minlength);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
int x;
|
||||
char *s;
|
||||
char phone[10];
|
||||
char new_cid[144];
|
||||
struct localuser *u;
|
||||
struct ast_config *cfg;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (!ast_strlen_zero(chan->cid.cid_num)) {
|
||||
LOCAL_USER_ADD (u);
|
||||
if (chan->callerid)
|
||||
{
|
||||
if (option_verbose > 2)
|
||||
ast_verbose (VERBOSE_PREFIX_3 "CallerID Present: Skipping\n");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
/*Answer the channel if it is not already*/
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
res = ast_answer(chan);
|
||||
if (res) {
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(data)) {
|
||||
parse = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (args.maxretries) {
|
||||
if (sscanf(args.maxretries, "%30d", &x) == 1)
|
||||
maxretries = x;
|
||||
else
|
||||
ast_log(LOG_WARNING, "Invalid max retries argument\n");
|
||||
}
|
||||
if (args.minlength) {
|
||||
if (sscanf(args.minlength, "%30d", &x) == 1)
|
||||
minlength = x;
|
||||
else
|
||||
ast_log(LOG_WARNING, "Invalid min length argument\n");
|
||||
}
|
||||
if (args.options)
|
||||
if (strchr(args.options, 'j'))
|
||||
priority_jump = 1;
|
||||
|
||||
}
|
||||
|
||||
if (!x)
|
||||
{
|
||||
/*Read in the config file*/
|
||||
cfg = ast_config_load(PRIV_CONFIG);
|
||||
/*Read in the config file*/
|
||||
cfg = ast_load(PRIV_CONFIG);
|
||||
|
||||
if (cfg && (s = ast_variable_retrieve(cfg, "general", "maxretries"))) {
|
||||
if (sscanf(s, "%30d", &x) == 1)
|
||||
maxretries = x;
|
||||
else
|
||||
ast_log(LOG_WARNING, "Invalid max retries argument\n");
|
||||
}
|
||||
|
||||
if (cfg && (s = ast_variable_retrieve(cfg, "general", "minlength"))) {
|
||||
if (sscanf(s, "%30d", &x) == 1)
|
||||
minlength = x;
|
||||
else
|
||||
ast_log(LOG_WARNING, "Invalid min length argument\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*Play unidentified call*/
|
||||
res = ast_safe_sleep(chan, 1000);
|
||||
@@ -159,21 +88,23 @@ static int privacy_exec (struct ast_channel *chan, void *data)
|
||||
if (!res)
|
||||
res = ast_waitstream(chan, "");
|
||||
|
||||
if (cfg && (s = ast_variable_retrieve(cfg, "general", "maxretries"))) {
|
||||
if (sscanf(s, "%d", &x) == 1) {
|
||||
maxretries = x;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Invalid max retries argument\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*Ask for 10 digit number, give 3 attempts*/
|
||||
for (retries = 0; retries < maxretries; retries++) {
|
||||
if (!res)
|
||||
res = ast_streamfile(chan, "privacy-prompt", chan->language);
|
||||
if (!res)
|
||||
res = ast_waitstream(chan, "");
|
||||
|
||||
if (!res )
|
||||
res = ast_readstring(chan, phone, sizeof(phone) - 1, /* digit timeout ms */ 3200, /* first digit timeout */ 5000, "#");
|
||||
|
||||
res = ast_app_getdata(chan, "privacy-prompt", phone, sizeof(phone), 0);
|
||||
if (res < 0)
|
||||
break;
|
||||
|
||||
/*Make sure we get at least digits*/
|
||||
if (strlen(phone) >= minlength )
|
||||
/*Make sure we get 10 digits*/
|
||||
if (strlen(phone) == 10)
|
||||
break;
|
||||
else {
|
||||
res = ast_streamfile(chan, "privacy-incorrect", chan->language);
|
||||
@@ -183,50 +114,57 @@ static int privacy_exec (struct ast_channel *chan, void *data)
|
||||
}
|
||||
|
||||
/*Got a number, play sounds and send them on their way*/
|
||||
if ((retries < maxretries) && res >= 0 ) {
|
||||
if ((retries < maxretries) && !res) {
|
||||
res = ast_streamfile(chan, "privacy-thankyou", chan->language);
|
||||
if (!res)
|
||||
res = ast_waitstream(chan, "");
|
||||
|
||||
ast_set_callerid (chan, phone, "Privacy Manager", NULL);
|
||||
|
||||
/* Clear the unavailable presence bit so if it came in on PRI
|
||||
* the caller id will now be passed out to other channels
|
||||
*/
|
||||
chan->cid.cid_pres &= (AST_PRES_UNAVAILABLE ^ 0xFF);
|
||||
|
||||
if (option_verbose > 2) {
|
||||
ast_verbose (VERBOSE_PREFIX_3 "Changed Caller*ID to %s, callerpres to %d\n",phone,chan->cid.cid_pres);
|
||||
}
|
||||
pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "SUCCESS");
|
||||
snprintf (new_cid, sizeof (new_cid), "\"%s\" <%s>", "Privacy Manager", phone);
|
||||
ast_set_callerid (chan, new_cid, 0);
|
||||
if (option_verbose > 2)
|
||||
ast_verbose (VERBOSE_PREFIX_3 "Changed Caller*ID to %s\n",new_cid);
|
||||
} else {
|
||||
if (priority_jump || ast_opt_priority_jumping)
|
||||
ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
|
||||
pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "FAILED");
|
||||
/*Send the call to n+101 priority, where n is the current priority*/
|
||||
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
|
||||
chan->priority+=100;
|
||||
}
|
||||
if (cfg)
|
||||
ast_config_destroy(cfg);
|
||||
ast_destroy(cfg);
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
LOCAL_USER_REMOVE (u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int
|
||||
unload_module (void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application (app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application (app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int
|
||||
load_module (void)
|
||||
{
|
||||
return ast_register_application (app, privacy_exec, synopsis, descrip);
|
||||
return ast_register_application (app, privacy_exec, synopsis,
|
||||
descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Require phone number to be entered, if no CallerID sent");
|
||||
char *
|
||||
description (void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int
|
||||
usecount (void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT (res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *
|
||||
key ()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
394
apps/app_qcall.c
Normal file
394
apps/app_qcall.c
Normal file
@@ -0,0 +1,394 @@
|
||||
/** @file app_qcall.c
|
||||
*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Call back a party and connect them to a running pbx thread
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License
|
||||
*
|
||||
* Call a user from a file contained within a queue (/var/spool/asterisk/qcall)
|
||||
*
|
||||
* The queue is a directory containing files with the call request information
|
||||
* as a single line of text as follows:
|
||||
*
|
||||
* Dialstring Caller-ID Extension Maxsecs [Identifier] [Required-response]
|
||||
*
|
||||
* Dialstring -- A Dial String (The number to be called) in the
|
||||
* format Technology/Number, such IAX/mysys/1234 or Zap/g1/1234
|
||||
*
|
||||
* Caller-ID -- A Standard nomalized representation of the Caller-ID of
|
||||
* the number being dialed (generally 10 digits in the US). Leave as
|
||||
* "asreceived" to use the default Caller*ID
|
||||
*
|
||||
* Extension -- The Extension (optionally Extension@context) that the
|
||||
* user should be "transferred" to after acceptance of the call.
|
||||
*
|
||||
* Maxsecs -- The Maximum time of the call in seconds. Specify 0 for infinite.
|
||||
*
|
||||
* Identifier -- The "Identifier" of the request. This is used to determine
|
||||
* the names of the audio prompt files played. The first prompt, the one that
|
||||
* asks for the input, is just the exact string specified as the identifier.
|
||||
* The second prompt, the one that is played after the correct input is given,
|
||||
* (generally a "thank you" recording), is the specified string with "-ok"
|
||||
* added to the end. So, if you specify "foo" as the identifier, your first
|
||||
* prompt file that will be played will be "foo" and the second one will be
|
||||
* "foo-ok". If omitted no prompt is given
|
||||
*
|
||||
* Required-Response (Optional) -- Specify a digit string to be used as the
|
||||
* acceptance "code" if you desire it to be something other then "1". This
|
||||
* can be used to implement some sort of PIN or security system. It may be
|
||||
* more then a single character.
|
||||
*
|
||||
* NOTE: It is important to remember that the process that creates these
|
||||
* files needs keep and maintain a write lock (using flock with the LOCK_EX
|
||||
* option) when writing these files.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/file.h>
|
||||
#include "../astconf.h"
|
||||
|
||||
static char qdir[255];
|
||||
static char *tdesc = "Call from Queue";
|
||||
static pthread_t qcall_thread;
|
||||
static int debug = 0;
|
||||
STANDARD_LOCAL_USER;
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
#define OLDESTOK 14400 /* not any more then this number of secs old */
|
||||
#define INITIALONE 1 /* initial wait before the first one in secs */
|
||||
#define NEXTONE 600 /* wait before trying it again in secs */
|
||||
#define MAXWAITFORANSWER 45000 /* max call time before answer */
|
||||
/* define either one or both of these two if your application requires it */
|
||||
#if 0
|
||||
#define ACCTCODE "SOMETHING" /* Account code */
|
||||
#define AMAFLAGS AST_CDR_BILLING /* AMA flags */
|
||||
#endif
|
||||
/* define this if you want to have a particular CLID display on the user's
|
||||
phone when they receive the call */
|
||||
#if 0
|
||||
#define OURCLID "2564286275" /* The callerid to be displayed when calling */
|
||||
#endif
|
||||
|
||||
static void *qcall_do(void *arg);
|
||||
|
||||
static void *qcall(void *ignore)
|
||||
{
|
||||
pthread_t dialer_thread;
|
||||
DIR *dirp;
|
||||
FILE *fp;
|
||||
struct dirent *dp;
|
||||
char fname[80];
|
||||
struct stat mystat;
|
||||
time_t t;
|
||||
void *arg;
|
||||
pthread_attr_t attr;
|
||||
|
||||
time(&t);
|
||||
if (debug) printf("@@@@ qcall starting at %s",ctime(&t));
|
||||
for(;;)
|
||||
{
|
||||
time(&t);
|
||||
dirp = opendir(qdir);
|
||||
if (!dirp)
|
||||
{
|
||||
perror("app_qcall:Cannot open queue directory");
|
||||
break;
|
||||
}
|
||||
while((dp = readdir(dirp)) != NULL)
|
||||
{
|
||||
if (dp->d_name[0] == '.') continue;
|
||||
snprintf(fname, sizeof(fname), "%s/%s", qdir, dp->d_name);
|
||||
if (stat(fname,&mystat) == -1)
|
||||
{
|
||||
perror("app_qcall:stat");
|
||||
fprintf(stderr,"%s\n",fname);
|
||||
continue;
|
||||
}
|
||||
/* if not a regular file, skip it */
|
||||
if ((mystat.st_mode & S_IFMT) != S_IFREG) continue;
|
||||
/* if not yet .... */
|
||||
if (mystat.st_atime == mystat.st_mtime)
|
||||
{ /* first time */
|
||||
if ((mystat.st_atime + INITIALONE) > t)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{ /* already looked at once */
|
||||
if ((mystat.st_atime + NEXTONE) > t) continue;
|
||||
}
|
||||
/* if too old */
|
||||
if (mystat.st_mtime < (t - OLDESTOK))
|
||||
{
|
||||
/* kill it, its too old */
|
||||
unlink(fname);
|
||||
continue;
|
||||
}
|
||||
/* "touch" file's access time */
|
||||
fp = fopen(fname,"r");
|
||||
if (fp) fclose(fp);
|
||||
/* make a copy of the filename string, so that we
|
||||
may go on and use the buffer */
|
||||
arg = (void *) strdup(fname);
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
if (ast_pthread_create(&dialer_thread,&attr,qcall_do,arg) == -1)
|
||||
{
|
||||
perror("qcall: Cannot create thread");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
closedir(dirp);
|
||||
sleep(1);
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
/* single thread with one file (request) to dial */
|
||||
static void *qcall_do(void *arg)
|
||||
{
|
||||
char fname[300] = "";
|
||||
char dialstr[300];
|
||||
char extstr[300];
|
||||
char ident[300] = "";
|
||||
char reqinp[300] = "";
|
||||
char buf[300];
|
||||
char clid[300],*tele,*context;
|
||||
FILE *fp;
|
||||
int ms = MAXWAITFORANSWER,maxsecs;
|
||||
struct ast_channel *channel;
|
||||
time_t t;
|
||||
|
||||
/* get the filename from the arg */
|
||||
strncpy(fname,(char *)arg, sizeof(fname) - 1);
|
||||
free(arg);
|
||||
time(&t);
|
||||
fp = fopen(fname,"r");
|
||||
if (!fp) /* if cannot open request file */
|
||||
{
|
||||
perror("qcall_do:fopen");
|
||||
fprintf(stderr,"%s\n",fname);
|
||||
unlink(fname);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
/* lock the file */
|
||||
if (flock(fileno(fp),LOCK_EX) == -1)
|
||||
{
|
||||
perror("qcall_do:flock");
|
||||
fprintf(stderr,"%s\n",fname);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
/* default required input for acknowledgement */
|
||||
reqinp[0] = '1';
|
||||
reqinp[1] = '\0';
|
||||
/* default no ident */
|
||||
ident[0] = '\0'; /* default no ident */
|
||||
if (fscanf(fp,"%s %s %s %d %s %s",dialstr,clid,
|
||||
extstr,&maxsecs,ident,reqinp) < 4)
|
||||
{
|
||||
fprintf(stderr,"qcall_do:file line invalid in file %s:\n",fname);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
flock(fileno(fp),LOCK_UN);
|
||||
fclose(fp);
|
||||
tele = strchr(dialstr,'/');
|
||||
if (!tele)
|
||||
{
|
||||
fprintf(stderr,"qcall_do:Dial number must be in format tech/number\n");
|
||||
unlink(fname);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
*tele++ = 0;
|
||||
channel = ast_request(dialstr,AST_FORMAT_SLINEAR,tele);
|
||||
if (channel)
|
||||
{
|
||||
ast_set_read_format(channel,AST_FORMAT_SLINEAR);
|
||||
ast_set_write_format(channel,AST_FORMAT_SLINEAR);
|
||||
#ifdef OURCLID
|
||||
if (channel->callerid)
|
||||
free(channel->callerid);
|
||||
channel->callerid = strdup(OURCLID);
|
||||
if (channel->ani)
|
||||
free(channel->ani);
|
||||
channel->ani = strdup(OURCLID);
|
||||
#endif
|
||||
channel->whentohangup = 0;
|
||||
channel->appl = "AppQcall";
|
||||
channel->data = "(Outgoing Line)";
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Qcall initiating call to %s/%s on %s (%s)\n",
|
||||
dialstr,tele,channel->name,fname);
|
||||
ast_call(channel,tele,MAXWAITFORANSWER);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr,"qcall_do:Sorry unable to obtain channel\n");
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
if (strcasecmp(clid, "asreceived")) {
|
||||
if (channel->callerid) free(channel->callerid);
|
||||
channel->callerid = NULL;
|
||||
if (channel->ani) free(channel->ani);
|
||||
channel->ani = NULL;
|
||||
}
|
||||
if (channel->_state == AST_STATE_UP)
|
||||
if (debug) printf("@@@@ Autodial:Line is Up\n");
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Qcall waiting for answer on %s\n",
|
||||
channel->name);
|
||||
while(ms > 0){
|
||||
struct ast_frame *f;
|
||||
ms = ast_waitfor(channel,ms);
|
||||
f = ast_read(channel);
|
||||
if (!f)
|
||||
{
|
||||
if (debug) printf("@@@@ qcall_do:Hung Up\n");
|
||||
unlink(fname);
|
||||
break;
|
||||
}
|
||||
if (f->frametype == AST_FRAME_CONTROL)
|
||||
{
|
||||
if (f->subclass == AST_CONTROL_HANGUP)
|
||||
{
|
||||
if (debug) printf("@@@@ qcall_do:Hung Up\n");
|
||||
unlink(fname);
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
if (f->subclass == AST_CONTROL_ANSWER)
|
||||
{
|
||||
if (debug) printf("@@@@ qcall_do:Phone Answered\n");
|
||||
if (channel->_state == AST_STATE_UP)
|
||||
{
|
||||
unlink(fname);
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Qcall got answer on %s\n",
|
||||
channel->name);
|
||||
usleep(1500000);
|
||||
if (strlen(ident)) {
|
||||
ast_streamfile(channel,ident,0);
|
||||
if (ast_readstring(channel,buf,strlen(reqinp),10000,5000,"#"))
|
||||
{
|
||||
ast_stopstream(channel);
|
||||
if (debug) printf("@@@@ qcall_do: timeout or hangup in dtmf read\n");
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
ast_stopstream(channel);
|
||||
if (strcmp(buf,reqinp)) /* if not match */
|
||||
{
|
||||
if (debug) printf("@@@@ qcall_do: response (%s) does not match required (%s)\n",buf,reqinp);
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
/* okay, now we go for it */
|
||||
context = strchr(extstr,'@');
|
||||
if (!context) context = "default";
|
||||
else *context++ = 0;
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Qcall got accept, now putting through to %s@%s on %s\n",
|
||||
extstr,context,channel->name);
|
||||
if (strlen(ident)) {
|
||||
strncat(ident,"-ok", sizeof(ident) - strlen(ident) - 1);
|
||||
/* if file existent, play it */
|
||||
if (!ast_streamfile(channel,ident,0))
|
||||
{
|
||||
ast_waitstream(channel,"");
|
||||
ast_stopstream(channel);
|
||||
}
|
||||
}
|
||||
if (strcasecmp(clid, "asreceived")) {
|
||||
channel->callerid = strdup(clid);
|
||||
channel->ani = strdup(clid);
|
||||
}
|
||||
channel->language[0] = 0;
|
||||
channel->dnid = strdup(extstr);
|
||||
#ifdef AMAFLAGS
|
||||
channel->amaflags = AMAFLAGS;
|
||||
#endif
|
||||
#ifdef ACCTCODE
|
||||
strncpy(channel->accountcode, ACCTCODE, sizeof(chan->accountcode) - 1);
|
||||
#else
|
||||
channel->accountcode[0] = 0;
|
||||
#endif
|
||||
if (maxsecs) /* if finite length call */
|
||||
{
|
||||
time(&channel->whentohangup);
|
||||
channel->whentohangup += maxsecs;
|
||||
}
|
||||
strncpy(channel->exten, extstr, sizeof(channel->exten) - 1);
|
||||
strncpy(channel->context, context, sizeof(channel->context) - 1);
|
||||
channel->priority = 1;
|
||||
if(debug) printf("Caller ID is %s\n", channel->callerid);
|
||||
ast_pbx_run(channel);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
}
|
||||
else if(f->subclass==AST_CONTROL_RINGING)
|
||||
if (debug) printf("@@@@ qcall_do:Phone Ringing end\n");
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
ast_hangup(channel);
|
||||
if (debug) printf("@@@@ qcall_do:Hung up channel\n");
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "qcall");
|
||||
mkdir(qdir,0760);
|
||||
ast_pthread_create(&qcall_thread,NULL,qcall,NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
5851
apps/app_queue.c
5851
apps/app_queue.c
File diff suppressed because it is too large
Load Diff
@@ -1,45 +1,29 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (c) 2003 - 2005 Tilghman Lesher. All rights reserved.
|
||||
* Random application
|
||||
*
|
||||
* Copyright (c) 2003-2004 Tilghman Lesher. All rights reserved.
|
||||
*
|
||||
* Tilghman Lesher <asterisk__app_random__200508@the-tilghman.com>
|
||||
* Tilghman Lesher <asterisk__app_random__20040111@the-tilghman.com>
|
||||
*
|
||||
* This code is released by the author with no restrictions on usage or distribution.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Random application
|
||||
*
|
||||
* \author Tilghman Lesher <asterisk__app_random__200508@the-tilghman.com>
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
|
||||
/*! \todo The Random() app should be removed from trunk following the release of 1.4 */
|
||||
static char *tdesc = "Random goto";
|
||||
|
||||
static char *app_random = "Random";
|
||||
|
||||
@@ -47,62 +31,97 @@ static char *random_synopsis = "Conditionally branches, based upon a probability
|
||||
|
||||
static char *random_descrip =
|
||||
"Random([probability]:[[context|]extension|]priority)\n"
|
||||
" probability := INTEGER in the range 1 to 100\n"
|
||||
"DEPRECATED: Use GotoIf($[${RAND(1,100)} > <number>]?<label>)\n";
|
||||
" probability := INTEGER in the range 1 to 100\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int random_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
struct ast_module_user *u;
|
||||
struct localuser *u;
|
||||
|
||||
char *s;
|
||||
char *exten, *pri, *context;
|
||||
char *prob;
|
||||
int probint;
|
||||
static int deprecated = 0;
|
||||
int probint, priorityint;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
if (!data) {
|
||||
ast_log(LOG_WARNING, "Random requires an argument ([probability]:[[context|]extension|]priority)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
s = ast_strdupa(data);
|
||||
LOCAL_USER_ADD(u);
|
||||
s = ast_strdupa((void *) data);
|
||||
|
||||
prob = strsep(&s,":");
|
||||
if ((!prob) || (sscanf(prob, "%30d", &probint) != 1))
|
||||
if ((!prob) || (sscanf(prob, "%d", &probint) != 1))
|
||||
probint = 0;
|
||||
|
||||
if (!deprecated) {
|
||||
deprecated = 1;
|
||||
ast_log(LOG_WARNING, "Random is deprecated in Asterisk 1.4. Replace with GotoIf($[${RAND(0,99)} + %d >= 100]?%s)\n", probint, s);
|
||||
}
|
||||
|
||||
if ((ast_random() % 100) + probint >= 100) {
|
||||
res = ast_parseable_goto(chan, s);
|
||||
if ((random() % 100) + probint > 100) {
|
||||
context = strsep(&s, "|");
|
||||
exten = strsep(&s, "|");
|
||||
if (!exten) {
|
||||
/* Only a priority */
|
||||
pri = context;
|
||||
exten = NULL;
|
||||
context = NULL;
|
||||
} else {
|
||||
pri = strsep(&s, "|");
|
||||
if (!pri) {
|
||||
pri = exten;
|
||||
exten = context;
|
||||
context = NULL;
|
||||
}
|
||||
}
|
||||
if (!pri) {
|
||||
ast_log(LOG_WARNING, "No label specified\n");
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
} else if (sscanf(pri, "%d", &priorityint) != 1) {
|
||||
ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", pri);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return -1;
|
||||
}
|
||||
/* At this point we have a priority and */
|
||||
/* maybe an extension and a context */
|
||||
chan->priority = priorityint - 1;
|
||||
if (exten && strcasecmp(exten, "BYEXTENSION"))
|
||||
strncpy(chan->exten, exten, sizeof(chan->exten)-1);
|
||||
if (context)
|
||||
strncpy(chan->context, context, sizeof(chan->context)-1);
|
||||
if (option_verbose > 2)
|
||||
ast_verbose( VERBOSE_PREFIX_3 "Random branches to (%s,%s,%d)\n",
|
||||
chan->context,chan->exten, chan->priority+1);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
}
|
||||
ast_module_user_remove(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app_random);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app_random);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app_random, random_exec, random_synopsis, random_descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Random goto");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
274
apps/app_read.c
274
apps/app_read.c
@@ -1,234 +1,144 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Trivial application to read a variable
|
||||
*
|
||||
* Copyright (C) 2003, Digium
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Trivial application to read a variable
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/app.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/indications.h"
|
||||
|
||||
enum {
|
||||
OPT_SKIP = (1 << 0),
|
||||
OPT_INDICATION = (1 << 1),
|
||||
OPT_NOANSWER = (1 << 2),
|
||||
} read_option_flags;
|
||||
|
||||
AST_APP_OPTIONS(read_app_options, {
|
||||
AST_APP_OPTION('s', OPT_SKIP),
|
||||
AST_APP_OPTION('i', OPT_INDICATION),
|
||||
AST_APP_OPTION('n', OPT_NOANSWER),
|
||||
});
|
||||
static char *tdesc = "Read Variable Application";
|
||||
|
||||
static char *app = "Read";
|
||||
|
||||
static char *synopsis = "Read a variable";
|
||||
|
||||
static char *descrip =
|
||||
" Read(variable[|filename][|maxdigits][|option][|attempts][|timeout])\n\n"
|
||||
"Reads a #-terminated string of digits a certain number of times from the\n"
|
||||
"user in to the given variable.\n"
|
||||
" filename -- file to play before reading digits or tone with option i\n"
|
||||
" Read(variable[|filename][|maxdigits][|option])\n\n"
|
||||
"Reads a #-terminated string of digits from the user in to the given variable,\n"
|
||||
"optionally playing a given filename first.\n"
|
||||
" maxdigits -- maximum acceptable number of digits. Stops reading after\n"
|
||||
" maxdigits have been entered (without requiring the user to\n"
|
||||
" press the '#' key).\n"
|
||||
" Defaults to 0 - no limit - wait for the user press the '#' key.\n"
|
||||
" Any value below 0 means the same. Max accepted value is 255.\n"
|
||||
" option -- options are 's' , 'i', 'n'\n"
|
||||
" 's' to return immediately if the line is not up,\n"
|
||||
" 'i' to play filename as an indication tone from your indications.conf\n"
|
||||
" 'n' to read digits even if the line is not up.\n"
|
||||
" attempts -- if greater than 1, that many attempts will be made in the \n"
|
||||
" event no data is entered.\n"
|
||||
" timeout -- An integer number of seconds to wait for a digit response. If greater\n"
|
||||
" than 0, that value will override the default timeout.\n\n"
|
||||
"Read should disconnect if the function fails or errors out.\n";
|
||||
" option -- may be 'skip' to return immediately if the line is not up,\n"
|
||||
" or 'noanswer' to read digits even if the line is not up.\n\n"
|
||||
"Returns -1 on hangup or error and 0 otherwise.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
#define ast_next_data(instr,ptr,delim) if((ptr=strchr(instr,delim))) { *(ptr) = '\0' ; ptr++;}
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int read_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_module_user *u;
|
||||
char tmp[256] = "";
|
||||
int maxdigits = 255;
|
||||
int tries = 1, to = 0, x = 0;
|
||||
char *argcopy = NULL;
|
||||
struct tone_zone_sound *ts;
|
||||
struct ast_flags flags = {0};
|
||||
|
||||
AST_DECLARE_APP_ARGS(arglist,
|
||||
AST_APP_ARG(variable);
|
||||
AST_APP_ARG(filename);
|
||||
AST_APP_ARG(maxdigits);
|
||||
AST_APP_ARG(options);
|
||||
AST_APP_ARG(attempts);
|
||||
AST_APP_ARG(timeout);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
struct localuser *u;
|
||||
char tmp[256];
|
||||
char argdata[256] = "";
|
||||
char *varname;
|
||||
char *filename;
|
||||
char *stringp;
|
||||
char *maxdigitstr;
|
||||
char *options;
|
||||
int option_skip = 0;
|
||||
int option_noanswer = 0;
|
||||
int maxdigits=255;
|
||||
if (!data || ast_strlen_zero((char *)data)) {
|
||||
ast_log(LOG_WARNING, "Read requires an argument (variable)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
argcopy = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(arglist, argcopy);
|
||||
|
||||
if (!ast_strlen_zero(arglist.options)) {
|
||||
ast_app_parse_options(read_app_options, &flags, NULL, arglist.options);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(arglist.attempts)) {
|
||||
tries = atoi(arglist.attempts);
|
||||
if (tries <= 0)
|
||||
tries = 1;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(arglist.timeout)) {
|
||||
to = atoi(arglist.timeout);
|
||||
if (to <= 0)
|
||||
to = 0;
|
||||
else
|
||||
to *= 1000;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(arglist.filename)) {
|
||||
arglist.filename = NULL;
|
||||
}
|
||||
if (!ast_strlen_zero(arglist.maxdigits)) {
|
||||
maxdigits = atoi(arglist.maxdigits);
|
||||
if ((maxdigits<1) || (maxdigits>255)) {
|
||||
maxdigits = 255;
|
||||
} else if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Accepting a maximum of %d digits.\n", maxdigits);
|
||||
}
|
||||
if (ast_strlen_zero(arglist.variable)) {
|
||||
ast_log(LOG_WARNING, "Invalid! Usage: Read(variable[|filename][|maxdigits][|option][|attempts][|timeout])\n\n");
|
||||
ast_module_user_remove(u);
|
||||
strncpy(argdata, (char *)data, sizeof(argdata)-1);
|
||||
stringp=argdata;
|
||||
varname = strsep(&stringp, "|");
|
||||
filename = strsep(&stringp, "|");
|
||||
maxdigitstr = strsep(&stringp,"|");
|
||||
options = strsep(&stringp, "|");
|
||||
if (options && !strcasecmp(options, "skip"))
|
||||
option_skip = 1;
|
||||
if (options && !strcasecmp(options, "noanswer"))
|
||||
option_noanswer = 1;
|
||||
if (!(filename) || ast_strlen_zero(filename))
|
||||
filename = NULL;
|
||||
if (maxdigitstr) {
|
||||
maxdigits = atoi(maxdigitstr);
|
||||
if ((maxdigits<1) || (maxdigits>255)) {
|
||||
maxdigits = 255;
|
||||
} else
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Accepting a maximum of %i digits.\n", maxdigits);
|
||||
}
|
||||
if (!(varname) || ast_strlen_zero(varname)) {
|
||||
ast_log(LOG_WARNING, "Read requires an variable name\n");
|
||||
return -1;
|
||||
}
|
||||
ts=NULL;
|
||||
if (ast_test_flag(&flags,OPT_INDICATION)) {
|
||||
if (!ast_strlen_zero(arglist.filename)) {
|
||||
ts = ast_get_indication_tone(chan->zone,arglist.filename);
|
||||
}
|
||||
}
|
||||
LOCAL_USER_ADD(u);
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
if (ast_test_flag(&flags,OPT_SKIP)) {
|
||||
if (option_skip) {
|
||||
/* At the user's option, skip if the line is not up */
|
||||
pbx_builtin_setvar_helper(chan, arglist.variable, "\0");
|
||||
ast_module_user_remove(u);
|
||||
pbx_builtin_setvar_helper(chan, varname, "\0");
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return 0;
|
||||
} else if (!ast_test_flag(&flags,OPT_NOANSWER)) {
|
||||
} else if (!option_noanswer) {
|
||||
/* Otherwise answer unless we're supposed to read while on-hook */
|
||||
res = ast_answer(chan);
|
||||
}
|
||||
}
|
||||
if (!res) {
|
||||
while (tries && !res) {
|
||||
ast_stopstream(chan);
|
||||
if (ts && ts->data[0]) {
|
||||
if (!to)
|
||||
to = chan->pbx ? chan->pbx->rtimeout * 1000 : 6000;
|
||||
res = ast_playtones_start(chan, 0, ts->data, 0);
|
||||
for (x = 0; x < maxdigits; ) {
|
||||
res = ast_waitfordigit(chan, to);
|
||||
ast_playtones_stop(chan);
|
||||
if (res < 1) {
|
||||
tmp[x]='\0';
|
||||
break;
|
||||
}
|
||||
tmp[x++] = res;
|
||||
if (tmp[x-1] == '#') {
|
||||
tmp[x-1] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = ast_app_getdata(chan, arglist.filename, tmp, maxdigits, to);
|
||||
}
|
||||
if (res > -1) {
|
||||
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
|
||||
if (!ast_strlen_zero(tmp)) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "User entered '%s'\n", tmp);
|
||||
tries = 0;
|
||||
} else {
|
||||
tries--;
|
||||
if (option_verbose > 2) {
|
||||
if (tries)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "User entered nothing, %d chance%s left\n", tries, (tries != 1) ? "s" : "");
|
||||
else
|
||||
ast_verbose(VERBOSE_PREFIX_3 "User entered nothing.\n");
|
||||
}
|
||||
}
|
||||
res = 0;
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "User disconnected\n");
|
||||
}
|
||||
ast_stopstream(chan);
|
||||
res = ast_app_getdata(chan, filename, tmp, maxdigits, 0);
|
||||
if (res > -1) {
|
||||
pbx_builtin_setvar_helper(chan, varname, tmp);
|
||||
ast_verbose(VERBOSE_PREFIX_3 "User entered '%s'\n", tmp);
|
||||
res = 0;
|
||||
} else {
|
||||
ast_verbose(VERBOSE_PREFIX_3 "User disconnected\n");
|
||||
}
|
||||
}
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, read_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read Variable Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Matt O'Gorman <mogorman@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief ReadFile application -- Reads in a File for you.
|
||||
*
|
||||
* \author Matt O'Gorman <mogorman@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/module.h"
|
||||
|
||||
static char *app_readfile = "ReadFile";
|
||||
|
||||
static char *readfile_synopsis = "ReadFile(varname=file,length)";
|
||||
|
||||
static char *readfile_descrip =
|
||||
"ReadFile(varname=file,length)\n"
|
||||
" Varname - Result stored here.\n"
|
||||
" File - The name of the file to read.\n"
|
||||
" Length - Maximum number of characters to capture.\n";
|
||||
|
||||
|
||||
static int readfile_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0;
|
||||
struct ast_module_user *u;
|
||||
char *s, *varname=NULL, *file=NULL, *length=NULL, *returnvar=NULL;
|
||||
int len=0;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "ReadFile require an argument!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
s = ast_strdupa(data);
|
||||
|
||||
varname = strsep(&s, "=");
|
||||
file = strsep(&s, "|");
|
||||
length = s;
|
||||
|
||||
if (!varname || !file) {
|
||||
ast_log(LOG_ERROR, "No file or variable specified!\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (length) {
|
||||
if ((sscanf(length, "%30d", &len) != 1) || (len < 0)) {
|
||||
ast_log(LOG_WARNING, "%s is not a positive number, defaulting length to max\n", length);
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((returnvar = ast_read_textfile(file))) {
|
||||
if (len > 0) {
|
||||
if (len < strlen(returnvar))
|
||||
returnvar[len]='\0';
|
||||
else
|
||||
ast_log(LOG_WARNING, "%s is longer than %d, and %d \n", file, len, (int)strlen(returnvar));
|
||||
}
|
||||
pbx_builtin_setvar_helper(chan, varname, returnvar);
|
||||
free(returnvar);
|
||||
}
|
||||
ast_module_user_remove(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app_readfile);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application(app_readfile, readfile_exec, readfile_synopsis, readfile_descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Stores output of file into a variable");
|
||||
@@ -1,263 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Anthony Minessale <anthmct@yahoo.com>
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief RealTime App
|
||||
*
|
||||
* \author Anthony Minessale <anthmct@yahoo.com>
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/cli.h"
|
||||
|
||||
#define next_one(var) var = var->next
|
||||
#define crop_data(str) { *(str) = '\0' ; (str)++; }
|
||||
|
||||
static char *app = "RealTime";
|
||||
static char *uapp = "RealTimeUpdate";
|
||||
static char *synopsis = "Realtime Data Lookup";
|
||||
static char *usynopsis = "Realtime Data Rewrite";
|
||||
static char *USAGE = "RealTime(<family>|<colmatch>|<value>[|<prefix>])";
|
||||
static char *UUSAGE = "RealTimeUpdate(<family>|<colmatch>|<value>|<newcol>|<newval>)";
|
||||
static char *desc =
|
||||
"Use the RealTime config handler system to read data into channel variables.\n"
|
||||
"RealTime(<family>|<colmatch>|<value>[|<prefix>])\n\n"
|
||||
"All unique column names will be set as channel variables with optional prefix\n"
|
||||
"to the name. For example, a prefix of 'var_' would make the column 'name'\n"
|
||||
"become the variable ${var_name}. REALTIMECOUNT will be set with the number\n"
|
||||
"of values read.\n";
|
||||
static char *udesc = "Use the RealTime config handler system to update a value\n"
|
||||
"RealTimeUpdate(<family>|<colmatch>|<value>|<newcol>|<newval>)\n\n"
|
||||
"The column <newcol> in 'family' matching column <colmatch>=<value> will be\n"
|
||||
"updated to <newval>. REALTIMECOUNT will be set with the number of rows\n"
|
||||
"updated or -1 if an error occurs.\n";
|
||||
|
||||
|
||||
static int cli_realtime_load(int fd, int argc, char **argv)
|
||||
{
|
||||
char *header_format = "%30s %-30s\n";
|
||||
struct ast_variable *var = NULL, *save = NULL;
|
||||
|
||||
if (argc < 5) {
|
||||
ast_cli(fd, "You must supply a family name, a column to match on, and a value to match to.\n");
|
||||
return RESULT_FAILURE;
|
||||
}
|
||||
|
||||
var = ast_load_realtime(argv[2], argv[3], argv[4], NULL);
|
||||
|
||||
if (var) {
|
||||
save = var;
|
||||
ast_cli(fd, header_format, "Column Name", "Column Value");
|
||||
ast_cli(fd, header_format, "--------------------", "--------------------");
|
||||
while (var) {
|
||||
ast_cli(fd, header_format, var->name, var->value);
|
||||
var = var->next;
|
||||
}
|
||||
ast_variables_destroy(save);
|
||||
} else {
|
||||
ast_cli(fd, "No rows found matching search criteria.\n");
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static int cli_realtime_update(int fd, int argc, char **argv) {
|
||||
int res = 0;
|
||||
|
||||
if(argc<7) {
|
||||
ast_cli(fd, "You must supply a family name, a column to update on, a new value, column to match, and value to to match.\n");
|
||||
ast_cli(fd, "Ex: realtime update sipfriends name bobsphone port 4343\n will execute SQL as UPDATE sipfriends SET port = 4343 WHERE name = bobsphone\n");
|
||||
return RESULT_FAILURE;
|
||||
}
|
||||
|
||||
res = ast_update_realtime(argv[2], argv[3], argv[4], argv[5], argv[6], NULL);
|
||||
|
||||
if(res < 0) {
|
||||
ast_cli(fd, "Failed to update. Check the debug log for possible SQL related entries.\n");
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ast_cli(fd, "Updated %d RealTime record%s.\n", res, (res != 1) ? "s" : "");
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static char cli_realtime_load_usage[] =
|
||||
"Usage: realtime load <family> <colmatch> <value>\n"
|
||||
" Prints out a list of variables using the RealTime driver.\n";
|
||||
|
||||
static char cli_realtime_update_usage[] =
|
||||
"Usage: realtime update <family> <colmatch> <value>\n"
|
||||
" Update a single variable using the RealTime driver.\n";
|
||||
|
||||
static struct ast_cli_entry cli_realtime[] = {
|
||||
{ { "realtime", "load", NULL, NULL },
|
||||
cli_realtime_load, "Used to print out RealTime variables.",
|
||||
cli_realtime_load_usage, NULL },
|
||||
|
||||
{ { "realtime", "update", NULL, NULL },
|
||||
cli_realtime_update, "Used to update RealTime variables.",
|
||||
cli_realtime_update_usage, NULL },
|
||||
};
|
||||
|
||||
static int realtime_update_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
char *family=NULL, *colmatch=NULL, *value=NULL, *newcol=NULL, *newval=NULL;
|
||||
struct ast_module_user *u;
|
||||
int res = 0, count = 0;
|
||||
char countc[13];
|
||||
|
||||
ast_log(LOG_WARNING, "The RealTimeUpdate application has been deprecated in favor of the REALTIME dialplan function.\n");
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_ERROR,"Invalid input: usage %s\n",UUSAGE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
family = ast_strdupa(data);
|
||||
if ((colmatch = strchr(family,'|'))) {
|
||||
crop_data(colmatch);
|
||||
if ((value = strchr(colmatch,'|'))) {
|
||||
crop_data(value);
|
||||
if ((newcol = strchr(value,'|'))) {
|
||||
crop_data(newcol);
|
||||
if ((newval = strchr(newcol,'|')))
|
||||
crop_data(newval);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! (family && value && colmatch && newcol && newval) ) {
|
||||
ast_log(LOG_ERROR,"Invalid input: usage %s\n",UUSAGE);
|
||||
res = -1;
|
||||
} else {
|
||||
count = ast_update_realtime(family,colmatch,value,newcol,newval,NULL);
|
||||
}
|
||||
|
||||
snprintf(countc, sizeof(countc), "%d", count);
|
||||
pbx_builtin_setvar_helper(chan, "REALTIMECOUNT", countc);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static int realtime_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res=0, count=0;
|
||||
struct ast_module_user *u;
|
||||
struct ast_variable *var, *itt;
|
||||
char *family=NULL, *colmatch=NULL, *value=NULL, *prefix=NULL, *vname=NULL;
|
||||
char countc[13];
|
||||
size_t len;
|
||||
|
||||
ast_log(LOG_WARNING, "The RealTime application has been deprecated in favor of the REALTIME dialplan function.\n");
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_ERROR,"Invalid input: usage %s\n",USAGE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
family = ast_strdupa(data);
|
||||
if ((colmatch = strchr(family,'|'))) {
|
||||
crop_data(colmatch);
|
||||
if ((value = strchr(colmatch,'|'))) {
|
||||
crop_data(value);
|
||||
if ((prefix = strchr(value,'|')))
|
||||
crop_data(prefix);
|
||||
}
|
||||
}
|
||||
if (! (family && value && colmatch) ) {
|
||||
ast_log(LOG_ERROR,"Invalid input: usage %s\n",USAGE);
|
||||
res = -1;
|
||||
} else {
|
||||
if (option_verbose > 3)
|
||||
ast_verbose(VERBOSE_PREFIX_4"Realtime Lookup: family:'%s' colmatch:'%s' value:'%s'\n",family,colmatch,value);
|
||||
if ((var = ast_load_realtime(family, colmatch, value, NULL))) {
|
||||
for (itt = var; itt; itt = itt->next) {
|
||||
if(prefix) {
|
||||
len = strlen(prefix) + strlen(itt->name) + 2;
|
||||
vname = alloca(len);
|
||||
snprintf(vname,len,"%s%s",prefix,itt->name);
|
||||
|
||||
} else
|
||||
vname = itt->name;
|
||||
|
||||
pbx_builtin_setvar_helper(chan, vname, itt->value);
|
||||
count++;
|
||||
}
|
||||
ast_variables_destroy(var);
|
||||
} else if (option_verbose > 3)
|
||||
ast_verbose(VERBOSE_PREFIX_4"No Realtime Matches Found.\n");
|
||||
}
|
||||
snprintf(countc, sizeof(countc), "%d", count);
|
||||
pbx_builtin_setvar_helper(chan, "REALTIMECOUNT", countc);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_cli_unregister_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry));
|
||||
res = ast_unregister_application(uapp);
|
||||
res |= ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_cli_register_multiple(cli_realtime, sizeof(cli_realtime) / sizeof(struct ast_cli_entry));
|
||||
res = ast_register_application(uapp, realtime_update_exec, usynopsis, udesc);
|
||||
res |= ast_register_application(app, realtime_exec, synopsis, desc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Data Lookup/Rewrite");
|
||||
@@ -1,92 +1,67 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Trivial application to record a sound file
|
||||
*
|
||||
* Copyright (C) 2001, Linux Support Services, Inc.
|
||||
*
|
||||
* Matthew Fredrickson <creslin@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Matthew Fredrickson <creslin@linux-support.net>
|
||||
*
|
||||
* 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 Trivial application to record a sound file
|
||||
*
|
||||
* \author Matthew Fredrickson <creslin@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/dsp.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/dsp.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/app.h"
|
||||
|
||||
static char *tdesc = "Trivial Record Application";
|
||||
|
||||
static char *app = "Record";
|
||||
|
||||
static char *synopsis = "Record to a file";
|
||||
|
||||
static char *descrip =
|
||||
" Record(filename.format|silence[|maxduration][|options])\n\n"
|
||||
" Record(filename:format|silence[|maxduration][|option])\n\n"
|
||||
"Records from the channel into a given filename. If the file exists it will\n"
|
||||
"be overwritten.\n"
|
||||
"- 'format' is the format of the file type to be recorded (wav, gsm, etc).\n"
|
||||
"- 'silence' is the number of seconds of silence to allow before returning.\n"
|
||||
"- 'maxduration' is the maximum recording duration in seconds. If missing\n"
|
||||
"or 0 there is no maximum.\n"
|
||||
"- 'options' may contain any of the following letters:\n"
|
||||
" 'a' : append to existing recording rather than replacing\n"
|
||||
" 'n' : do not answer, but record anyway if line not yet answered\n"
|
||||
" 'q' : quiet (do not play a beep tone)\n"
|
||||
" 's' : skip recording if the line is not yet answered\n"
|
||||
" 't' : use alternate '*' terminator key (DTMF) instead of default '#'\n"
|
||||
" 'x' : ignore all terminator keys (DTMF) and keep recording until hangup\n"
|
||||
"\n"
|
||||
"- 'option' may be 'skip' to return immediately if the line is not up,\n"
|
||||
"or 'noanswer' to attempt to record even if the line is not up.\n\n"
|
||||
"If filename contains '%d', these characters will be replaced with a number\n"
|
||||
"incremented by one each time the file is recorded. A channel variable\n"
|
||||
"named RECORDED_FILE will also be set, which contains the final filemname.\n\n"
|
||||
"Use 'core show file formats' to see the available formats on your system\n\n"
|
||||
"incremented by one each time the file is recorded. \n\n"
|
||||
"Formats: g723, g729, gsm, h263, ulaw, alaw, vox, wav, WAV\n\n"
|
||||
"User can press '#' to terminate the recording and continue to the next priority.\n\n"
|
||||
"If the user should hangup during a recording, all data will be lost and the\n"
|
||||
"application will teminate. \n";
|
||||
"Returns -1 when the user hangs up.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int record_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
int count = 0;
|
||||
int percentflag = 0;
|
||||
char *filename, *ext = NULL, *silstr, *maxstr, *options;
|
||||
char *vdata, *p;
|
||||
int i = 0;
|
||||
char fil[256];
|
||||
char tmp[256];
|
||||
char ext[10];
|
||||
char *vdata;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
struct ast_filestream *s = '\0';
|
||||
struct ast_module_user *u;
|
||||
struct localuser *u;
|
||||
struct ast_frame *f = NULL;
|
||||
|
||||
struct ast_dsp *sildet = NULL; /* silence detector dsp */
|
||||
@@ -94,134 +69,124 @@ static int record_exec(struct ast_channel *chan, void *data)
|
||||
int dspsilence = 0;
|
||||
int silence = 0; /* amount of silence to allow */
|
||||
int gotsilence = 0; /* did we timeout for silence? */
|
||||
int maxduration = 0; /* max duration of recording in milliseconds */
|
||||
char silencestr[5];
|
||||
char durationstr[8];
|
||||
int maxduration = 0; /* max duration of recording */
|
||||
int gottimeout = 0; /* did we timeout for maxduration exceeded? */
|
||||
time_t timeout = 0;
|
||||
char option[16];
|
||||
int option_skip = 0;
|
||||
int option_noanswer = 0;
|
||||
int option_append = 0;
|
||||
int terminator = '#';
|
||||
int option_quiet = 0;
|
||||
int rfmt = 0;
|
||||
int flags;
|
||||
int waitres;
|
||||
struct ast_silence_generator *silgen = NULL;
|
||||
|
||||
char *end=NULL;
|
||||
char *p=NULL;
|
||||
|
||||
|
||||
|
||||
/* The next few lines of code parse out the filename and header from the input string */
|
||||
if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
|
||||
if (!data) { /* no data implies no filename or anything is present */
|
||||
ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
/* Yay for strsep being easy */
|
||||
vdata = ast_strdupa(data);
|
||||
|
||||
p = vdata;
|
||||
filename = strsep(&p, "|");
|
||||
silstr = strsep(&p, "|");
|
||||
maxstr = strsep(&p, "|");
|
||||
options = strsep(&p, "|");
|
||||
|
||||
if (filename) {
|
||||
if (strstr(filename, "%d"))
|
||||
percentflag = 1;
|
||||
ext = strrchr(filename, '.'); /* to support filename with a . in the filename, not format */
|
||||
if (!ext)
|
||||
ext = strchr(filename, ':');
|
||||
if (ext) {
|
||||
*ext = '\0';
|
||||
ext++;
|
||||
p = vdata;
|
||||
while(p && (p=strchr(p,':'))) {
|
||||
end=p;
|
||||
if(!strcasecmp(end,":end")) {
|
||||
*end='\0';
|
||||
end++;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
end=NULL;
|
||||
}
|
||||
if (!ext) {
|
||||
ast_log(LOG_WARNING, "No extension specified to filename!\n");
|
||||
ast_module_user_remove(u);
|
||||
|
||||
|
||||
for (; vdata[i] && (vdata[i] != ':') && (vdata[i] != '|'); i++ ) {
|
||||
if ((vdata[i] == '%') && (vdata[i+1] == 'd')) {
|
||||
percentflag = 1; /* the wildcard is used */
|
||||
}
|
||||
if (j < sizeof(fil) - 1)
|
||||
fil[j++] = vdata[i];
|
||||
}
|
||||
fil[j] = '\0';
|
||||
|
||||
if (vdata[i] != ':') {
|
||||
ast_log(LOG_WARNING, "No extension found\n");
|
||||
return -1;
|
||||
}
|
||||
if (silstr) {
|
||||
if ((sscanf(silstr, "%30d", &i) == 1) && (i > -1)) {
|
||||
silence = i * 1000;
|
||||
} else if (!ast_strlen_zero(silstr)) {
|
||||
ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", silstr);
|
||||
}
|
||||
i++;
|
||||
|
||||
j = 0;
|
||||
for (; vdata[i] && (vdata[i] != '|'); i++)
|
||||
if (j < sizeof(ext) - 1)
|
||||
ext[j++] = vdata[i];
|
||||
ext[j] = '\0';
|
||||
|
||||
if (vdata[i] == '|')
|
||||
i++;
|
||||
|
||||
j = 0;
|
||||
for (; vdata[i] && (vdata[i] != '|'); i++)
|
||||
if (j < sizeof(silencestr) - 1)
|
||||
silencestr[j++] = vdata[i];
|
||||
silencestr[j] = '\0';
|
||||
|
||||
if (j > 0) {
|
||||
silence = atoi(silencestr);
|
||||
if (silence > 0)
|
||||
silence *= 1000;
|
||||
}
|
||||
|
||||
if (maxstr) {
|
||||
if ((sscanf(maxstr, "%30d", &i) == 1) && (i > -1))
|
||||
/* Convert duration to milliseconds */
|
||||
maxduration = i * 1000;
|
||||
else if (!ast_strlen_zero(maxstr))
|
||||
ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", maxstr);
|
||||
}
|
||||
if (options) {
|
||||
/* Retain backwards compatibility with old style options */
|
||||
if (!strcasecmp(options, "skip"))
|
||||
option_skip = 1;
|
||||
else if (!strcasecmp(options, "noanswer"))
|
||||
option_noanswer = 1;
|
||||
else {
|
||||
if (strchr(options, 's'))
|
||||
option_skip = 1;
|
||||
if (strchr(options, 'n'))
|
||||
option_noanswer = 1;
|
||||
if (strchr(options, 'a'))
|
||||
option_append = 1;
|
||||
if (strchr(options, 't'))
|
||||
terminator = '*';
|
||||
if (strchr(options, 'x'))
|
||||
terminator = 0;
|
||||
if (strchr(options, 'q'))
|
||||
option_quiet = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (vdata[i] == '|')
|
||||
i++;
|
||||
|
||||
j = 0;
|
||||
for (; vdata[i] && (vdata[i] != '|'); i++)
|
||||
if (j < sizeof(durationstr) - 1)
|
||||
durationstr[j++] = vdata[i];
|
||||
durationstr[j] = '\0';
|
||||
|
||||
if (j > 0)
|
||||
maxduration = atoi(durationstr);
|
||||
|
||||
if (vdata[i] == '|')
|
||||
i++;
|
||||
|
||||
j = 0;
|
||||
for (; vdata[i] && (vdata[i] != '|'); i++)
|
||||
if (j < sizeof(option) - 1)
|
||||
option[j++] = vdata[i];
|
||||
option[j] = '\0';
|
||||
|
||||
if (!strcasecmp(option, "skip"))
|
||||
option_skip = 1;
|
||||
if (!strcasecmp(option, "noanswer"))
|
||||
option_noanswer = 1;
|
||||
|
||||
/* done parsing */
|
||||
|
||||
|
||||
/* these are to allow the use of the %d in the config file for a wild card of sort to
|
||||
create a new file with the inputed name scheme */
|
||||
if (percentflag) {
|
||||
AST_DECLARE_APP_ARGS(fname,
|
||||
AST_APP_ARG(piece)[100];
|
||||
);
|
||||
char *tmp2 = ast_strdupa(filename);
|
||||
char countstring[15];
|
||||
int i;
|
||||
|
||||
/* Separate each piece out by the format specifier */
|
||||
AST_NONSTANDARD_APP_ARGS(fname, tmp2, '%');
|
||||
do {
|
||||
int tmplen;
|
||||
/* First piece has no leading percent, so it's copied verbatim */
|
||||
ast_copy_string(tmp, fname.piece[0], sizeof(tmp));
|
||||
tmplen = strlen(tmp);
|
||||
for (i = 1; i < fname.argc; i++) {
|
||||
if (fname.piece[i][0] == 'd') {
|
||||
/* Substitute the count */
|
||||
snprintf(countstring, sizeof(countstring), "%d", count);
|
||||
ast_copy_string(tmp + tmplen, countstring, sizeof(tmp) - tmplen);
|
||||
tmplen += strlen(countstring);
|
||||
} else if (tmplen + 2 < sizeof(tmp)) {
|
||||
/* Unknown format specifier - just copy it verbatim */
|
||||
tmp[tmplen++] = '%';
|
||||
tmp[tmplen++] = fname.piece[i][0];
|
||||
}
|
||||
/* Copy the remaining portion of the piece */
|
||||
ast_copy_string(tmp + tmplen, &(fname.piece[i][1]), sizeof(tmp) - tmplen);
|
||||
}
|
||||
snprintf(tmp, sizeof(tmp), fil, count);
|
||||
count++;
|
||||
} while (ast_fileexists(tmp, ext, chan->language) > 0);
|
||||
} while ( ast_fileexists(tmp, ext, chan->language) != -1 );
|
||||
pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
|
||||
} else
|
||||
ast_copy_string(tmp, filename, sizeof(tmp));
|
||||
strncpy(tmp, fil, sizeof(tmp)-1);
|
||||
/* end of routine mentioned */
|
||||
|
||||
|
||||
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
if (option_skip) {
|
||||
/* At the user's option, skip if the line is not up */
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return 0;
|
||||
} else if (!option_noanswer) {
|
||||
/* Otherwise answer unless we're supposed to record while on-hook */
|
||||
@@ -229,12 +194,7 @@ static int record_exec(struct ast_channel *chan, void *data)
|
||||
}
|
||||
}
|
||||
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!option_quiet) {
|
||||
if (!res) {
|
||||
/* Some code to play a nice little beep to signify the start of the record operation */
|
||||
res = ast_streamfile(chan, "beep", chan->language);
|
||||
if (!res) {
|
||||
@@ -243,144 +203,138 @@ static int record_exec(struct ast_channel *chan, void *data)
|
||||
ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
|
||||
}
|
||||
ast_stopstream(chan);
|
||||
}
|
||||
|
||||
/* The end of beep code. Now the recording starts */
|
||||
|
||||
if (silence > 0) {
|
||||
rfmt = chan->readformat;
|
||||
res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
sildet = ast_dsp_new();
|
||||
if (!sildet) {
|
||||
ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
|
||||
ast_module_user_remove(u);
|
||||
return -1;
|
||||
}
|
||||
ast_dsp_set_threshold(sildet, 256);
|
||||
}
|
||||
|
||||
|
||||
flags = option_append ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
|
||||
s = ast_writefile( tmp, ext, NULL, flags , 0, 0644);
|
||||
|
||||
if (!s) {
|
||||
ast_log(LOG_WARNING, "Could not create file %s\n", filename);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ast_opt_transmit_silence)
|
||||
silgen = ast_channel_start_silence_generator(chan);
|
||||
|
||||
/* Request a video update */
|
||||
ast_indicate(chan, AST_CONTROL_VIDUPDATE);
|
||||
|
||||
if (maxduration <= 0)
|
||||
maxduration = -1;
|
||||
|
||||
while ((waitres = ast_waitfor(chan, maxduration)) > -1) {
|
||||
if (maxduration > 0) {
|
||||
if (waitres == 0) {
|
||||
gottimeout = 1;
|
||||
break;
|
||||
}
|
||||
maxduration = waitres;
|
||||
/* The end of beep code. Now the recording starts */
|
||||
|
||||
|
||||
if (silence > 0) {
|
||||
rfmt = chan->readformat;
|
||||
res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
|
||||
return -1;
|
||||
}
|
||||
sildet = ast_dsp_new();
|
||||
if (!sildet) {
|
||||
ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
|
||||
return -1;
|
||||
}
|
||||
ast_dsp_set_threshold(sildet, 256);
|
||||
}
|
||||
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
if (f->frametype == AST_FRAME_VOICE) {
|
||||
res = ast_writestream(s, f);
|
||||
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "Problem writing frame\n");
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
|
||||
if (silence > 0) {
|
||||
dspsilence = 0;
|
||||
ast_dsp_silence(sildet, f, &dspsilence);
|
||||
if (dspsilence) {
|
||||
totalsilence = dspsilence;
|
||||
} else {
|
||||
totalsilence = 0;
|
||||
}
|
||||
if (totalsilence > silence) {
|
||||
/* Ended happily with silence */
|
||||
ast_frfree(f);
|
||||
gotsilence = 1;
|
||||
|
||||
|
||||
flags = end ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
|
||||
s = ast_writefile( tmp, ext, NULL, flags , 0, 0644);
|
||||
|
||||
|
||||
if (s) {
|
||||
if (maxduration > 0)
|
||||
timeout = time(NULL) + (time_t)maxduration;
|
||||
|
||||
while (ast_waitfor(chan, -1) > -1) {
|
||||
if (maxduration > 0 && time(NULL) > timeout) {
|
||||
gottimeout = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (f->frametype == AST_FRAME_VIDEO) {
|
||||
res = ast_writestream(s, f);
|
||||
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "Problem writing frame\n");
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
} else if ((f->frametype == AST_FRAME_DTMF) &&
|
||||
(f->subclass == terminator)) {
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
if (!f) {
|
||||
ast_log(LOG_DEBUG, "Got hangup\n");
|
||||
res = -1;
|
||||
}
|
||||
|
||||
if (gotsilence) {
|
||||
ast_stream_rewind(s, silence-1000);
|
||||
ast_truncstream(s);
|
||||
} else if (!gottimeout) {
|
||||
/* Strip off the last 1/4 second of it */
|
||||
ast_stream_rewind(s, 250);
|
||||
ast_truncstream(s);
|
||||
}
|
||||
ast_closestream(s);
|
||||
|
||||
if (silgen)
|
||||
ast_channel_stop_silence_generator(chan, silgen);
|
||||
|
||||
out:
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
if (f->frametype == AST_FRAME_VOICE) {
|
||||
res = ast_writestream(s, f);
|
||||
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "Problem writing frame\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (silence > 0) {
|
||||
dspsilence = 0;
|
||||
ast_dsp_silence(sildet, f, &dspsilence);
|
||||
if (dspsilence) {
|
||||
totalsilence = dspsilence;
|
||||
} else {
|
||||
totalsilence = 0;
|
||||
}
|
||||
if (totalsilence > silence) {
|
||||
/* Ended happily with silence */
|
||||
ast_frfree(f);
|
||||
gotsilence = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (f->frametype == AST_FRAME_VIDEO) {
|
||||
res = ast_writestream(s, f);
|
||||
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "Problem writing frame\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((f->frametype == AST_FRAME_DTMF) &&
|
||||
(f->subclass == '#')) {
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
if (!f) {
|
||||
ast_log(LOG_DEBUG, "Got hangup\n");
|
||||
res = -1;
|
||||
}
|
||||
|
||||
if (gotsilence) {
|
||||
ast_stream_rewind(s, silence-1000);
|
||||
ast_truncstream(s);
|
||||
} else if (!gottimeout) {
|
||||
/* Strip off the last 1/4 second of it */
|
||||
ast_stream_rewind(s, 250);
|
||||
ast_truncstream(s);
|
||||
}
|
||||
ast_closestream(s);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Could not create file %s\n", fil);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
if ((silence > 0) && rfmt) {
|
||||
res = ast_set_read_format(chan, rfmt);
|
||||
if (res)
|
||||
ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
|
||||
res = ast_set_read_format(chan, rfmt);
|
||||
if (res)
|
||||
ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
|
||||
if (sildet)
|
||||
ast_dsp_free(sildet);
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, record_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial Record Application");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
10654
apps/app_rpt.c
10654
apps/app_rpt.c
File diff suppressed because it is too large
Load Diff
@@ -1,47 +1,30 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (c) 2003, 2006 Tilghman Lesher. All rights reserved.
|
||||
* Copyright (c) 2006 Digium, Inc.
|
||||
* SayUnixTime application
|
||||
*
|
||||
* Copyright (c) 2003 Tilghman Lesher. All rights reserved.
|
||||
*
|
||||
* Tilghman Lesher <app_sayunixtime__200309@the-tilghman.com>
|
||||
*
|
||||
* This code is released by the author with no restrictions on usage.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief SayUnixTime application
|
||||
*
|
||||
* \author Tilghman Lesher <app_sayunixtime__200309@the-tilghman.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/say.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/say.h"
|
||||
#include "asterisk/app.h"
|
||||
|
||||
static char *tdesc = "Say time";
|
||||
|
||||
static char *app_sayunixtime = "SayUnixTime";
|
||||
static char *app_datetime = "DateTime";
|
||||
@@ -55,7 +38,8 @@ static char *sayunixtime_descrip =
|
||||
" timezone: timezone, see /usr/share/zoneinfo for a list.\n"
|
||||
" defaults to machine default.\n"
|
||||
" format: a format the time is to be said in. See voicemail.conf.\n"
|
||||
" defaults to \"ABdY 'digits/at' IMp\"\n";
|
||||
" defaults to \"ABdY 'digits/at' IMp\"\n"
|
||||
" Returns 0 or -1 on hangup.\n";
|
||||
static char *datetime_descrip =
|
||||
"DateTime([unixtime][|[timezone][|format]])\n"
|
||||
" unixtime: time, in seconds since Jan 1, 1970. May be negative.\n"
|
||||
@@ -63,64 +47,95 @@ static char *datetime_descrip =
|
||||
" timezone: timezone, see /usr/share/zoneinfo for a list.\n"
|
||||
" defaults to machine default.\n"
|
||||
" format: a format the time is to be said in. See voicemail.conf.\n"
|
||||
" defaults to \"ABdY 'digits/at' IMp\"\n";
|
||||
" defaults to \"ABdY 'digits/at' IMp\"\n"
|
||||
" Returns 0 or -1 on hangup.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int sayunixtime_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(timeval);
|
||||
AST_APP_ARG(timezone);
|
||||
AST_APP_ARG(format);
|
||||
);
|
||||
char *parse;
|
||||
int res = 0;
|
||||
struct ast_module_user *u;
|
||||
int res=0;
|
||||
struct localuser *u;
|
||||
char *s,*zone=NULL,*timec;
|
||||
time_t unixtime;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
char *format = "ABdY 'digits/at' IMp";
|
||||
struct timeval tv;
|
||||
|
||||
parse = ast_strdupa(data);
|
||||
LOCAL_USER_ADD(u);
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
gettimeofday(&tv,NULL);
|
||||
unixtime = (time_t)tv.tv_sec;
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
if (data) {
|
||||
s = data;
|
||||
s = ast_strdupa(s);
|
||||
if (s) {
|
||||
timec = strsep(&s,"|");
|
||||
if ((timec) && (*timec != '\0')) {
|
||||
long timein;
|
||||
if (sscanf(timec,"%ld",&timein) == 1) {
|
||||
unixtime = (time_t)timein;
|
||||
}
|
||||
}
|
||||
if (s) {
|
||||
zone = strsep(&s,"|");
|
||||
if (zone && (*zone == '\0'))
|
||||
zone = NULL;
|
||||
if (s) {
|
||||
format = s;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Out of memory error\n");
|
||||
}
|
||||
}
|
||||
|
||||
ast_get_time_t(args.timeval, &unixtime, time(NULL), NULL);
|
||||
|
||||
if (chan->_state != AST_STATE_UP)
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
res = ast_answer(chan);
|
||||
|
||||
}
|
||||
if (!res)
|
||||
res = ast_say_date_with_format(chan, unixtime, AST_DIGIT_ANY,
|
||||
chan->language, args.format, args.timezone);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
res = ast_say_date_with_format(chan, unixtime, AST_DIGIT_ANY, chan->language, format, zone);
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
res = ast_unregister_application(app_sayunixtime);
|
||||
res |= ast_unregister_application(app_datetime);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
if (! res)
|
||||
return ast_unregister_application(app_datetime);
|
||||
else
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application(app_sayunixtime, sayunixtime_exec, sayunixtime_synopsis, sayunixtime_descrip);
|
||||
res |= ast_register_application(app_datetime, sayunixtime_exec, sayunixtime_synopsis, datetime_descrip);
|
||||
|
||||
if (! res)
|
||||
return ast_register_application(app_datetime, sayunixtime_exec, sayunixtime_synopsis, datetime_descrip);
|
||||
else
|
||||
return res;
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Say time");
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,143 +1,84 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* App to send DTMF digits
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief App to send DTMF digits
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/options.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <asterisk/app.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/manager.h"
|
||||
static char *tdesc = "Send DTMF digits Application";
|
||||
|
||||
static char *app = "SendDTMF";
|
||||
|
||||
static char *synopsis = "Sends arbitrary DTMF digits";
|
||||
|
||||
static char *descrip =
|
||||
" SendDTMF(digits[|timeout_ms]): Sends DTMF digits on a channel. \n"
|
||||
" Accepted digits: 0-9, *#abcd, w (.5s pause)\n"
|
||||
" The application will either pass the assigned digits or terminate if it\n"
|
||||
" encounters an error.\n";
|
||||
" SendDTMF(digits): Sends DTMF digits on a channel. \n"
|
||||
" Accepted digits: 0-9, *#abcd\n"
|
||||
" Returns 0 on success or -1 on a hangup.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int senddtmf_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_module_user *u;
|
||||
char *digits = NULL, *to = NULL;
|
||||
int timeout = 250;
|
||||
struct localuser *u;
|
||||
char *digits = data;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
if (!digits || ast_strlen_zero(digits)) {
|
||||
ast_log(LOG_WARNING, "SendDTMF requires an argument (digits or *#aAbBcCdD)\n");
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
digits = ast_strdupa(data);
|
||||
|
||||
if ((to = strchr(digits,'|'))) {
|
||||
*to = '\0';
|
||||
to++;
|
||||
timeout = atoi(to);
|
||||
}
|
||||
|
||||
if (timeout <= 0)
|
||||
timeout = 250;
|
||||
|
||||
res = ast_dtmf_stream(chan,NULL,digits,timeout);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
res = ast_dtmf_stream(chan,NULL,digits,250);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static char mandescr_playdtmf[] =
|
||||
"Description: Plays a dtmf digit on the specified channel.\n"
|
||||
"Variables: (all are required)\n"
|
||||
" Channel: Channel name to send digit to\n"
|
||||
" Digit: The dtmf digit to play\n";
|
||||
|
||||
static int manager_play_dtmf(struct mansession *s, const struct message *m)
|
||||
int unload_module(void)
|
||||
{
|
||||
const char *channel = astman_get_header(m, "Channel");
|
||||
const char *digit = astman_get_header(m, "Digit");
|
||||
struct ast_channel *chan = ast_get_channel_by_name_locked(channel);
|
||||
|
||||
if (!chan) {
|
||||
astman_send_error(s, m, "Channel not specified");
|
||||
return 0;
|
||||
}
|
||||
if (ast_strlen_zero(digit)) {
|
||||
astman_send_error(s, m, "No digit specified");
|
||||
ast_mutex_unlock(&chan->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_senddigit(chan, *digit);
|
||||
|
||||
ast_mutex_unlock(&chan->lock);
|
||||
astman_send_ack(s, m, "DTMF successfully queued");
|
||||
|
||||
return 0;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
res |= ast_manager_unregister("PlayDTMF");
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
return ast_register_application(app, senddtmf_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_manager_register2( "PlayDTMF", EVENT_FLAG_CALL, manager_play_dtmf, "Play DTMF signal on a specific channel.", mandescr_playdtmf );
|
||||
res |= ast_register_application(app, senddtmf_exec, synopsis, descrip);
|
||||
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send DTMF digits Application");
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,133 +1,94 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* App to transmit a text message
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief App to transmit a text message
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \note Requires support of sending text messages from channel driver
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/channel_pvt.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/image.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/image.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/app.h"
|
||||
static char *tdesc = "Send Text Applications";
|
||||
|
||||
static const char *app = "SendText";
|
||||
static char *app = "SendText";
|
||||
|
||||
static const char *synopsis = "Send a Text Message";
|
||||
static char *synopsis = "Send a Text Message";
|
||||
|
||||
static const char *descrip =
|
||||
" SendText(text[|options]): Sends text to current channel (callee).\n"
|
||||
"Result of transmission will be stored in the SENDTEXTSTATUS\n"
|
||||
"channel variable:\n"
|
||||
" SUCCESS Transmission succeeded\n"
|
||||
" FAILURE Transmission failed\n"
|
||||
" UNSUPPORTED Text transmission not supported by channel\n"
|
||||
"\n"
|
||||
"At this moment, text is supposed to be 7 bit ASCII in most channels.\n"
|
||||
"The option string many contain the following character:\n"
|
||||
"'j' -- jump to n+101 priority if the channel doesn't support\n"
|
||||
" text transport\n";
|
||||
static char *descrip =
|
||||
" SendText(text): Sends text to client. If the client\n"
|
||||
"does not support text transport, and there exists a step with\n"
|
||||
"priority n + 101, then execution will continue at that step.\n"
|
||||
"Otherwise, execution will continue at the next priority level.\n"
|
||||
"SendText only returns 0 if the text was sent correctly or if\n"
|
||||
"the channel does not support text transport, and -1 otherwise.\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int sendtext_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_module_user *u;
|
||||
char *status = "UNSUPPORTED";
|
||||
char *parse = NULL;
|
||||
int priority_jump = 0;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(text);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
/* NOT ast_strlen_zero, because some protocols (e.g. SIP) MUST be able to
|
||||
* send a zero-length message. */
|
||||
if (!data) {
|
||||
ast_log(LOG_WARNING, "SendText requires an argument (text[|options])\n");
|
||||
ast_module_user_remove(u);
|
||||
struct localuser *u;
|
||||
if (!data || !strlen((char *)data)) {
|
||||
ast_log(LOG_WARNING, "SendText requires an argument (text)\n");
|
||||
return -1;
|
||||
} else
|
||||
parse = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (args.options) {
|
||||
if (strchr(args.options, 'j'))
|
||||
priority_jump = 1;
|
||||
}
|
||||
|
||||
ast_channel_lock(chan);
|
||||
if (!chan->tech->send_text) {
|
||||
ast_channel_unlock(chan);
|
||||
pbx_builtin_setvar_helper(chan, "SENDTEXTSTATUS", status);
|
||||
LOCAL_USER_ADD(u);
|
||||
ast_mutex_lock(&chan->lock);
|
||||
if (!chan->pvt->send_text) {
|
||||
ast_mutex_unlock(&chan->lock);
|
||||
/* Does not support transport */
|
||||
if (priority_jump || ast_opt_priority_jumping)
|
||||
ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
|
||||
ast_module_user_remove(u);
|
||||
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
|
||||
chan->priority += 100;
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return 0;
|
||||
}
|
||||
status = "FAILURE";
|
||||
res = ast_sendtext(chan, args.text);
|
||||
if (!res)
|
||||
status = "SUCCESS";
|
||||
ast_channel_unlock(chan);
|
||||
pbx_builtin_setvar_helper(chan, "SENDTEXTSTATUS", status);
|
||||
ast_module_user_remove(u);
|
||||
return 0;
|
||||
ast_mutex_unlock(&chan->lock);
|
||||
res = ast_sendtext(chan, (char *)data);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, sendtext_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send Text Applications");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
@@ -1,56 +1,55 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* App to set callerid
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief App to set callerid
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/image.h>
|
||||
#include <asterisk/callerid.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/image.h"
|
||||
#include "asterisk/callerid.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
static char *app2 = "SetCallerPres";
|
||||
|
||||
static char *synopsis2 = "Set CallerID Presentation";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static struct {
|
||||
int val;
|
||||
char *name;
|
||||
} preses[] = {
|
||||
{ AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED, "allowed_not_screened" },
|
||||
{ AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, "allowed_passed_screen" },
|
||||
{ AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN, "allowed_failed_screen" },
|
||||
{ AST_PRES_ALLOWED_NETWORK_NUMBER, "allowed" },
|
||||
{ AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED , "prohib_not_screened" },
|
||||
{ AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN, "prohib_passed_screen" },
|
||||
{ AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN, "prohib_failed_screen" },
|
||||
{ AST_PRES_PROHIB_NETWORK_NUMBER, "prohib" },
|
||||
{ AST_PRES_NUMBER_NOT_AVAILABLE, "unavailable" },
|
||||
};
|
||||
|
||||
static char *descrip2 =
|
||||
" SetCallerPres(presentation): Set Caller*ID presentation on a call.\n"
|
||||
" Valid presentations are:\n"
|
||||
" SetCallerPres(presentation): Set Caller*ID presentation on\n"
|
||||
"a call to a new value. Sets ANI as well if a flag is used.\n"
|
||||
"Always returns 0. Valid presentations are:\n"
|
||||
"\n"
|
||||
" allowed_not_screened : Presentation Allowed, Not Screened\n"
|
||||
" allowed_passed_screen : Presentation Allowed, Passed Screen\n"
|
||||
@@ -66,61 +65,56 @@ static char *descrip2 =
|
||||
|
||||
static int setcallerid_pres_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_module_user *u;
|
||||
int res = 0;
|
||||
char tmp[256] = "";
|
||||
struct localuser *u;
|
||||
int x;
|
||||
char *opts;
|
||||
int pres = -1;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
/* For interface consistency, permit the argument to be specified as a number */
|
||||
if (sscanf(data, "%30d", &pres) != 1 || pres < 0 || pres > 255 || (pres & 0x9c)) {
|
||||
pres = ast_parse_caller_presentation(data);
|
||||
if (data)
|
||||
strncpy(tmp, (char *)data, sizeof(tmp) - 1);
|
||||
opts = strchr(tmp, '|');
|
||||
if (opts) {
|
||||
*opts = '\0';
|
||||
opts++;
|
||||
}
|
||||
for (x=0;x<sizeof(preses) / sizeof(preses[0]);x++) {
|
||||
if (!strcasecmp(preses[x].name, tmp)) {
|
||||
pres = preses[x].val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pres < 0) {
|
||||
ast_log(LOG_WARNING, "'%s' is not a valid presentation (see 'show application SetCallerPres')\n",
|
||||
(char *) data);
|
||||
ast_module_user_remove(u);
|
||||
ast_log(LOG_WARNING, "'%s' is not a valid presentation (see 'show application SetCallerPres')\n", tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
chan->cid.cid_pres = pres;
|
||||
ast_module_user_remove(u);
|
||||
return 0;
|
||||
LOCAL_USER_ADD(u);
|
||||
chan->callingpres = pres;
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static char *tdesc = "Set CallerID Application";
|
||||
|
||||
static char *app = "SetCallerID";
|
||||
|
||||
static char *synopsis = "Set CallerID";
|
||||
|
||||
static char *descrip =
|
||||
" SetCallerID(clid[|a]): Set Caller*ID on a call to a new\n"
|
||||
"value. Sets ANI as well if a flag is used. \n";
|
||||
"value. Sets ANI as well if a flag is used. Always returns 0\n";
|
||||
|
||||
static int setcallerid_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
char *tmp = NULL;
|
||||
char name[256];
|
||||
char num[256];
|
||||
struct ast_module_user *u;
|
||||
char tmp[256] = "";
|
||||
struct localuser *u;
|
||||
char *opt;
|
||||
int anitoo = 0;
|
||||
static int dep_warning = 0;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "SetCallerID requires an argument!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (!dep_warning) {
|
||||
dep_warning = 1;
|
||||
ast_log(LOG_WARNING, "SetCallerID is deprecated. Please use Set(CALLERID(all)=...) or Set(CALLERID(ani)=...) instead.\n");
|
||||
}
|
||||
|
||||
tmp = ast_strdupa(data);
|
||||
|
||||
if (data)
|
||||
strncpy(tmp, (char *)data, sizeof(tmp) - 1);
|
||||
opt = strchr(tmp, '|');
|
||||
if (opt) {
|
||||
*opt = '\0';
|
||||
@@ -128,35 +122,38 @@ static int setcallerid_exec(struct ast_channel *chan, void *data)
|
||||
if (*opt == 'a')
|
||||
anitoo = 1;
|
||||
}
|
||||
|
||||
ast_callerid_split(tmp, name, sizeof(name), num, sizeof(num));
|
||||
ast_set_callerid(chan, num, name, anitoo ? num : NULL);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
ast_set_callerid(chan, strlen(tmp) ? tmp : NULL, anitoo);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
ast_unregister_application(app2);
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
ast_register_application(app2, setcallerid_pres_exec, synopsis2, descrip2);
|
||||
return ast_register_application(app, setcallerid_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app2);
|
||||
res |= ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
char *key()
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application(app2, setcallerid_pres_exec, synopsis2, descrip2);
|
||||
res |= ast_register_application(app, setcallerid_exec, synopsis, descrip);
|
||||
|
||||
return res;
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Set CallerID Application");
|
||||
|
||||
@@ -1,47 +1,30 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
* Applictions connected with CDR engine
|
||||
*
|
||||
* Copyright (C) 2003, Digium
|
||||
*
|
||||
* Justin Huff <jjhuff@mspin.net>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Applictions connected with CDR engine
|
||||
*
|
||||
* \author Justin Huff <jjhuff@mspin.net>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/cdr.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/config.h>
|
||||
#include <asterisk/manager.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/cdr.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/manager.h"
|
||||
#include "asterisk/utils.h"
|
||||
|
||||
static char *tdesc = "CDR user field apps";
|
||||
|
||||
static char *setcdruserfield_descrip =
|
||||
"[Synopsis]\n"
|
||||
@@ -53,7 +36,7 @@ static char *setcdruserfield_descrip =
|
||||
" CDR records can be used for billing or storing other arbitrary data\n"
|
||||
" (I.E. telephone survey responses)\n"
|
||||
" Also see AppendCDRUserField().\n"
|
||||
"\nThis application is deprecated in favor of Set(CDR(userfield)=...)\n";
|
||||
" Always returns 0\n";
|
||||
|
||||
|
||||
static char *setcdruserfield_app = "SetCDRUserField";
|
||||
@@ -69,18 +52,21 @@ static char *appendcdruserfield_descrip =
|
||||
" CDR records can be used for billing or storing other arbitrary data\n"
|
||||
" (I.E. telephone survey responses)\n"
|
||||
" Also see SetCDRUserField().\n"
|
||||
"\nThis application is deprecated in favor of Set(CDR(userfield)=...)\n";
|
||||
" Always returns 0\n";
|
||||
|
||||
static char *appendcdruserfield_app = "AppendCDRUserField";
|
||||
static char *appendcdruserfield_synopsis = "Append to the CDR user field";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
static int action_setcdruserfield(struct mansession *s, const struct message *m)
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int action_setcdruserfield(struct mansession *s, struct message *m)
|
||||
{
|
||||
struct ast_channel *c = NULL;
|
||||
const char *userfield = astman_get_header(m, "UserField");
|
||||
const char *channel = astman_get_header(m, "Channel");
|
||||
const char *append = astman_get_header(m, "Append");
|
||||
char *userfield = astman_get_header(m, "UserField");
|
||||
char *channel = astman_get_header(m, "Channel");
|
||||
char *append = astman_get_header(m, "Append");
|
||||
|
||||
if (ast_strlen_zero(channel)) {
|
||||
astman_send_error(s, m, "No Channel specified");
|
||||
@@ -90,7 +76,13 @@ static int action_setcdruserfield(struct mansession *s, const struct message *m)
|
||||
astman_send_error(s, m, "No UserField specified");
|
||||
return 0;
|
||||
}
|
||||
c = ast_get_channel_by_name_locked(channel);
|
||||
c = ast_channel_walk_locked(NULL);
|
||||
while (c) {
|
||||
if (!strcasecmp(c->name, channel))
|
||||
break;
|
||||
ast_mutex_unlock(&c->lock);
|
||||
c = ast_channel_walk_locked(c);
|
||||
}
|
||||
if (!c) {
|
||||
astman_send_error(s, m, "No such channel");
|
||||
return 0;
|
||||
@@ -106,70 +98,68 @@ static int action_setcdruserfield(struct mansession *s, const struct message *m)
|
||||
|
||||
static int setcdruserfield_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_module_user *u;
|
||||
struct localuser *u;
|
||||
int res = 0;
|
||||
static int dep_warning = 0;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (chan->cdr && data) {
|
||||
LOCAL_USER_ADD(u)
|
||||
if (chan->cdr && data)
|
||||
{
|
||||
ast_cdr_setuserfield(chan, (char*)data);
|
||||
}
|
||||
|
||||
if (!dep_warning) {
|
||||
dep_warning = 1;
|
||||
ast_log(LOG_WARNING, "SetCDRUserField is deprecated. Please use CDR(userfield) instead.\n");
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int appendcdruserfield_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_module_user *u;
|
||||
struct localuser *u;
|
||||
int res = 0;
|
||||
static int dep_warning = 0;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (chan->cdr && data) {
|
||||
LOCAL_USER_ADD(u)
|
||||
if (chan->cdr && data)
|
||||
{
|
||||
ast_cdr_appenduserfield(chan, (char*)data);
|
||||
}
|
||||
|
||||
if (!dep_warning) {
|
||||
dep_warning = 1;
|
||||
ast_log(LOG_WARNING, "AppendCDRUserField is deprecated. Please use CDR(userfield) instead.\n");
|
||||
}
|
||||
|
||||
ast_module_user_remove(u);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
res = ast_unregister_application(setcdruserfield_app);
|
||||
res |= ast_unregister_application(appendcdruserfield_app);
|
||||
res |= ast_manager_unregister("SetCDRUserField");
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
ast_manager_unregister("SetCDRUserField");
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application(setcdruserfield_app, setcdruserfield_exec, setcdruserfield_synopsis, setcdruserfield_descrip);
|
||||
res |= ast_register_application(appendcdruserfield_app, appendcdruserfield_exec, appendcdruserfield_synopsis, appendcdruserfield_descrip);
|
||||
res |= ast_manager_register("SetCDRUserField", EVENT_FLAG_CALL, action_setcdruserfield, "Set the CDR UserField");
|
||||
|
||||
ast_manager_register("SetCDRUserField", EVENT_FLAG_CALL, action_setcdruserfield, "Set the CDR UserField");
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "CDR user field apps");
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
107
apps/app_setcidname.c
Normal file
107
apps/app_setcidname.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* App to set callerid
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/image.h>
|
||||
#include <asterisk/callerid.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static char *tdesc = "Set CallerID Name";
|
||||
|
||||
static char *app = "SetCIDName";
|
||||
|
||||
static char *synopsis = "Set CallerID Name";
|
||||
|
||||
static char *descrip =
|
||||
" SetCIDName(cname[|a]): Set Caller*ID Name on a call to a new\n"
|
||||
"value, while preserving the original Caller*ID number. This is\n"
|
||||
"useful for providing additional information to the called\n"
|
||||
"party. Sets ANI as well if a flag is used. Always returns 0\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int setcallerid_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
char tmp[256] = "";
|
||||
char oldcid[256] = "", *l, *n;
|
||||
char newcid[256] = "";
|
||||
struct localuser *u;
|
||||
char *opt;
|
||||
int anitoo = 0;
|
||||
if (data)
|
||||
strncpy(tmp, (char *)data, sizeof(tmp) - 1);
|
||||
opt = strchr(tmp, '|');
|
||||
if (opt) {
|
||||
*opt = '\0';
|
||||
opt++;
|
||||
if (*opt == 'a')
|
||||
anitoo = 1;
|
||||
}
|
||||
LOCAL_USER_ADD(u);
|
||||
if (chan->callerid) {
|
||||
strncpy(oldcid, chan->callerid, sizeof(oldcid) - 1);
|
||||
ast_callerid_parse(oldcid, &n, &l);
|
||||
n = tmp;
|
||||
if (!ast_strlen_zero(n)) {
|
||||
if (l && !ast_strlen_zero(l))
|
||||
snprintf(newcid, sizeof(newcid), "\"%s\" <%s>", n, l);
|
||||
else
|
||||
strncpy(newcid, tmp, sizeof(newcid) - 1);
|
||||
} else if (l && !ast_strlen_zero(l)) {
|
||||
strncpy(newcid, l, sizeof(newcid) - 1);
|
||||
}
|
||||
} else
|
||||
strncpy(newcid, tmp, sizeof(newcid) - 1);
|
||||
ast_set_callerid(chan, !ast_strlen_zero(newcid) ? newcid : NULL, anitoo);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, setcallerid_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
108
apps/app_setcidnum.c
Normal file
108
apps/app_setcidnum.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* App to set callerid
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
* Oliver Daudey <traveler@xs4all.nl>
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <asterisk/lock.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/translate.h>
|
||||
#include <asterisk/image.h>
|
||||
#include <asterisk/callerid.h>
|
||||
#include <asterisk/utils.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static char *tdesc = "Set CallerID Number";
|
||||
|
||||
static char *app = "SetCIDNum";
|
||||
|
||||
static char *synopsis = "Set CallerID Number";
|
||||
|
||||
static char *descrip =
|
||||
" SetCIDNum(cnum[|a]): Set Caller*ID Number on a call to a new\n"
|
||||
"value, while preserving the original Caller*ID name. This is\n"
|
||||
"useful for providing additional information to the called\n"
|
||||
"party. Sets ANI as well if a flag is used. Always returns 0\n";
|
||||
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int setcallerid_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
char tmp[256] = "";
|
||||
char oldcid[256] = "", *l, *n;
|
||||
char newcid[256] = "";
|
||||
struct localuser *u;
|
||||
char *opt;
|
||||
int anitoo = 0;
|
||||
if (data)
|
||||
strncpy(tmp, (char *)data, sizeof(tmp) - 1);
|
||||
opt = strchr(tmp, '|');
|
||||
if (opt) {
|
||||
*opt = '\0';
|
||||
opt++;
|
||||
if (*opt == 'a')
|
||||
anitoo = 1;
|
||||
}
|
||||
LOCAL_USER_ADD(u);
|
||||
if (chan->callerid) {
|
||||
strncpy(oldcid, chan->callerid, sizeof(oldcid) - 1);
|
||||
ast_callerid_parse(oldcid, &n, &l);
|
||||
l = tmp;
|
||||
if (!ast_strlen_zero(l)) {
|
||||
if (n && !ast_strlen_zero(n))
|
||||
snprintf(newcid, sizeof(newcid), "\"%s\" <%s>", n, l);
|
||||
else
|
||||
strncpy(newcid, tmp, sizeof(newcid) - 1);
|
||||
} else if (n && !ast_strlen_zero(n)) {
|
||||
strncpy(newcid, n, sizeof(newcid) - 1);
|
||||
}
|
||||
} else
|
||||
strncpy(newcid, tmp, sizeof(newcid) - 1);
|
||||
ast_set_callerid(chan, !ast_strlen_zero(newcid) ? newcid : NULL, anitoo);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, setcallerid_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *key()
|
||||
{
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2005, Frank Sautter, levigo holding gmbh, www.levigo.de
|
||||
*
|
||||
* Frank Sautter - asterisk+at+sautter+dot+com
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief App to set the ISDN Transfer Capability
|
||||
*
|
||||
* \author Frank Sautter - asterisk+at+sautter+dot+com
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/transcap.h"
|
||||
|
||||
|
||||
static char *app = "SetTransferCapability";
|
||||
|
||||
static char *synopsis = "Set ISDN Transfer Capability";
|
||||
|
||||
|
||||
static struct { int val; char *name; } transcaps[] = {
|
||||
{ AST_TRANS_CAP_SPEECH, "SPEECH" },
|
||||
{ AST_TRANS_CAP_DIGITAL, "DIGITAL" },
|
||||
{ AST_TRANS_CAP_RESTRICTED_DIGITAL, "RESTRICTED_DIGITAL" },
|
||||
{ AST_TRANS_CAP_3_1K_AUDIO, "3K1AUDIO" },
|
||||
{ AST_TRANS_CAP_DIGITAL_W_TONES, "DIGITAL_W_TONES" },
|
||||
{ AST_TRANS_CAP_VIDEO, "VIDEO" },
|
||||
};
|
||||
|
||||
static char *descrip =
|
||||
" SetTransferCapability(transfercapability): Set the ISDN Transfer \n"
|
||||
"Capability of a call to a new value.\n"
|
||||
"Valid Transfer Capabilities are:\n"
|
||||
"\n"
|
||||
" SPEECH : 0x00 - Speech (default, voice calls)\n"
|
||||
" DIGITAL : 0x08 - Unrestricted digital information (data calls)\n"
|
||||
" RESTRICTED_DIGITAL : 0x09 - Restricted digital information\n"
|
||||
" 3K1AUDIO : 0x10 - 3.1kHz Audio (fax calls)\n"
|
||||
" DIGITAL_W_TONES : 0x11 - Unrestricted digital information with tones/announcements\n"
|
||||
" VIDEO : 0x18 - Video\n"
|
||||
"\n"
|
||||
"This application is deprecated in favor of Set(CHANNEL(transfercapability)=...)\n"
|
||||
;
|
||||
|
||||
static int settransfercapability_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
char *tmp = NULL;
|
||||
struct ast_module_user *u;
|
||||
int x;
|
||||
char *opts;
|
||||
int transfercapability = -1;
|
||||
static int dep_warning = 0;
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
if (!dep_warning) {
|
||||
dep_warning = 1;
|
||||
ast_log(LOG_WARNING, "SetTransferCapability is deprecated. Please use CHANNEL(transfercapability) instead.\n");
|
||||
}
|
||||
|
||||
if (data)
|
||||
tmp = ast_strdupa(data);
|
||||
else
|
||||
tmp = "";
|
||||
|
||||
opts = strchr(tmp, '|');
|
||||
if (opts)
|
||||
*opts = '\0';
|
||||
|
||||
for (x = 0; x < (sizeof(transcaps) / sizeof(transcaps[0])); x++) {
|
||||
if (!strcasecmp(transcaps[x].name, tmp)) {
|
||||
transfercapability = transcaps[x].val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (transfercapability < 0) {
|
||||
ast_log(LOG_WARNING, "'%s' is not a valid transfer capability (see 'show application SetTransferCapability')\n", tmp);
|
||||
ast_module_user_remove(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
chan->transfercapability = (unsigned short)transfercapability;
|
||||
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Setting transfer capability to: 0x%.2x - %s.\n", transfercapability, tmp);
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
|
||||
ast_module_user_hangup_all();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, settransfercapability_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Set ISDN Transfer Capability");
|
||||
151
apps/app_skel.c
151
apps/app_skel.c
@@ -1,133 +1,74 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
* Asterisk -- A telephony toolkit for Linux.
|
||||
*
|
||||
* Copyright (C) <Year>, <Your Name Here>
|
||||
* Skeleton application
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* <Your Name Here> <<Your Email Here>>
|
||||
*
|
||||
* 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.
|
||||
* Mark Spencer <markster@linux-support.net>
|
||||
*
|
||||
* 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.
|
||||
* the GNU General Public License
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Skeleton application
|
||||
*
|
||||
* \author <Your Name Here> <<Your Email Here>>
|
||||
*
|
||||
* This is a skeleton for development of an Asterisk application
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<defaultenabled>no</defaultenabled>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include <stdio.h>
|
||||
#include <asterisk/file.h>
|
||||
#include <asterisk/logger.h>
|
||||
#include <asterisk/channel.h>
|
||||
#include <asterisk/pbx.h>
|
||||
#include <asterisk/module.h>
|
||||
#include <asterisk/lock.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/app.h"
|
||||
|
||||
static char *app = "Skel";
|
||||
static char *tdesc = "Trivial skeleton Application";
|
||||
static char *app = "skel";
|
||||
static char *synopsis =
|
||||
"Skeleton application.";
|
||||
static char *descrip = "This application is a template to build other applications from.\n"
|
||||
" It shows you the basic structure to create your own Asterisk applications.\n";
|
||||
" This is a skeleton application that shows you the basic structure to create your\n"
|
||||
"own asterisk applications.\n";
|
||||
|
||||
enum {
|
||||
OPTION_A = (1 << 0),
|
||||
OPTION_B = (1 << 1),
|
||||
OPTION_C = (1 << 2),
|
||||
} option_flags;
|
||||
STANDARD_LOCAL_USER;
|
||||
|
||||
enum {
|
||||
OPTION_ARG_B = 0,
|
||||
OPTION_ARG_C = 1,
|
||||
/* This *must* be the last value in this enum! */
|
||||
OPTION_ARG_ARRAY_SIZE = 2,
|
||||
} option_args;
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
AST_APP_OPTIONS(app_opts,{
|
||||
AST_APP_OPTION('a', OPTION_A),
|
||||
AST_APP_OPTION_ARG('b', OPTION_B, OPTION_ARG_B),
|
||||
AST_APP_OPTION_ARG('c', OPTION_C, OPTION_ARG_C),
|
||||
});
|
||||
|
||||
|
||||
static int app_exec(struct ast_channel *chan, void *data)
|
||||
static int skel_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_flags flags;
|
||||
struct ast_module_user *u;
|
||||
char *parse, *opts[OPTION_ARG_ARRAY_SIZE];
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(dummy);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "%s requires an argument (dummy|[options])\n", app);
|
||||
int res=0;
|
||||
struct localuser *u;
|
||||
if (!data) {
|
||||
ast_log(LOG_WARNING, "skel requires an argument (filename)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u = ast_module_user_add(chan);
|
||||
|
||||
LOCAL_USER_ADD(u);
|
||||
/* Do our thing here */
|
||||
|
||||
/* We need to make a copy of the input string if we are going to modify it! */
|
||||
parse = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (args.argc == 2)
|
||||
ast_app_parse_options(app_opts, &flags, opts, args.options);
|
||||
|
||||
if (!ast_strlen_zero(args.dummy))
|
||||
ast_log(LOG_NOTICE, "Dummy value is : %s\n", args.dummy);
|
||||
|
||||
if (ast_test_flag(&flags, OPTION_A))
|
||||
ast_log(LOG_NOTICE, "Option A is set\n");
|
||||
|
||||
if (ast_test_flag(&flags, OPTION_B))
|
||||
ast_log(LOG_NOTICE, "Option B is set with : %s\n", opts[OPTION_ARG_B] ? opts[OPTION_ARG_B] : "<unspecified>");
|
||||
|
||||
if (ast_test_flag(&flags, OPTION_C))
|
||||
ast_log(LOG_NOTICE, "Option C is set with : %s\n", opts[OPTION_ARG_C] ? opts[OPTION_ARG_C] : "<unspecified>");
|
||||
|
||||
ast_module_user_remove(u);
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
int unload_module(void)
|
||||
{
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
int load_module(void)
|
||||
{
|
||||
return ast_register_application(app, skel_exec, synopsis, tdesc);
|
||||
}
|
||||
|
||||
char *description(void)
|
||||
{
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
{
|
||||
int res;
|
||||
res = ast_unregister_application(app);
|
||||
return res;
|
||||
STANDARD_USECOUNT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
char *key()
|
||||
{
|
||||
return ast_register_application(app, app_exec, synopsis, descrip);
|
||||
return ASTERISK_GPL_KEY;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Skeleton (sample) Application");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user