Compare commits

...

5 Commits

Author SHA1 Message Date
Russell Bryant
a187d0c3f1 Creating tag for the release of asterisk-1.6.0-beta1
git-svn-id: https://origsvn.digium.com/svn/asterisk/tags/1.6.0-beta1@99163 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-01-19 00:19:29 +00:00
Russell Bryant
3eb28e7735 Use autotagged externals
git-svn-id: https://origsvn.digium.com/svn/asterisk/tags/1.6.0-beta1@99126 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-01-18 22:37:14 +00:00
Russell Bryant
18a0385d23 Importing files for 1.6.0-beta1 release
git-svn-id: https://origsvn.digium.com/svn/asterisk/tags/1.6.0-beta1@99125 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-01-18 22:37:01 +00:00
Russell Bryant
acc92aeff8 Creating tag for the release of asterisk-1.6.0-beta1
git-svn-id: https://origsvn.digium.com/svn/asterisk/tags/1.6.0-beta1@99123 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-01-18 22:34:31 +00:00
Russell Bryant
bcad1ed930 Creating tag for the release of asterisk-1.6.0-beta1
git-svn-id: https://origsvn.digium.com/svn/asterisk/tags/1.6.0-beta1@99120 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-01-18 22:27:23 +00:00
1095 changed files with 589376 additions and 0 deletions

1
.lastclean Normal file
View File

@@ -0,0 +1 @@
33

1
.version Normal file
View File

@@ -0,0 +1 @@
1.6.0-beta1

34728
ChangeLog Normal file

File diff suppressed because it is too large Load Diff

1
trunk/.cleancount Normal file
View File

@@ -0,0 +1 @@
33

22
trunk/BUGS Normal file
View File

@@ -0,0 +1,22 @@
Asterisk Bug Tracking Information
=================================
To learn about and report Asterisk bugs, please visit
the official Asterisk Bug Tracker at:
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
Thank you!

496
trunk/CHANGES Normal file
View File

@@ -0,0 +1,496 @@
------------------------------------------------------------------------------
--- Functionality changes since Asterisk 1.4-beta was branched ----------------
-------------------------------------------------------------------------------
AMI - The manager (TCP/TLS/HTTP)
--------------------------------
* Manager has undergone a lot of changes, all of them documented
in doc/manager_1_1.txt
* Manager version has changed to 1.1
* Added a new action 'CoreShowChannels' to list currently defined channels
and some information about them.
* Added a new action 'SIPshowregistry' to list SIP registrations.
* Added TLS support for the manager interface and HTTP server
* Added the URI redirect option for the built-in HTTP server
* The output of CallerID in Manager events is now more consistent.
CallerIDNum is used for number and CallerIDName for name.
* Enable https support for builtin web server.
See configs/http.conf.sample for details.
* Added a new action, GetConfigJSON, which can return the contents of an
Asterisk configuration file in JSON format. This is intended to help
improve the performance of AJAX applications using the manager interface
over HTTP.
* SIP and IAX manager events now use "ChannelType" in all cases where we
indicate channel driver. Previously, we used a mixture of "Channel"
and "ChannelDriver" headers.
* Added a "Bridge" action which allows you to bridge any two channels that
are currently active on the system.
* Added a "ListAllVoicemailUsers" action that allows you to get a list of all
the voicemail users setup.
* Added 'DBDel' and 'DBDelTree' manager commands.
* cdr_manager now reports events via the "cdr" level, separating it from
the very verbose "call" level.
* Manager users are now stored in memory. If you change the manager account
list (delete or add accounts) you need to reload manager.
* Added Masquerade manager event for when a masquerade happens between
two channels.
* Added "manager reload" command for the CLI
* Lots of commands that only provided information are now allowed under the
Reporting privilege, instead of only under Call or System.
* The IAX* commands now require either System or Reporting privilege, to
mirror the privileges of the SIP* commands.
Dialplan functions
------------------
* Added the DEVICE_STATE() dialplan function which allows retrieving any device
state in the dialplan, as well as creating custom device states that are
controllable from the dialplan.
* Extend CALLERID() function with "pres" and "ton" parameters to
fetch string representation of calling number presentation indicator
and numeric representation of type of calling number value.
* MailboxExists converted to dialplan function
* A new option to Dial() for telling IP phones not to count the call
as "missed" when dial times out and cancels.
* Added LOCK(), TRYLOCK(), and UNLOCK(), which provide a single level dialplan
mutex. No deadlocks are possible, as LOCK() only allows a single lock to be
held for any given channel. Also, locks are automatically freed when a
channel is hung up.
* Added HINT() dialplan function that allows retrieving hint information.
Hints are mappings between extensions and devices for the sake of
determining the state of an extension. This function can retrieve the list
of devices or the name associated with a hint.
* Added EXTENSION_STATE() dialplan function which allows retrieving the state
of any extension.
* Added SYSINFO() dialplan function which allows retrieval of system information
* Added a new dialplan function, DIALPLAN_EXISTS(), which allows you to check for
the existence of a dialplan target.
* Added two new dialplan functions, TOUPPER and TOLOWER, which convert a string to
upper and lower case, respectively.
CLI Changes
-----------
* New CLI command "core show hint" (usage: core show hint <exten>)
* New CLI command "core show settings"
* Added 'core show channels count' CLI command.
* Added the ability to set the core debug and verbose values on a per-file basis.
* Added 'queue pause member' and 'queue unpause member' CLI commands
* Ability to set process limits ("ulimit") without restarting Asterisk
* Enhanced "agi debug" to print the channel name as a prefix to the debug
output to make debugging on busy systems much easier.
* New CLI commands "dialplan set extenpatternmatching true/false"
* New CLI command: "core set chanvar" to set a channel variable from the CLI.
* Added an easy way to execute Asterisk CLI commands at startup. Any commands
listed in the startup_commands file in the Asterisk configuration directory
will get executed.
SIP changes
-----------
* Improved NAT and STUN support.
chan_sip now can use port numbers in bindaddr, externip and externhost
options, as well as contact a STUN server to detect its external address
for the SIP socket. See sip.conf.sample, 'NAT' section.
* The default SIP useragent= identifier now includes the Asterisk version
* A new option, match_auth_username in sip.conf changes the matching of incoming requests.
If set, and the incoming request carries authentication info,
the username to match in the users list is taken from the Digest header
rather than from the From: field. This feature is considered experimental.
* The "musiconhold" and "musicclass" settings in sip.conf are now removed,
since they where replaced by "mohsuggest" and "mohinterpret" in version 1.4
* The "localmask" setting was removed in version 1.2 and the reminder about it
being removed is now also removed.
* A new option "busylevel" for setting a level of calls where asterisk reports
a device as busy, to separate it from call-limit. This value is also added
to the SIP_PEER dialplan function.
* A new realtime family called "sipregs" is now supported to store SIP registration
data. If this family is defined, "sippeers" will be used for configuration and
"sipregs" for registrations. If it's not defined, "sippeers" will be used for
registration data, as before.
* The SIPPEER function have new options for port address, call and pickup groups
* Added support for T.140 realtime text in SIP/RTP
* The "checkmwi" option has been removed from sip.conf, as it is no longer
required due to the restructuring of how MWI is handled. See the descriptions
in this file of the "pollmailboxes" and "pollfreq" options to voicemail.conf
for more information.
* Added rtpdest option to CHANNEL() dialplan function.
* Added SIPREFERRINGCONTEXT and SIPREFERREDBYHDR variables which are set when a transfer takes place.
* SIP now adds a header to the CANCEL if the call was answered by another phone
in the same dial command, or if the new c option in dial() is used.
* The new default is that 100 Trying is not sent on REGISTER attempts as the RFC specifically
states it is not needed. For phones, however, that do require it the "registertrying" option
has been added so it can be enabled.
* A new option called "callcounter" (global/peer/user level) enables call counters needed
for better status reports needed for queues and SIP subscriptions. (Call-Limit was previously
used to enable this functionality).
* New settings for timer T1 and timer B on a global level or per device. This makes it
possible to force timeout faster on non-responsive SIP servers. These settings are
considered advanced, so don't use them unless you have a problem.
* Added a dial string option to be able to set the To: header in an INVITE to any
SIP uri.
* Added a new global and per-peer option, qualifyfreq, which allows you to configure
the qualify frequency.
* Added SIP Session Timers support (RFC 4028). This prevents stuck SIP sessions that
were not properly torn down due to network or endpoint failures during an established
SIP session.
* Added TCP and TLS support for SIP. See doc/siptls.txt and configs/sip.conf.sample for
more information on how it is used.
IAX2 changes
------------
* Added the trunkmaxsize configuration option to chan_iax2.
* Added the srvlookup option to iax.conf
* Added support for OSP. The token is set and retrieved through the CHANNEL()
dialplan function.
XMPP Google Talk/Jingle changes
-------------------------------
* Added the bindaddr option to gtalk.conf.
Skinny changes
-------------
* Added skinny show device, skinny show line, and skinny show settings CLI commands.
* Proper codec support in chan_skinny.
* Added settings for IP and Ethernet QoS requests
MGCP changes
------------
* Added separate settings for media QoS in mgcp.conf
Console Channel Driver changes
-------------------
* Added experimental support for video send & receive to chan_oss.
This requires SDL and ffmpeg/avcodec, plus Video4Linux or X11 to act as
a video source.
Phone channel changes (chan_phone)
----------------------------------
* Added G729 passthrough support to chan_phone for Sigma Designs boards.
H.323 channel Changes
---------------------
* H323 remote hold notification support added (by NOTIFY message
and/or H.450 supplementary service)
Local channel changes
---------------------
* The device state functionality in the Local channel driver has been updated
to indicate INUSE or NOT_INUSE when a Local channel is being used as opposed
to just UNKNOWN if the extension exists.
* Added jitterbuffer support for chan_local. This allows you to use the
generic jitterbuffer on incoming calls going to Asterisk applications.
For example, this would allow you to use a jitterbuffer for an incoming
SIP call to Voicemail by putting a Local channel in the middle. This
feature is enabled by using the 'j' option in the Dial string to the Local
channel in conjunction with the existing 'n' option for local channels.
Zaptel channel driver (chan_zap) Changes
----------------------------------------
* SS7 support in chan_zap (via libss7 library)
* In India, some carriers transmit CID via dtmf. Some code has been added
that will handle some situations. The cidstart=polarity_IN choice has been added for
those carriers that transmit CID via dtmf after a polarity change.
* CID matching information is now shown when doing 'dialplan show'.
* Added zap show version CLI command to chan_zap.
* Added setvar support to zapata.conf channel entries.
* Added two new options: mwimonitor and mwimonitornotify. These options allow
you to enable MWI monitoring on FXO lines. When the MWI state changes,
the script specified in the mwimonitornotify option is executed. An internal
event indicating the new state of the mailbox is also generated, so that
the normal MWI facilities in Asterisk work as usual.
* Added signalling type 'auto', which attempts to use the same signalling type
for a channel as configured in Zaptel. This is primarily designed for analog
ports, but will also work for digital ports that are configured for FXS or FXO
signalling types. This mode is also the default now, so if your zapata.conf
does not specify signalling for a channel (which is unlikely as the sample
configuration file has always recommended specifying it for every channel) then
the 'auto' mode will be used for that channel if possible.
* Added a 'zap set dnd' command to allow CLI control of the Do-Not-Disturb
state for a channel; also ensured that the DNDState Manager event is
emitted no matter how the DND state is set or cleared.
New Channel Drivers
-------------------
* Added a new channel driver, chan_unistim. See doc/unistim.txt and
configs/unistim.conf.sample for details. This new channel driver allows
you to use Nortel i2002, i2004, and i2050 phones with Asterisk.
* Added a new channel driver, chan_console, which uses portaudio as a cross
platform audio interface. It was written as a channel driver that would
work with Mac CoreAudio, but portaudio supports a number of other audio
interfaces, as well. Note that this channel driver requires v19 or higher
of portaudio; older versions have a different API.
DUNDi changes
-------------
* Added the ability to specify arguments to the Dial application when using
the DUNDi switch in the dialplan.
* Added the ability to set weights for responses dynamically. This can be
done using a global variable or a dialplan function. Using the SHELL()
function would allow you to have an external script set the weight for
each response.
* Added two new dialplan functions, DUNDIQUERY and DUNDIRESULT. These
functions will allow you to initiate a DUNDi query from the dialplan,
find out how many results there are, and access each one.
ENUM changes
------------
* Added two new dialplan functions, ENUMQUERY and ENUMRESULT. These
functions will allow you to initiate an ENUM lookup from the dialplan,
and Asterisk will cache the results. ENUMRESULT can be used to access
the results without doing multiple DNS queries.
Voicemail Changes
-----------------
* Added the ability to customize which sound files are used for some of the
prompts within the Voicemail application by changing them in voicemail.conf
* Added the ability for the "voicemail show users" CLI command to show users
configured by the dynamic realtime configuration method.
* MWI (Message Waiting Indication) handling has been significantly
restructured internally to Asterisk. It is now totally event based
instead of polling based. The voicemail application will notify other
modules that have subscribed to MWI events when something in the mailbox
changes.
This also means that if any other entity outside of Asterisk is changing
the contents of mailboxes, then the voicemail application still needs to
poll for changes. Examples of situations that would require this option
are web interfaces to voicemail or an email client in the case of using
IMAP storage. So, two new options have been added to voicemail.conf
to account for this: "pollmailboxes" and "pollfreq". See the sample
configuration file for details.
* Added "tw" language support
* Added support for storage of greetings using an IMAP server
* Added ability to customize forward, reverse, stop, and pause keys for message playback
* SMDI is now enabled in voicemail using the smdienable option.
* A "lockmode" option has been added to asterisk.conf to configure the file
locking method used for voicemail, and potentially other things in the
future. The default is the old behavior, lockfile. However, there is a
new method, "flock", that uses a different method for situations where the
lockfile will not work, such as on SMB/CIFS mounts.
* Added the ability to backup deleted messages, to ease recovery in the case
that a user accidentally deletes a message, and discovers that they need it.
Queue changes
-------------
* Added the general option 'shared_lastcall' so that member's wrapuptime may be
used across multiple queues.
* Added QUEUE_VARIABLES function to set queue variables added setqueuevar and
setqueueentryvar options for each queue, see queues.conf.sample for details.
* Added keepstats option to queues.conf which will keep queue
statistics during a reload.
* setinterfacevar option in queues.conf also now sets a variable
called MEMBERNAME which contains the member's name.
* Added 'Strategy' field to manager event QueueParams which represents
the queue strategy in use.
* Added option to run macro when a queue member is connected to a caller,
see queues.conf.sample for details.
* app_queue now has a 'loose' option which is almost exactly like 'strict' except it
does not count paused queue members as unavailable.
* Added min-announce-frequency option to queues.conf which allows you to control the
minimum amount of time between queue announcements for use when the caller's queue
position changes frequently.
* Added additional information to EXITWITHTIMEOUT and EXITWITHKEY events in the
queue log.
* Added ability for non-realtime queues to have realtime members
* Added the "linear" strategy to queues.
* Added the "wrandom" strategy to queues.
* Added new channel variable QUEUE_MIN_PENALTY
* QUEUE_MAX_PENALTY and QUEUE_MIN_PENALTY may be adjusted in mid-call by defining
rules in queuerules.conf. See configs/queuerules.conf.sample for details
* Added a new parameter for member definition, called state_interface. This may be
used so that a member may be called via one interface but have a different interface's
device state reported.
MeetMe Changes
--------------
* The 'o' option to provide an optimization has been removed and its functionality
has been enabled by default.
* When a conference is created, the UNIQUEID of the channel that caused it to be
created is stored. Then, every channel that joins the conference will have the
MEETMEUNIQUEID channel variable set with this ID. This can be used to relate
callers that come and go from long standing conferences.
* Added a new application, MeetMeChannelAdmin, which is similar to MeetMeAdmin,
except it does operations on a channel by name, instead of number in a conference.
This is a very useful feature in combination with the 'X' option to ChanSpy.
* Added 'C' option to Meetme which causes a caller to continue in the dialplan
when kicked out.
* Added new RealTime functionality to provide support for scheduled conferencing.
This includes optional messages to the caller if they attempt to join before
the schedule start time, or to allow the caller to join the conference early.
Also included is optional support for limiting the number of callers per
RealTime conference.
* Added the S() and L() options to the MeetMe application. These are pretty
much identical to the S() and L() options to Dial(). They let you set
timeouts for the conference, as well as have warning sounds played to
let the caller know how much time is left, and when it is running out.
* Added the ability to do "meetme concise" with the "meetme" CLI command.
This extends the concise capabilities of this CLI command to include
listing all conferences, instead of an addition to the other sub commands
for the "meetme" command.
* Added the ability to specify the music on hold class used to play into the
conference when there is only one member and the M option is used.
Other Dialplan Application Changes
----------------------------------
* Argument support for Gosub application
* From the to-do lists: straighten out the app timeout args:
Wait() app now really does 0.3 seconds- was truncating arg to an int.
WaitExten() same as Wait().
Congestion() - Now takes floating pt. argument.
Busy() - now takes floating pt. argument.
Read() - timeout now can be floating pt.
WaitForRing() now takes floating pt timeout arg.
SpeechBackground() -- clarified in the docstrings that the timeout is an integer seconds.
* Added 's' option to Page application.
* Added 'E' and 'V' commands to ExternalIVR.
* Added 'o' and 'X' options to Chanspy.
* Added a new dialplan application, Bridge, which allows you to bridge the
calling channel to any other active channel on the system.
* Added the ability to specify a music on hold class to play instead of ringing
for the SLATrunk application.
* The Read application no longer exits the dialplan on error. Instead, it sets
READSTATUS to ERROR, which you can catch and handle separately.
* Added 'm' option to Directory, which lists out names, 8 at a time, instead
of asking for verification of each name, one at a time.
* Privacy() no longer uses privacy.conf, as all options are specifyable as
direct options to the app.
* AMD() has a new "maximum word length" option. "show application AMD" from the CLI
for more details
Music On Hold Changes
---------------------
* A new option, "digit", has been added for music on hold classes in
musiconhold.conf. If this is set for a music on hold class, a caller
listening to music on hold can press this digit to switch to listening
to this music on hold class.
* Support for realtime music on hold has been added.
* In conjunction with the realtime music on hold, a general section has
been added to musiconhold.conf, its sole variable is cachertclasses. If this
is set, then music on hold classes found in realtime will be cached in memory.
AEL Changes
-----------
* AEL upgraded to use the Gosub with Arguments instead
of Macro application, to hopefully reduce the problems
seen with the artificially low stack ceiling that
Macro bumps into. Macros can only call other Macros
to a depth of 7. Tests run using gosub, show depths
limited only by virtual memory. A small test demonstrated
recursive call depths of 100,000 without problems.
-- in addition to this, all apps that allowed a macro
to be called, as in Dial, queues, etc, are now allowing
a gosub call in similar fashion.
* AEL now generates LOCAL(argname) declarations when it
Set()'s the each arg name to the value of ${ARG1}, ${ARG2),
etc. That makes the arguments local in scope. The user
can define their own local variables in macros, now,
by saying "local myvar=someval;" or using Set() in this
fashion: Set(LOCAL(myvar)=someval); ("local" is now
an AEL keyword).
* utils/conf2ael introduced. Will convert an extensions.conf
file into extensions.ael. Very crude and unfinished, but
will be improved as time goes by. Should be useful for a
first pass at conversion.
* aelparse will now read extensions.conf to see if a referenced
macro or context is there before issueing a warning.
Call Features (res_features) Changes
------------------------------------
* Added the parkedcalltransfers option to features.conf
* The built-in method for doing attended transfers has been updated to
include some new options that allow you to have the transferee sent
back to the person that did the transfer if the transfer is not successful.
See the options "atxferdropcall", "atxferloopdelay", and "atxfercallbackretries"
in features.conf.sample.
* Added support for configuring named groups of custom call features in
features.conf. This means that features can be written a single time, and
then mapped into groups of features for different key mappings or easier
access control.
* Updated the ParkedCall application to allow you to not specify a parking
extension. If you don't specify a parking space to pick up, it will grab
the first one available.
Language Support Changes
------------------------
* Brazilian Portuguese (pt-BR) in VM, and say.c was added
* Added support for the Hungarian language for saying numbers, dates, and times.
AGI Changes
-----------
* Added SPEECH commands for speech recognition. A complete listing can be found
using agi show.
Logger changes
--------------
* Added rotatestrategy option to logger.conf, along with two new options:
"timestamp" which will use the time to name the logger files instead of
sequence number; and "rotate", which rotates the names of the logfiles,
similar to the way syslog rotates files.
* Added exec_after_rotate option to logger.conf, which allows a system
command to be run after rotation. This is primarily useful with
rotatestrategry=rotate, to allow a limit on the number of logfiles kept
and to ensure that the oldest log file gets deleted.
* Added realtime support for the queue log
Miscellaneous New Modules
-------------------------
* Added a new CDR module, cdr_sqlite3_custom.
* Added a new realtime configuration module, res_config_sqlite
* Added a new codec translation module, codec_resample, which re-samples
signed linear audio between 8 kHz and 16 kHz to help support wideband
codecs.
* Added a new module, res_phoneprov, which allows auto-provisioning of phones
based on configuration templates that use Asterisk dialplan function and
variable substitution. It should be possible to create phone profiles and
templates that work for the majority of phones provisioned over http. It
is currently only intended to provision a single user account per phone.
An example profile and set of templates for Polycom phones is provided.
NOTE: Polycom firmware is not included, but should be placed in
AST_DATA_DIR/phoneprov/configs to match up with the included templates.
* Added a new module, app_jack, which provides interfaces to JACK, the Jack
Audio Connection Kit (http://www.jackaudio.org/). Two interfaces are
provided; there is a JACK() application, and a JACK_HOOK() function. Both
interfaces create an input and output JACK port. The application makes
these ports the endpoint of the call. The audio coming from the channel
goes out the output port and whatever comes back in on the input port is
what gets sent to the channel. The JACK_HOOK() function turns on a JACK
audiohook on the channel. This lets you run the audio coming from a
channel through JACK, and whatever comes back in is what gets forwarded
on as the channel's audio. This is very useful for building custom
vocoders or doing recording or analysis of the channel's audio in another
application.
* Added a new module, res_config_curl, which permits using a HTTP POST url
to retrieve, create, update, and delete realtime information from a remote
web server. Note that this module requires func_curl.so to be loaded for
backend functionality.
Miscellaneous
-------------
* Ability to use libcap to set high ToS bits when non-root
on Linux. If configure is unable to find libcap then you
can use --with-cap to specify the path.
* Added maxfiles option to options section of asterisk.conf which allows you to specify
what Asterisk should set as the maximum number of open files when it loads.
* Added the jittertargetextra configuration option.
* The cdr_manager module has a [mappings] feature, like cdr_custom,
to add fields to the manager event from the CDR variables.
* Added support for setting the CoS for VLAN traffic (802.1p). See the sample
configuration files for the IP channel drivers. The new option is "cos".
This information is also documented in doc/qos.tex, or the IP Quality of Service
section of asterisk.pdf.
* When originating a call using AMI or pbx_spool that fails the reason for failure
will now be available in the failed extension using the REASON dialplan variable.
* Added support for reading the TOUCH_MONITOR_PREFIX channel variable.
It allows you to configure a prefix for auto-monitor recordings.
* Added support for writing and running your dialplan in lua. See
configs/extensions.lua.sample for examples of how to do this.
* A new extension pattern matching algorithm, based on a trie, is introduced
here, that could noticeably speed up mid-sized to large dialplans.
It is NOT used by default, as duplicating the behaviour of the old pattern
matcher is still under development. A config file option, in extensions.conf,
in the [general] section, called "extenpatternmatchingnew", is by default
set to false; setting that to true will force the use of the new algorithm.
Also, the cli commands "dialplan set extenpatternmatchingnew true/false" can
be used to switch the algorithms at run time.
* A new option when starting a remote asterisk (rasterisk, asterisk -r) for
specifying which socket to use to connect to the running Asterisk daemon
(-s)
* Added logging to 'make update' command. See update.log

341
trunk/COPYING Normal file
View File

@@ -0,0 +1,341 @@
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.

244
trunk/CREDITS Normal file
View File

@@ -0,0 +1,244 @@
=== DEVELOPMENT SUPPORT ===
We'd like to thank the following companies for helping fund development of
Asterisk:
Pilosoft, Inc. - for supporting ADSI development in Asterisk
Asterlink, Inc. - for supporting broad Asterisk development
GFS - for supporting ALSA development
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
John Todd, TalkPlus, Inc. and JR Richardson, Ntegrated Solutions. - for funding
the development of SIP Session Timers support.
=== WISHLIST CONTRIBUTERS ===
Jeremy McNamara - SpeeX support
Nick Seraphin - RDNIS support
Gary - Phonejack ADSI (in progress)
Wasim - Hangup detect
=== HARDWARE DONORS ===
* 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 release manager and countless enhancements and bug
fixes.
russell(AT)digium.com
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(AT)yahoo.com http://www.asterlink.com
James Golovich - Innumerable contributions, including SIP TCP and TLS support.
You can find him and asterisk-perl at http://asterisk.gnuinter.net
Andre Bierwirth - Extension hints and status
Jean-Denis Girard - Various contributions from the South Pacific Islands
jd-girard(AT)esoft.pf http://www.esoft.pf
William Jordan / Vonage - MySQL enhancements to Voicemail
wjordan(AT)vonage.com
Jac Kersing - Various fixes
Steven Critchfield - Seek and Trunc functions for playback and recording
critch(AT)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(AT)oa.com.au
James Dennis - Cisco SIP compatibility patches to work with SIP service
providers. Can be contacted at asterisk(AT)jdennis.net
Tilghman Lesher - ast_localtime(); ast_say_date_with_format();
GotoIfTime, SayUnixTime, HasNewVoicemail applications;
CUT, SORT, EVAL, CURL, FIELDQTY, STRFTIME, some QUEUE* functions;
func_odbc, cdr_adaptive_odbc, and other innumerable bug fixes.
tilghman(AT)digium.com http://asterisk.drunkcoder.com/
Jayson Vantuyl - Manager protocol changes, various other bugs.
jvantuyl(AT)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(AT)sigmasoft.com
Josh Roberson - chan_zap reload support, Advanced Voicemail Features, & other
misc. patches. - josh(AT)asteriasgi.com, http://www.asteriasgi.com
William Waites - syslog support, SIP NAT traversal for SIP-UA. ww(AT)styx.org
Rich Murphey - Porting to FreeBSD, NetBSD, OpenBSD, and Darwin.
rich(AT)whiteoaklabs.com http://whiteoaklabs.com
Simon Lockhart - Porting to Solaris (based on work of Logan ???)
simon(AT)slimey.org
Olle E. Johansson - SIP RFC compliance, documentation and testing, testing,
testing; MiniVM - the small voicemail system, many documentation
updates/corrections, and many bug fixes.
oej(AT)edvina.net, http://edvina.net
Steve Kann - new jitter buffer for IAX2
stevek(AT)stevek.com
Constantine Filin - major contributions to the Asterisk Realtime Architecture
Steve Murphy - privacy support, $[ ] parser upgrade, AEL2 parser upgrade.
murf(AT)digium.com
Claude Patry - bug fixes, feature enhancements, and bug marshalling
cpatry(AT)gmail.com
Miroslav Nachev, miro(AT)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(AT)securax.be
Roy Sigurd Karlsbakk - providing funding for generic jitterbuffer development
roy(AT)karlsbakk.net, Briiz Telecom AS
Voop AS, Nuvio Inc, Inotel S.A and Foniris Telecom A/S - funding for rewrite
of SIP transfers
Philippe Sultan - RADIUS CDR module, many fixes to res_jabber and gtalk/jingle
channel drivers.
INRIA, http://www.inria.fr/
John Martin, Aupix - Improved video support in the SIP channel
T.140 text support in RTP/SIP
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(AT)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(AT)gdspartners.com
Bartosz Supczinski - Support for Polish added by DIR (www.dir.pl)
Bartosz.Supczinski(AT)dir.pl
James Rothenberger - Support for IMAP storage integration added by
OneBizTone LLC Work funded by University of Pennsylvania jar(AT)onebiztone.com
Paul Cadach - Bringing chan_h323 up to date, bug fixes, and more!
Voop AS - Financial support for a lot of work with the SIP driver and the IAX
trunk MTU patch
Cedric Hans - Development of chan_unistim
cedric.hans(AT)mlkj.net
Sergio Fadda - console_video: video support for chan_oss and chan_alsa
Marta Carbone - console_video and the astobj2 framework
Luigi Rizzo - astobj2, console_video, windows build, chan_oss cleanup,
and a bunch of infrastructure work (loader, new_cli, ...)
Brett Bryant - digit option for musiconhold selection, ENUMQUERY and ENUMRESULT functions,
feature group configuration for features.conf, per-file CLI debug and verbose settings,
TCP and TLS support for SIP, and various bug fixes.
brettbryant(AT)gmail.com
=== 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(AT)clapper.org>
=== HOLD MUSIC ===
Music provided by www.freeplaymusic.com
=== 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
GSM source:
Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
Technische Universitaet Berlin
Any use of this software is permitted provided that this notice is not
removed and that neither the authors nor the Technische Universitaet Berlin
are deemed to have made any representations as to the suitability of this
software for any purpose nor are held responsible for any defects of
this software. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
As a matter of courtesy, the authors request to be informed about uses
this software has found, about bugs in this software, and about any
improvements that may be of general interest.
Berlin, 28.11.1994
Jutta Degener
Carsten Bormann
And the copyright on the ADPCM source:
Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The
Netherlands.
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the names of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

69
trunk/LICENSE Normal file
View File

@@ -0,0 +1,69 @@
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. Most of these components are also
distributed under the GPL version 2 as well, except for the following:
contrib/firmware/iax/iaxy.bin:
This file is Copyright (C) Digium, Inc. and is licensed for
use with Digium IAXy hardware devices only. It can be
distributed freely as long as the distribution is in the
original form present in this package (not reformatted or
modified).
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.
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).
Specific permission is also granted to link Asterisk with OpenSSL and
OpenH323 and distribute the resulting binary files.
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.
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
If you have any questions regarding our licensing policy, please
contact us:
+1.877.344.4861 (via telephone in the USA)
+1.256.428.6000 (via telephone outside the USA)
+1.256.864.0464 (via FAX inside or outside the USA)
IAX2/misery.digium.com/6000 (via IAX2)
licensing@digium.com (via email)
Digium, Inc.
445 Jan Davis Drive
Huntsville, AL 35806
USA

834
trunk/Makefile Normal file
View File

@@ -0,0 +1,834 @@
#
# Asterisk -- A telephony toolkit for Linux.
#
# Top level Makefile
#
# Copyright (C) 1999-2006, Digium, Inc.
#
# Mark Spencer <markster@digium.com>
#
# This program is free software, distributed under the terms of
# the GNU General Public License
#
# All Makefiles use the following variables:
#
# ASTCFLAGS - compiler options
# ASTLDFLAGS - linker flags (not libraries)
# LIBS - additional libraries, at top-level for all links,
# on a single object just for that object
# SOLINK - linker flags used only for creating shared objects (.so files),
# used for all .so links
#
# Initial values for ASTCFLAGS and ASTLDFLAGS can be specified in the
# environment when running make, as follows:
#
# $ ASTCFLAGS="-Werror" make ...
#
# note that this is different from
#
# $ make ASTCFLAGS="-Werror" ...
#
# If you need to pass compiler/linker flags as 'make' variables, please use
#
# $ make COPTS="..." LDOPTS="..." ...
#
#
# You can add the path of local module subdirs from the command line with
# make LOCAL_MOD_SUBDIRS= ....
export ASTTOPDIR # Top level dir, used in subdirs' Makefiles
export ASTERISKVERSION
export ASTERISKVERSIONNUM
#--- values used for default paths
# DESTDIR is the staging (or final) directory where files are copied
# during the install process. Define it before 'export', otherwise
# export will set it to the empty string making ?= fail.
# WARNING: do not put spaces or comments after the value.
DESTDIR?=$(INSTALL_PATH)
export DESTDIR
export INSTALL_PATH # Additional prefix for the following paths
export ASTETCDIR # Path for config files
export ASTVARRUNDIR
export MODULES_DIR
export ASTSPOOLDIR
export ASTVARLIBDIR
export ASTDATADIR
export ASTLOGDIR
export ASTLIBDIR
export ASTMANDIR
export ASTHEADERDIR
export ASTBINDIR
export ASTSBINDIR
export AGI_DIR
export ASTCONFPATH
export OSARCH # Operating system
export PROC # Processor type
export NOISY_BUILD # Used in Makefile.rules
export MENUSELECT_CFLAGS # Options selected in menuselect.
export AST_DEVMODE # Set to "yes" for additional compiler
# and runtime checks
export SOLINK # linker flags for shared objects
export STATIC_BUILD # Additional cflags, set to -static
# for static builds. Probably
# should go directly to ASTLDFLAGS
#--- paths to various commands
export CC
export CXX
export AR
export RANLIB
export HOST_CC
export INSTALL
export STRIP
export DOWNLOAD
export AWK
export GREP
export ID
# even though we could use '-include makeopts' here, use a wildcard
# lookup anyway, so that make won't try to build makeopts if it doesn't
# exist (other rules will force it to be built if needed)
ifneq ($(wildcard makeopts),)
include makeopts
endif
# Some build systems, such as the one in openwrt, like to pass custom target
# CFLAGS and LDFLAGS in the COPTS and LDOPTS variables.
ASTCFLAGS+=$(COPTS)
ASTLDFLAGS+=$(LDOPTS)
#Uncomment this to see all build commands instead of 'quiet' output
#NOISY_BUILD=yes
ASTTOPDIR:=$(CURDIR)
# Overwite config files on "make samples"
OVERWRITE=y
# Include debug and macro symbols in the executables (-g) and profiling info (-pg)
DEBUG=-g3
# Define standard directories for various platforms
# These apply if they are not redefined in asterisk.conf
ifeq ($(OSARCH),SunOS)
ASTETCDIR=/var/etc/asterisk
ASTLIBDIR=/opt/asterisk/lib
ASTVARLIBDIR=/var/opt/asterisk
ASTDBDIR=$(ASTVARLIBDIR)
ASTKEYDIR=$(ASTVARLIBDIR)
ASTSPOOLDIR=/var/spool/asterisk
ASTLOGDIR=/var/log/asterisk
ASTHEADERDIR=/opt/asterisk/include
ASTBINDIR=/opt/asterisk/bin
ASTSBINDIR=/opt/asterisk/sbin
ASTVARRUNDIR=/var/run/asterisk
ASTMANDIR=/opt/asterisk/man
else
ASTETCDIR=$(sysconfdir)/asterisk
ASTLIBDIR=$(libdir)/asterisk
ASTHEADERDIR=$(includedir)/asterisk
ASTBINDIR=$(bindir)
ASTSBINDIR=$(sbindir)
ASTSPOOLDIR=$(localstatedir)/spool/asterisk
ASTLOGDIR=$(localstatedir)/log/asterisk
ASTVARRUNDIR=$(localstatedir)/run
ASTMANDIR=$(mandir)
ifneq ($(findstring BSD,$(OSARCH)),)
ASTVARLIBDIR=$(prefix)/share/asterisk
ASTVARRUNDIR=$(localstatedir)/run/asterisk
ASTDBDIR=$(localstatedir)/db/asterisk
else
ASTVARLIBDIR=$(localstatedir)/lib/asterisk
ASTDBDIR=$(ASTVARLIBDIR)
endif
ASTKEYDIR=$(ASTVARLIBDIR)
endif
ifeq ($(ASTDATADIR),)
ASTDATADIR:=$(ASTVARLIBDIR)
endif
# Asterisk.conf is located in ASTETCDIR or by using the -C flag
# when starting Asterisk
ASTCONFPATH=$(ASTETCDIR)/asterisk.conf
MODULES_DIR=$(ASTLIBDIR)/modules
AGI_DIR=$(ASTDATADIR)/agi-bin
# If you use Apache, you may determine by a grep 'DocumentRoot' of your httpd.conf file
HTTP_DOCSDIR=/var/www/html
# Determine by a grep 'ScriptAlias' of your Apache httpd.conf file
HTTP_CGIDIR=/var/www/cgi-bin
# Uncomment this to use the older DSP routines
#ASTCFLAGS+=-DOLD_DSP_ROUTINES
# If the file .asterisk.makeopts is present in your home directory, you can
# include all of your favorite menuselect options so that every time you download
# a new version of Asterisk, you don't have to run menuselect to set them.
# The file /etc/asterisk.makeopts will also be included but can be overridden
# by the file in your home directory.
GLOBAL_MAKEOPTS=$(wildcard /etc/asterisk.makeopts)
USER_MAKEOPTS=$(wildcard ~/.asterisk.makeopts)
MOD_SUBDIR_CFLAGS=-I$(ASTTOPDIR)/include
OTHER_SUBDIR_CFLAGS=-I$(ASTTOPDIR)/include
# Create OPTIONS variable, but probably we can assign directly to ASTCFLAGS
OPTIONS=
ifeq ($(OSARCH),linux-gnu)
ifeq ($(PROC),x86_64)
# You must have GCC 3.4 to use k8, otherwise use athlon
PROC=k8
#PROC=athlon
endif
ifeq ($(PROC),sparc64)
#The problem with sparc is the best stuff is in newer versions of gcc (post 3.0) only.
#This works for even old (2.96) versions of gcc and provides a small boost either way.
#A ultrasparc cpu is really v9 but the stock debian stable 3.0 gcc doesn't support it.
#So we go lowest common available by gcc and go a step down, still a step up from
#the default as we now have a better instruction set to work with. - Belgarath
PROC=ultrasparc
OPTIONS+=$(shell if $(CC) -mtune=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mtune=$(PROC)"; fi)
OPTIONS+=$(shell if $(CC) -mcpu=v8 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mcpu=v8"; fi)
OPTIONS+=-fomit-frame-pointer
endif
ifeq ($(PROC),arm)
# The Cirrus logic is the only heavily shipping arm processor with a real floating point unit
ifeq ($(SUB_PROC),maverick)
OPTIONS+=-fsigned-char -mcpu=ep9312
else
ifeq ($(SUB_PROC),xscale)
OPTIONS+=-fsigned-char -mcpu=xscale
else
OPTIONS+=-fsigned-char
endif
endif
endif
endif
ifeq ($(findstring -save-temps,$(ASTCFLAGS)),)
ASTCFLAGS+=-pipe
endif
ASTCFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG)
ASTCFLAGS+=-include $(ASTTOPDIR)/include/asterisk/autoconfig.h
ifeq ($(AST_DEVMODE),yes)
ASTCFLAGS+=-Werror -Wunused -Wundef $(AST_DECLARATION_AFTER_STATEMENT)
endif
ifneq ($(findstring BSD,$(OSARCH)),)
ASTCFLAGS+=-I/usr/local/include
ASTLDFLAGS+=-L/usr/local/lib
endif
ifneq ($(PROC),ultrasparc)
ASTCFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi)
endif
ifeq ($(PROC),ppc)
ASTCFLAGS+=-fsigned-char
endif
ifeq ($(OSARCH),FreeBSD)
# -V is understood by BSD Make, not by GNU make.
BSDVERSION=$(shell make -V OSVERSION -f /usr/share/mk/bsd.port.subdir.mk)
ASTCFLAGS+=$(shell if test $(BSDVERSION) -lt 500016 ; then echo "-D_THREAD_SAFE"; fi)
endif
ifeq ($(OSARCH),NetBSD)
ASTCFLAGS+=-pthread -I/usr/pkg/include
endif
ifeq ($(OSARCH),OpenBSD)
ASTCFLAGS+=-pthread
endif
ifeq ($(OSARCH),SunOS)
ASTCFLAGS+=-Wcast-align -DSOLARIS -I../include/solaris-compat -I/opt/ssl/include -I/usr/local/ssl/include -D_XPG4_2
endif
ASTERISKVERSION:=$(shell GREP=$(GREP) AWK=$(AWK) build_tools/make_version .)
ifneq ($(wildcard .version),)
ASTERISKVERSIONNUM:=$(shell $(AWK) -F. '{printf "%01d%02d%02d", $$1, $$2, $$3}' .version)
RPMVERSION:=$(shell sed 's/[-\/:]/_/g' .version)
else
RPMVERSION=unknown
endif
ifneq ($(wildcard .svn),)
ASTERISKVERSIONNUM:=999999
endif
# XXX MALLOC_DEBUG is probably unused, Makefile.moddir_rules adds the
# value directly to ASTCFLAGS
ASTCFLAGS+=$(MALLOC_DEBUG)$(OPTIONS)
MOD_SUBDIRS:=channels pbx apps codecs formats cdr funcs tests main res $(LOCAL_MOD_SUBDIRS)
OTHER_SUBDIRS:=utils agi
SUBDIRS:=$(OTHER_SUBDIRS) $(MOD_SUBDIRS)
SUBDIRS_INSTALL:=$(SUBDIRS:%=%-install)
SUBDIRS_CLEAN:=$(SUBDIRS:%=%-clean)
SUBDIRS_DIST_CLEAN:=$(SUBDIRS:%=%-dist-clean)
SUBDIRS_UNINSTALL:=$(SUBDIRS:%=%-uninstall)
MOD_SUBDIRS_EMBED_LDSCRIPT:=$(MOD_SUBDIRS:%=%-embed-ldscript)
MOD_SUBDIRS_EMBED_LDFLAGS:=$(MOD_SUBDIRS:%=%-embed-ldflags)
MOD_SUBDIRS_EMBED_LIBS:=$(MOD_SUBDIRS:%=%-embed-libs)
MOD_SUBDIRS_MENUSELECT_TREE:=$(MOD_SUBDIRS:%=%-menuselect-tree)
ifneq ($(findstring darwin,$(OSARCH)),)
ASTCFLAGS+=-D__Darwin__
SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace
else
# These are used for all but Darwin
SOLINK=-shared -Xlinker -x
ifneq ($(findstring BSD,$(OSARCH)),)
LDFLAGS+=-L/usr/local/lib
endif
endif
ifeq ($(OSARCH),SunOS)
SOLINK=-shared -fpic -L/usr/local/ssl/lib
endif
# comment to print directories during submakes
#PRINT_DIR=yes
SILENTMAKE:=$(MAKE) --quiet --no-print-directory
ifneq ($(PRINT_DIR)$(NOISY_BUILD),)
SUBMAKE:=$(MAKE) --quiet
else
SUBMAKE:=$(MAKE) --quiet --no-print-directory
endif
# This is used when generating the doxygen documentation
ifneq ($(DOT),:)
HAVEDOT=yes
else
HAVEDOT=no
endif
# $(MAKE) is printed in several places, and we want it to be a
# fixed size string. Define a variable whose name has also the
# same size, so we can easily align text.
ifeq ($(MAKE), gmake)
mK="gmake"
else
mK=" make"
endif
all: _all
@echo " +--------- Asterisk Build Complete ---------+"
@echo " + Asterisk has successfully been built, and +"
@echo " + can be installed by running: +"
@echo " + +"
@echo " + $(mK) install +"
@echo " +-------------------------------------------+"
_all: cleantest makeopts $(SUBDIRS)
makeopts: configure
@echo "****"
@echo "**** The configure script must be executed before running '$(MAKE)'."
@echo "**** Please run \"./configure\"."
@echo "****"
@exit 1
menuselect.makeopts: menuselect/menuselect menuselect-tree
menuselect/menuselect --check-deps $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts
$(MOD_SUBDIRS_EMBED_LDSCRIPT):
@echo "EMBED_LDSCRIPTS+="`$(SILENTMAKE) -C $(@:-embed-ldscript=) SUBDIR=$(@:-embed-ldscript=) __embed_ldscript` >> makeopts.embed_rules
$(MOD_SUBDIRS_EMBED_LDFLAGS):
@echo "EMBED_LDFLAGS+="`$(SILENTMAKE) -C $(@:-embed-ldflags=) SUBDIR=$(@:-embed-ldflags=) __embed_ldflags` >> makeopts.embed_rules
$(MOD_SUBDIRS_EMBED_LIBS):
@echo "EMBED_LIBS+="`$(SILENTMAKE) -C $(@:-embed-libs=) SUBDIR=$(@:-embed-libs=) __embed_libs` >> makeopts.embed_rules
$(MOD_SUBDIRS_MENUSELECT_TREE):
@$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) moduleinfo
@$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) makeopts
makeopts.embed_rules: menuselect.makeopts
@echo "Generating embedded module rules ..."
@rm -f $@
@$(MAKE) $(PRINT_DIR) $(MOD_SUBDIRS_EMBED_LDSCRIPT)
@$(MAKE) $(PRINT_DIR) $(MOD_SUBDIRS_EMBED_LDFLAGS)
@$(MAKE) $(PRINT_DIR) $(MOD_SUBDIRS_EMBED_LIBS)
$(SUBDIRS): main/version.c include/asterisk/build.h include/asterisk/buildopts.h defaults.h makeopts.embed_rules
ifeq ($(findstring $(OSARCH), mingw32 cygwin ),)
# Non-windows:
# ensure that all module subdirectories are processed before 'main' during
# a parallel build, since if there are modules selected to be embedded the
# directories containing them must be completed before the main Asterisk
# binary can be built
main: $(filter-out main,$(MOD_SUBDIRS))
else
# Windows: we need to build main (i.e. the asterisk dll) first,
# followed by res, followed by the other directories, because
# dll symbols must be resolved during linking and not at runtime.
D1:= $(filter-out main,$(MOD_SUBDIRS))
D1:= $(filter-out res,$(D1))
$(D1): res
res: main
endif
$(MOD_SUBDIRS):
@ASTCFLAGS="$(MOD_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" $(MAKE) $(PRINT_DIR) --no-builtin-rules -C $@ SUBDIR=$@ all
$(OTHER_SUBDIRS):
@ASTCFLAGS="$(OTHER_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" $(MAKE) $(PRINT_DIR) --no-builtin-rules -C $@ SUBDIR=$@ all
defaults.h: makeopts
@build_tools/make_defaults_h > $@.tmp
@cmp -s $@.tmp $@ || mv $@.tmp $@
@rm -f $@.tmp
main/version.c:
@build_tools/make_version_c > $@.tmp
@cmp -s $@.tmp $@ || mv $@.tmp $@
@rm -f $@.tmp
include/asterisk/buildopts.h: menuselect.makeopts
@build_tools/make_buildopts_h > $@.tmp
@cmp -s $@.tmp $@ || mv $@.tmp $@
@rm -f $@.tmp
include/asterisk/build.h:
@build_tools/make_build_h > $@.tmp
@cmp -s $@.tmp $@ || mv $@.tmp $@
@rm -f $@.tmp
$(SUBDIRS_CLEAN):
@$(MAKE) $(PRINT_DIR) -C $(@:-clean=) clean
$(SUBDIRS_DIST_CLEAN):
@$(MAKE) $(PRINT_DIR) -C $(@:-dist-clean=) dist-clean
clean: $(SUBDIRS_CLEAN)
rm -f defaults.h
rm -f include/asterisk/build.h
rm -f main/version.c
@$(MAKE) -C menuselect clean
cp -f .cleancount .lastclean
dist-clean: distclean
distclean: $(SUBDIRS_DIST_CLEAN) clean
@$(MAKE) -C menuselect dist-clean
@$(MAKE) -C sounds dist-clean
rm -f menuselect.makeopts makeopts menuselect-tree menuselect.makedeps
rm -f makeopts.embed_rules
rm -f config.log config.status
rm -rf autom4te.cache
rm -f include/asterisk/autoconfig.h
rm -f include/asterisk/buildopts.h
rm -rf doc/api
rm -f build_tools/menuselect-deps
datafiles: _all
if [ x`$(ID) -un` = xroot ]; then CFLAGS="$(ASTCFLAGS)" sh build_tools/mkpkgconfig $(DESTDIR)/usr/lib/pkgconfig; fi
# Should static HTTP be installed during make samples or even with its own target ala
# webvoicemail? There are portions here that *could* be customized but might also be
# improved a lot. I'll put it here for now.
mkdir -p $(DESTDIR)$(ASTDATADIR)/phoneprov
for x in phoneprov/*; do \
$(INSTALL) -m 644 $$x $(DESTDIR)$(ASTDATADIR)/phoneprov ; \
done
mkdir -p $(DESTDIR)$(ASTDATADIR)/static-http
for x in static-http/*; do \
$(INSTALL) -m 644 $$x $(DESTDIR)$(ASTDATADIR)/static-http ; \
done
if [ -d doc/tex/asterisk ] ; then \
mkdir -p $(DESTDIR)$(ASTDATADIR)/static-http/docs ; \
for n in doc/tex/asterisk/* ; do \
$(INSTALL) -m 644 $$n $(DESTDIR)$(ASTDATADIR)/static-http/docs ; \
done \
fi
mkdir -p $(DESTDIR)$(ASTDATADIR)/images
for x in images/*.jpg; do \
$(INSTALL) -m 644 $$x $(DESTDIR)$(ASTDATADIR)/images ; \
done
mkdir -p $(DESTDIR)$(AGI_DIR)
$(MAKE) -C sounds install
update:
@if [ -d .svn ]; then \
echo "Updating from Subversion..." ; \
fromrev="`svn info | $(AWK) '/Revision: / {print $$2}'`"; \
svn update | tee update.out; \
torev="`svn info | $(AWK) '/Revision: / {print $$2}'`"; \
echo "`date` Updated from revision $${fromrev} to $${torev}." >> update.log; \
rm -f .version; \
if [ `grep -c ^C update.out` -gt 0 ]; then \
echo ; echo "The following files have conflicts:" ; \
grep ^C update.out | cut -b4- ; \
fi ; \
rm -f update.out; \
else \
echo "Not under version control"; \
fi
NEWHEADERS=$(notdir $(wildcard include/asterisk/*.h))
OLDHEADERS=$(filter-out $(NEWHEADERS),$(notdir $(wildcard $(DESTDIR)$(ASTHEADERDIR)/*.h)))
bininstall: _all
mkdir -p $(DESTDIR)$(MODULES_DIR)
mkdir -p $(DESTDIR)$(ASTSBINDIR)
mkdir -p $(DESTDIR)$(ASTETCDIR)
mkdir -p $(DESTDIR)$(ASTBINDIR)
mkdir -p $(DESTDIR)$(ASTVARRUNDIR)
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/voicemail
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/dictate
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/system
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/tmp
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/meetme
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/monitor
$(INSTALL) -m 755 main/asterisk $(DESTDIR)$(ASTSBINDIR)/
$(LN) -sf asterisk $(DESTDIR)$(ASTSBINDIR)/rasterisk
$(INSTALL) -m 755 contrib/scripts/astgenkey $(DESTDIR)$(ASTSBINDIR)/
$(INSTALL) -m 755 contrib/scripts/autosupport $(DESTDIR)$(ASTSBINDIR)/
if [ ! -f $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ]; then \
cat contrib/scripts/safe_asterisk | sed 's|__ASTERISK_SBIN_DIR__|$(ASTSBINDIR)|;s|__ASTERISK_VARRUN_DIR__|$(ASTVARRUNDIR)|;' > $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ;\
chmod 755 $(DESTDIR)$(ASTSBINDIR)/safe_asterisk;\
fi
$(INSTALL) -d $(DESTDIR)$(ASTHEADERDIR)
$(INSTALL) -m 644 include/asterisk.h $(DESTDIR)$(includedir)
$(INSTALL) -m 644 include/asterisk/*.h $(DESTDIR)$(ASTHEADERDIR)
if [ -n "$(OLDHEADERS)" ]; then \
rm -f $(addprefix $(DESTDIR)$(ASTHEADERDIR)/,$(OLDHEADERS)) ;\
fi
mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-csv
mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-custom
mkdir -p $(DESTDIR)$(ASTDATADIR)/keys
mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware
mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware/iax
mkdir -p $(DESTDIR)$(ASTMANDIR)/man8
$(INSTALL) -m 644 keys/iaxtel.pub $(DESTDIR)$(ASTDATADIR)/keys
$(INSTALL) -m 644 keys/freeworlddialup.pub $(DESTDIR)$(ASTDATADIR)/keys
$(INSTALL) -m 644 doc/asterisk.8 $(DESTDIR)$(ASTMANDIR)/man8
$(INSTALL) -m 644 contrib/scripts/astgenkey.8 $(DESTDIR)$(ASTMANDIR)/man8
$(INSTALL) -m 644 contrib/scripts/autosupport.8 $(DESTDIR)$(ASTMANDIR)/man8
$(INSTALL) -m 644 contrib/scripts/safe_asterisk.8 $(DESTDIR)$(ASTMANDIR)/man8
if [ -f contrib/firmware/iax/iaxy.bin ] ; then \
$(INSTALL) -m 644 contrib/firmware/iax/iaxy.bin $(DESTDIR)$(ASTDATADIR)/firmware/iax/iaxy.bin; \
fi
$(SUBDIRS_INSTALL):
@DESTDIR="$(DESTDIR)" ASTSBINDIR="$(ASTSBINDIR)" $(MAKE) --quiet $(PRINT_DIR) -C $(@:-install=) install
NEWMODS:=$(foreach d,$(MOD_SUBDIRS),$(notdir $(wildcard $(d)/*.so)))
OLDMODS=$(filter-out $(NEWMODS),$(notdir $(wildcard $(DESTDIR)$(MODULES_DIR)/*.so)))
oldmodcheck:
@if [ -n "$(OLDMODS)" ]; then \
echo " WARNING WARNING WARNING" ;\
echo "" ;\
echo " Your Asterisk modules directory, located at" ;\
echo " $(DESTDIR)$(MODULES_DIR)" ;\
echo " contains modules that were not installed by this " ;\
echo " version of Asterisk. Please ensure that these" ;\
echo " modules are compatible with this version before" ;\
echo " attempting to run Asterisk." ;\
echo "" ;\
for f in $(OLDMODS); do \
echo " $$f" ;\
done ;\
echo "" ;\
echo " WARNING WARNING WARNING" ;\
fi
install: datafiles bininstall $(SUBDIRS_INSTALL)
@if [ -x /usr/sbin/asterisk-post-install ]; then \
/usr/sbin/asterisk-post-install $(DESTDIR) . ; \
fi
@echo " +---- Asterisk Installation Complete -------+"
@echo " + +"
@echo " + YOU MUST READ THE SECURITY DOCUMENT +"
@echo " + +"
@echo " + Asterisk has successfully been installed. +"
@echo " + If you would like to install the sample +"
@echo " + configuration files (overwriting any +"
@echo " + existing config files), run: +"
@echo " + +"
@echo " + $(mK) samples +"
@echo " + +"
@echo " +----------------- or ---------------------+"
@echo " + +"
@echo " + You can go ahead and install the asterisk +"
@echo " + program documentation now or later run: +"
@echo " + +"
@echo " + $(mK) progdocs +"
@echo " + +"
@echo " + **Note** This requires that you have +"
@echo " + doxygen installed on your local system +"
@echo " +-------------------------------------------+"
@$(MAKE) -s oldmodcheck
upgrade: bininstall
# XXX why *.adsi is installed first ?
adsi:
@echo Installing adsi config files...
@mkdir -p $(DESTDIR)$(ASTETCDIR)
@for x in configs/*.adsi; do \
dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x`" ; \
if [ -f $${dst} ] ; then \
echo "Overwriting $$x" ; \
else \
echo "Installing $$x" ; \
fi ; \
$(INSTALL) -m 644 $$x $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x` ; \
done
samples: adsi
@echo Installing other config files...
@mkdir -p $(DESTDIR)$(ASTETCDIR)
@for x in configs/*.sample; do \
dst="$(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample`" ; \
if [ -f $${dst} ]; then \
if [ "$(OVERWRITE)" = "y" ]; then \
if cmp -s $${dst} $$x ; then \
echo "Config file $$x is unchanged"; \
continue; \
fi ; \
mv -f $${dst} $${dst}.old ; \
else \
echo "Skipping config file $$x"; \
continue; \
fi ;\
fi ; \
echo "Installing file $$x"; \
$(INSTALL) -m 644 $$x $${dst} ;\
done
@if [ "$(OVERWRITE)" = "y" ] || [ ! -f $(DESTDIR)$(ASTCONFPATH) ]; then \
echo "Creating asterisk.conf"; \
( \
echo "[directories](!) ; remove the (!) to enable this" ; \
echo "astetcdir => $(ASTETCDIR)" ; \
echo "astmoddir => $(MODULES_DIR)" ; \
echo "astvarlibdir => $(ASTVARLIBDIR)" ; \
echo "astdbdir => $(ASTDBDIR)" ; \
echo "astkeydir => $(ASTKEYDIR)" ; \
echo "astdatadir => $(ASTDATADIR)" ; \
echo "astagidir => $(AGI_DIR)" ; \
echo "astspooldir => $(ASTSPOOLDIR)" ; \
echo "astrundir => $(ASTVARRUNDIR)" ; \
echo "astlogdir => $(ASTLOGDIR)" ; \
echo "" ; \
echo ";[options]" ; \
echo ";verbose = 3" ; \
echo ";debug = 3" ; \
echo ";alwaysfork = yes ; same as -F at startup" ; \
echo ";nofork = yes ; same as -f at startup" ; \
echo ";quiet = yes ; same as -q at startup" ; \
echo ";timestamp = yes ; same as -T at startup" ; \
echo ";execincludes = yes ; support #exec in config files" ; \
echo ";console = yes ; Run as console (same as -c at startup)" ; \
echo ";highpriority = yes ; Run realtime priority (same as -p at startup)" ; \
echo ";initcrypto = yes ; Initialize crypto keys (same as -i at startup)" ; \
echo ";nocolor = yes ; Disable console colors" ; \
echo ";dontwarn = yes ; Disable some warnings" ; \
echo ";dumpcore = yes ; Dump core on crash (same as -g at startup)" ; \
echo ";languageprefix = yes ; Use the new sound prefix path syntax" ; \
echo ";internal_timing = yes" ; \
echo ";systemname = my_system_name ; prefix uniqueid with a system name for global uniqueness issues" ; \
echo ";autosystemname = yes ; automatically set systemname to hostname - uses 'localhost' on failure, or systemname if set" ; \
echo ";maxcalls = 10 ; Maximum amount of calls allowed" ; \
echo ";maxload = 0.9 ; Asterisk stops accepting new calls if the load average exceed this limit" ; \
echo ";maxfiles = 1000 ; Maximum amount of openfiles" ; \
echo ";minmemfree = 1 ; in MBs, Asterisk stops accepting new calls if the amount of free memory falls below this watermark" ; \
echo ";cache_record_files = yes ; Cache recorded sound files to another directory during recording" ; \
echo ";record_cache_dir = /tmp ; Specify cache directory (used in cnjunction with cache_record_files)" ; \
echo ";transmit_silence_during_record = yes ; Transmit SLINEAR silence while a channel is being recorded" ; \
echo ";transcode_via_sln = yes ; Build transcode paths via SLINEAR, instead of directly" ; \
echo ";runuser = asterisk ; The user to run as" ; \
echo ";rungroup = asterisk ; The group to run as" ; \
echo "" ; \
echo "; Changing the following lines may compromise your security." ; \
echo ";[files]" ; \
echo ";astctlpermissions = 0660" ; \
echo ";astctlowner = root" ; \
echo ";astctlgroup = apache" ; \
echo ";astctl = asterisk.ctl" ; \
) > $(DESTDIR)$(ASTCONFPATH) ; \
else \
echo "Skipping asterisk.conf creation"; \
fi
mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/voicemail/default/1234/INBOX
build_tools/make_sample_voicemail $(DESTDIR)/$(ASTDATADIR) $(DESTDIR)/$(ASTSPOOLDIR)
webvmail:
@[ -d $(DESTDIR)$(HTTP_DOCSDIR)/ ] || ( printf "http docs directory not found.\nUpdate assignment of variable HTTP_DOCSDIR in Makefile!\n" && exit 1 )
@[ -d $(DESTDIR)$(HTTP_CGIDIR) ] || ( printf "cgi-bin directory not found.\nUpdate assignment of variable HTTP_CGIDIR in Makefile!\n" && exit 1 )
$(INSTALL) -m 4755 -o root -g root contrib/scripts/vmail.cgi $(DESTDIR)$(HTTP_CGIDIR)/vmail.cgi
mkdir -p $(DESTDIR)$(HTTP_DOCSDIR)/_asterisk
for x in images/*.gif; do \
$(INSTALL) -m 644 $$x $(DESTDIR)$(HTTP_DOCSDIR)/_asterisk/; \
done
@echo " +--------- Asterisk Web Voicemail ----------+"
@echo " + +"
@echo " + Asterisk Web Voicemail is installed in +"
@echo " + your cgi-bin directory: +"
@echo " + $(DESTDIR)$(HTTP_CGIDIR)"
@echo " + IT USES A SETUID ROOT PERL SCRIPT, SO +"
@echo " + IF YOU DON'T LIKE THAT, UNINSTALL IT! +"
@echo " + +"
@echo " + Other static items have been stored in: +"
@echo " + $(DESTDIR)$(HTTP_DOCSDIR)"
@echo " + +"
@echo " + If these paths do not match your httpd +"
@echo " + installation, correct the definitions +"
@echo " + in your Makefile of HTTP_CGIDIR and +"
@echo " + HTTP_DOCSDIR +"
@echo " + +"
@echo " +-------------------------------------------+"
spec:
sed "s/^Version:.*/Version: $(RPMVERSION)/g" redhat/asterisk.spec > asterisk.spec ; \
rpm: __rpm
__rpm: main/version.c include/asterisk/buildopts.h spec
rm -rf /tmp/asterisk ; \
mkdir -p /tmp/asterisk/redhat/RPMS/i386 ; \
$(MAKE) DESTDIR=/tmp/asterisk install ; \
$(MAKE) DESTDIR=/tmp/asterisk samples ; \
mkdir -p /tmp/asterisk/etc/rc.d/init.d ; \
cp -f contrib/init.d/rc.redhat.asterisk /tmp/asterisk/etc/rc.d/init.d/asterisk ; \
rpmbuild --rcfile /usr/lib/rpm/rpmrc:redhat/rpmrc -bb asterisk.spec
progdocs:
(cat contrib/asterisk-ng-doxygen; echo "HAVE_DOT=$(HAVEDOT)"; \
echo "PROJECT_NUMBER=$(ASTERISKVERSION)") | doxygen -
config:
@if [ "${OSARCH}" = "linux-gnu" ]; then \
if [ -f /etc/redhat-release -o -f /etc/fedora-release ]; then \
$(INSTALL) -m 755 contrib/init.d/rc.redhat.asterisk $(DESTDIR)/etc/rc.d/init.d/asterisk; \
if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \
elif [ -f /etc/debian_version ]; then \
$(INSTALL) -m 755 contrib/init.d/rc.debian.asterisk $(DESTDIR)/etc/init.d/asterisk; \
if [ -z "$(DESTDIR)" ]; then /usr/sbin/update-rc.d asterisk start 50 2 3 4 5 . stop 91 2 3 4 5 .; fi; \
elif [ -f /etc/gentoo-release ]; then \
$(INSTALL) -m 755 contrib/init.d/rc.gentoo.asterisk $(DESTDIR)/etc/init.d/asterisk; \
if [ -z "$(DESTDIR)" ]; then /sbin/rc-update add asterisk default; fi; \
elif [ -f /etc/mandrake-release -o -f /etc/mandriva-release ]; then \
$(INSTALL) -m 755 contrib/init.d/rc.mandrake.asterisk $(DESTDIR)/etc/rc.d/init.d/asterisk; \
if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \
elif [ -f /etc/SuSE-release -o -f /etc/novell-release ]; then \
$(INSTALL) -m 755 contrib/init.d/rc.suse.asterisk $(DESTDIR)/etc/init.d/asterisk; \
if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \
elif [ -f /etc/slackware-version ]; then \
echo "Slackware is not currently supported, although an init script does exist for it." \
else \
echo "We could not install init scripts for your distribution."; \
fi \
else \
echo "We could not install init scripts for your operating system."; \
fi
sounds:
$(MAKE) -C sounds all
# If the cleancount has been changed, force a make clean.
# .cleancount is the global clean count, and .lastclean is the
# last clean count we had
cleantest:
@cmp -s .cleancount .lastclean || $(MAKE) clean
$(SUBDIRS_UNINSTALL):
@$(MAKE) $(PRINT_DIR) -C $(@:-uninstall=) uninstall
_uninstall: $(SUBDIRS_UNINSTALL)
rm -f $(DESTDIR)$(MODULES_DIR)/*
rm -f $(DESTDIR)$(ASTSBINDIR)/*asterisk*
rm -f $(DESTDIR)$(ASTSBINDIR)/astgenkey
rm -f $(DESTDIR)$(ASTSBINDIR)/autosupport
rm -rf $(DESTDIR)$(ASTHEADERDIR)
rm -rf $(DESTDIR)$(ASTDATADIR)/firmware
rm -f $(DESTDIR)$(ASTMANDIR)/man8/asterisk.8
rm -f $(DESTDIR)$(ASTMANDIR)/man8/astgenkey.8
rm -f $(DESTDIR)$(ASTMANDIR)/man8/autosupport.8
rm -f $(DESTDIR)$(ASTMANDIR)/man8/safe_asterisk.8
$(MAKE) -C sounds uninstall
uninstall: _uninstall
@echo " +--------- Asterisk Uninstall Complete -----+"
@echo " + Asterisk binaries, sounds, man pages, +"
@echo " + headers, modules, and firmware builds, +"
@echo " + have all been uninstalled. +"
@echo " + +"
@echo " + To remove ALL traces of Asterisk, +"
@echo " + including configuration, spool +"
@echo " + directories, and logs, run the following +"
@echo " + command: +"
@echo " + +"
@echo " + $(mK) uninstall-all +"
@echo " +-------------------------------------------+"
uninstall-all: _uninstall
rm -rf $(DESTDIR)$(ASTLIBDIR)
rm -rf $(DESTDIR)$(ASTVARLIBDIR)
rm -rf $(DESTDIR)$(ASTDATADIR)
rm -rf $(DESTDIR)$(ASTSPOOLDIR)
rm -rf $(DESTDIR)$(ASTETCDIR)
rm -rf $(DESTDIR)$(ASTLOGDIR)
menuconfig: menuselect
gmenuconfig: gmenuselect
menuselect: menuselect/menuselect menuselect-tree
-@menuselect/menuselect $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts && (echo "menuselect changes saved!"; rm -f channels/h323/Makefile.ast main/asterisk) || echo "menuselect changes NOT saved!"
gmenuselect: menuselect/gmenuselect menuselect-tree
-@menuselect/gmenuselect $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts && (echo "menuselect changes saved!"; rm -f channels/h323/Makefile.ast main/asterisk) || echo "menuselect changes NOT saved!"
# options for make in menuselect/
MAKE_MENUSELECT=CC="$(HOST_CC)" CXX="$(CXX)" LD="" AR="" RANLIB="" CFLAGS="" $(MAKE) -C menuselect CONFIGURE_SILENT="--silent"
menuselect/menuselect: menuselect/makeopts
$(MAKE_MENUSELECT)
menuselect/gmenuselect: menuselect/makeopts
$(MAKE_MENUSELECT) gmenuselect
menuselect/makeopts:
$(MAKE_MENUSELECT) makeopts
menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(dir)/*.c) $(wildcard $(dir)/*.cc)) build_tools/cflags.xml sounds/sounds.xml build_tools/embed_modules.xml configure
@echo "Generating input for menuselect ..."
@echo "<?xml version=\"1.0\"?>" > $@
@echo >> $@
@echo "<menu name=\"Asterisk Module and Build Option Selection\">" >> $@
@for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SUBMAKE) -C $${dir} SUBDIR=$${dir} moduleinfo >> $@; done
@for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SUBMAKE) -C $${dir} SUBDIR=$${dir} makeopts >> $@; done
@cat build_tools/cflags.xml >> $@
@cat build_tools/embed_modules.xml >> $@
@cat sounds/sounds.xml >> $@
@echo "</menu>" >> $@
pdf: asterisk.pdf
asterisk.pdf:
$(MAKE) -C doc/tex asterisk.pdf
.PHONY: menuselect main sounds clean dist-clean distclean all prereqs cleantest uninstall _uninstall uninstall-all pdf dont-optimize $(SUBDIRS_INSTALL) $(SUBDIRS_DIST_CLEAN) $(SUBDIRS_CLEAN) $(SUBDIRS_UNINSTALL) $(SUBDIRS) $(MOD_SUBDIRS_EMBED_LDSCRIPT) $(MOD_SUBDIRS_EMBED_LDFLAGS) $(MOD_SUBDIRS_EMBED_LIBS) main/version.c

166
trunk/Makefile.moddir_rules Normal file
View File

@@ -0,0 +1,166 @@
#
# 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
#
# Makefile rules for building modules.
# In most cases, we set target-specific variables for certain targets
# (remember that they apply recursively to prerequisites).
# Also note that we can only set one variable per rule, so we have to
# repeat the left hand side to set multiple variables.
ifneq ($(findstring MALLOC_DEBUG,$(MENUSELECT_CFLAGS)),)
ifeq ($(findstring astmm.h,$(ASTCFLAGS)),)
ASTCFLAGS+=-include $(ASTTOPDIR)/include/asterisk/astmm.h
endif
endif
ifeq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
ASTCFLAGS+=${GC_CFLAGS}
endif
ifneq ($(findstring STATIC_BUILD,$(MENUSELECT_CFLAGS)),)
STATIC_BUILD=-static
endif
include $(ASTTOPDIR)/Makefile.rules
# If MODULE_PREFIX is defined, use it to run the standard functions to set
# C_MODS, CC_MODS, LOADABLE_MODS and EMBEDDED_MODS.
# Each word of MODULE_PREFIX is a prefix for filenames that we consider
# valid C or CC modules (eg. app, func ...). Note that the underscore
# is added here, and does not need to be in MODULE_PREFIX
#
# Use MODULE_EXCLUDE to specify additional modules to exclude.
ifneq ($(MODULE_PREFIX),)
ALL_C_MODS:=
ALL_C_MODS+=$(foreach p,$(MODULE_PREFIX),$(patsubst %.c,%,$(wildcard $(p)_*.c)))
ALL_CC_MODS:=
ALL_CC_MODS+=$(foreach p,$(MODULE_PREFIX),$(patsubst %.cc,%,$(wildcard $(p)_*.cc)))
C_MODS:=$(filter-out $(MENUSELECT_$(MENUSELECT_CATEGORY)),$(ALL_C_MODS))
CC_MODS:=$(filter-out $(MENUSELECT_$(MENUSELECT_CATEGORY)),$(ALL_CC_MODS))
# and store in the list of embedded or loadable modules
ifneq ($(findstring $(MENUSELECT_CATEGORY),$(MENUSELECT_EMBED)),)
EMBEDDED_MODS:=$(C_MODS) $(CC_MODS)
else
LOADABLE_MODS:=$(C_MODS) $(CC_MODS)
endif
endif
# Both C++ and C++ sources need their module name in AST_MODULE
# We also pass whatever _INCLUDE list is generated by menuselect
# (they are stored in file 'makeopts')
$(addsuffix .oo,$(CC_MODS)) $(addsuffix .o,$(C_MODS)): \
ASTCFLAGS+= -DAST_MODULE=\"$*\" $(MENUSELECT_OPTS_$*:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_INCLUDE))
ifeq ($(findstring $(OSARCH), mingw32 cygwin ),)
# don't define -fPIC on mingw32 and cygwin, it is the default
$(LOADABLE_MODS:%=%.so): ASTCFLAGS+=-fPIC
endif
# For loadable modules, pass _LIB and _LDFLAGS from menuselect.
$(LOADABLE_MODS:%=%.so): LIBS+=$(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_LIB))
$(LOADABLE_MODS:%=%.so): ASTLDFLAGS+=$(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_LDFLAGS))
$(EMBEDDED_MODS:%=%.o): ASTCFLAGS+=-DEMBEDDED_MODULE=$*
$(addsuffix .so,$(filter $(LOADABLE_MODS),$(C_MODS))): %.so: %.o
$(addsuffix .so,$(filter $(LOADABLE_MODS),$(CC_MODS))): %.so: %.oo
modules.link: $(addsuffix .eo,$(filter $(EMBEDDED_MODS),$(C_MODS)))
.PHONY: clean uninstall _all moduleinfo makeopts
ifneq ($(LOADABLE_MODS),)
_all: $(LOADABLE_MODS:%=%.so)
ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
# linker options and extra libraries for cygwin
SOLINK=-Wl,--out-implib=lib$@.a -shared
LIBS+=-L$(ASTTOPDIR)/main -lasterisk -L$(ASTTOPDIR)/res $($@_LIBS)
# additional libraries in res/
endif
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 %.eo,$^)); do echo "INPUT (../$${file})" >> $@; done
@for file in $(patsubst %,$(SUBDIR)/%,$(filter-out %.eo,$^)); do echo "INPUT (../$${file})" >> $@; done
clean::
rm -f *.so *.o *.oo *.eo
rm -f .*.o.d .*.oo.d
rm -f *.s *.i
rm -f modules.link
install:: all
@echo "Installing modules from `basename $(CURDIR)`..."
@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

98
trunk/Makefile.rules Normal file
View File

@@ -0,0 +1,98 @@
#
# Asterisk -- A telephony toolkit for Linux.
#
# Makefile rules
#
# 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
#
# Rules for various build phases.
# 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
# extra cflags to build dependencies. Recursively expanded.
MAKE_DEPS= -MMD -MT $@ -MF .$(subst /,_,$@).d -MP
ifeq ($(NOISY_BUILD),)
ECHO_PREFIX=@
CMD_PREFIX=@
else
ECHO_PREFIX=@\#
CMD_PREFIX=
endif
ifeq ($(findstring DONT_OPTIMIZE,$(MENUSELECT_CFLAGS)),)
# More GSM codec optimization
# Uncomment to enable MMXTM optimizations for x86 architecture CPU's
# which support MMX instructions. This should be newer pentiums,
# ppro's, etc, as well as the AMD K6 and K7.
#K6OPT=-DK6OPT
OPTIMIZE?=-O6
ASTCFLAGS+=$(OPTIMIZE)
endif
# build rules for various targets
%.o: %.c
$(ECHO_PREFIX) echo " [CC] $< -> $@"
$(CMD_PREFIX) $(CC) -o $@ -c $< $(PTHREAD_CFLAGS) $(ASTCFLAGS) $(MAKE_DEPS)
%.o: %.i
$(ECHO_PREFIX) echo " [CCi] $< -> $@"
$(CMD_PREFIX) $(CC) -o $@ -c $< $(PTHREAD_CFLAGS) $(ASTCFLAGS) $(MAKE_DEPS)
%.i: %.c
$(ECHO_PREFIX) echo " [CPP] $< -> $@"
$(CMD_PREFIX) $(CC) -o $@ -E $< $(PTHREAD_CFLAGS) $(ASTCFLAGS) $(MAKE_DEPS)
%.o: %.s
$(ECHO_PREFIX) echo " [AS] $< -> $@"
$(CMD_PREFIX) $(CC) -o $@ -c $< $(PTHREAD_CFLAGS) $(ASTCFLAGS) $(MAKE_DEPS)
%.oo: %.cc
$(ECHO_PREFIX) echo " [CXX] $< -> $@"
$(CMD_PREFIX) $(CXX) -o $@ -c $< $(PTHREAD_CFLAGS) $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations,$(ASTCFLAGS)) $(MAKE_DEPS)
%.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 $@ $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) $^ $(PTHREAD_LIBS) $(LIBS)
%.so: %.oo
$(ECHO_PREFIX) echo " [LDXX] $^ -> $@"
$(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) $^ $(PTHREAD_LIBS) $(LIBS)
%.eo: %.o
$(ECHO_PREFIX) echo " [EMBED] $< -> $@"
$(CMD_PREFIX) $(ASTTOPDIR)/build_tools/make_linker_eo_script $* > .$@.ld
$(CMD_PREFIX) $(LD) -r -T .$@.ld -o $@ $<
$(CMD_PREFIX) rm -f .$@.ld
%.eo: %.oo
$(ECHO_PREFIX) echo " [EMBED] $< -> $@"
$(CMD_PREFIX) $(ASTTOPDIR)/build_tools/make_linker_eo_script $* > .$@.ld
$(CMD_PREFIX) $(LD) -r -T .$@.ld -o $@ $<
$(CMD_PREFIX) rm -f .$@.ld
%: %.o
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
$(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $^ $(PTHREAD_LIBS) $(LIBS)
dist-clean::

262
trunk/README Normal file
View File

@@ -0,0 +1,262 @@
The Asterisk(R) Open Source PBX
by Mark Spencer <markster@digium.com>
and the Asterisk.org developer community
Copyright (C) 2001-2006 Digium, Inc.
and other copyright holders.
================================================================
* 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.
* 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
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
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.
* SUPPORTED OPERATING SYSTEMS
== 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.
== 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:
* 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
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.
* UPGRADING FROM AN EARLIER VERSION
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.
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
the sample PBX, with demonstration extensions, etc. If so, run:
6) "make samples"
Doing so will overwrite any existing config files you have.
Finally, you can launch Asterisk in the foreground mode (not a daemon)
with:
# asterisk -vvvc
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
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
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
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
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,
those of the form 'variable = value', and those of the form 'object =>
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 zapata.conf, one might specify:
switchtype=national
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:
switchtype = national
channel => 1-4
channel => 10-12
switchtype = dms100
channel => 25-47
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
parameters. For example, the line "channel => 25-47" creates objects for
the channels 25 through 47 of the 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
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
by itself use NTP (http://www.ntp.org/) to keep the system clock
synchronized to "real time". NTP is designed to keep the system clock
synchronized by speeding up or slowing down the system clock until it
is synchronized to "real time" rather than by jumping the time and
causing discontinuities. Most Linux distributions include precompiled
versions of NTP. Beware of some time synchronization methods that get
the correct real time periodically and then manually set the system
clock.
Apparent time changes due to daylight savings time are just that,
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.
* FILE DESCRIPTORS
Depending on the size of your system and your configuration,
Asterisk can consume a large number of file descriptors. In UNIX,
file descriptors are used for more than just files on disk. File
descriptors are also used for handling network communication
(e.g. SIP, IAX2, or H.323 calls) and hardware access (e.g. analog and
digital trunk hardware). Asterisk accesses many on-disk files for
everything from configuration information to voicemail storage.
Most systems limit the number of file descriptors that Asterisk can
have open at one time. This can limit the number of simultaneous
calls that your system can handle. For example, if the limit is set
at 1024 (a common default value) Asterisk can handle approxiately 150
SIP calls simultaneously. To change the number of file descriptors
follow the instructions for your system below:
== PAM-based Linux System ==
If your system uses PAM (Pluggable Authentication Modules) edit
/etc/security/limits.conf. Add these lines to the bottom of the file:
root soft nofile 4096
root hard nofile 8196
asterisk soft nofile 4096
asterisk hard nofile 8196
(adjust the numbers to taste). You may need to reboot the system for
these changes to take effect.
== Generic UNIX System ==
If there are no instructions specifically adapted to your system
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.
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
Welcome to the growing worldwide community of Asterisk users!
Mark Spencer
----
Asterisk is a trademark belonging to Digium, inc

163
trunk/UPGRADE.txt Normal file
View File

@@ -0,0 +1,163 @@
Information for Upgrading From Previous Asterisk Releases
=========================================================
AEL:
* Macros are now implemented underneath with the Gosub() application.
Heaven Help You if you wrote code depending on any aspect of this!
Previous to 1.6, macros were implemented with the Macro() app, which
provided a nice feature of auto-returning. The compiler will do its
best to insert a Return() app call at the end of your macro if you did
not include it, but really, you should make sure that all execution
paths within your macros end in "return;".
* The conf2ael program is 'introduced' in this release; it is in a rather
crude state, but deemed useful for making a first pass at converting
extensions.conf code into AEL. More intelligence will come with time.
Core:
* The 'languageprefix' option in asterisk.conf is now deprecated, and
the default sound file layout for non-English sounds is the 'new
style' layout introduced in Asterisk 1.4 (and used by the automatic
sound file installer in the Makefile).
* The ast_expr2 stuff has been modified to handle floating-point numbers.
Numbers of the format D.D are now acceptable input for the expr parser,
Where D is a string of base-10 digits. All math is now done in "long double",
if it is available on your compiler/architecture. This was half-way between
a bug-fix (because the MATH func returns fp by default), and an enhancement.
Also, for those counting on, or needing, integer operations, a series of
'functions' were also added to the expr language, to allow several styles
of rounding/truncation, along with a set of common floating point operations,
like sin, cos, tan, log, pow, etc. The ability to call external functions
like CDR(), etc. was also added, without having to use the ${...} notation.
* The delimiter passed to applications has been changed to the comma (','), as
that is what people are used to using within extensions.conf. If you are
using realtime extensions, you will need to translate your existing dialplan
to use this separator. To use a literal comma, you need merely to escape it
with a backslash ('\'). Another possible side effect is that you may need to
remove the obscene level of backslashing that was necessary for the dialplan
to work correctly in 1.4 and previous versions. This should make writing
dialplans less painful in the future, albeit with the pain of a one-time
conversion.
* The logger.conf option 'rotatetimestamp' has been deprecated in favor of
'rotatestrategy'. This new option supports a 'rotate' strategy that more
closely mimics the system logger in terms of file rotation.
* The concise versions of various CLI commands are now deprecated. We recommend
using the manager interface (AMI) for application integration with Asterisk.
Voicemail:
* The voicemail configuration values 'maxmessage' and 'minmessage' have
been changed to 'maxsecs' and 'minsecs' to clarify their purpose and
to make them more distinguishable from 'maxmsgs', which sets folder
size. The old variables will continue to work in this version, albeit
with a deprecation warning.
* If you use any interface for modifying voicemail aside from the built in
dialplan applications, then the option "pollmailboxes" *must* be set in
voicemail.conf for message waiting indication (MWI) to work properly. This
is because Voicemail notification is now event based instead of polling
based. The channel drivers are no longer responsible for constantly manually
checking mailboxes for changes so that they can send MWI information to users.
Examples of situations that would require this option are web interfaces to
voicemail or an email client in the case of using IMAP storage.
Applications:
* ChanIsAvail() now has a 't' option, which allows the specified device
to be queried for state without consulting the channel drivers. This
performs mostly a 'ChanExists' sort of function.
* SetCallerPres() has been replaced with the CALLERPRES() dialplan function
and is now deprecated.
* DISA()'s fifth argument is now an options argument. If you have previously
used 'NOANSWER' in this argument, you'll need to convert that to the new
option 'n'.
* Macro() is now deprecated. If you need subroutines, you should use the
Gosub()/Return() applications. To replace MacroExclusive(), we have
introduced dialplan functions LOCK(), TRYLOCK(), and UNLOCK(). You may use
these functions in any location where you desire to ensure that only one
channel is executing that path at any one time.
* Read() now sets a READSTATUS variable on exit. It does NOT automatically
return -1 (and hangup) anymore on error. If you want to hangup on error,
you need to do so explicitly in your dialplan.
* Privacy() no longer uses privacy.conf, so any options must be specified
directly in the application arguments.
Dialplan Functions:
* QUEUE_MEMBER_COUNT() has been deprecated in favor of the QUEUE_MEMBER() function. For
more information, issue a "show function QUEUE_MEMBER" from the CLI.
CDR:
* The cdr_sqlite module has been marked as deprecated in favor of
cdr_sqlite3_custom. It will potentially be removed from the tree
after Asterisk 1.6 is released.
* The cdr_odbc module now uses res_odbc to manage its connections. The
username and password parameters in cdr_odbc.conf, therefore, are no
longer used. The dsn parameter now points to an entry in res_odbc.conf.
Formats:
* format_wav: The GAIN preprocessor definition and source code that used it
is removed. This change was made in response to user complaints of
choppiness or the clipping of loud signal peaks. To increase the volume
of voicemail messages, use the 'volgain' option in voicemail.conf
Channel Drivers:
* SIP: a small upgrade to support the "Record" button on the SNOM360,
which sends a sip INFO message with a "Record: on" or "Record: off"
header. If Asterisk is set up (via features.conf) to accept "One Touch Monitor"
requests (by default, via '*1'), then the user-configured dialpad sequence
is generated, and recording can be started and stopped via this button. The
file names and formats are all controlled via the normal mechanisms. If the
user has not configured the automon feature, the normal "415 Unsupported media type"
is returned, and nothing is done.
* SIP: The "call-limit" option is marked as deprecated. It still works in this version of
Asterisk, but will be removed in the following version. Please use the groupcount functions
in the dialplan to enforce call limits. The "limitonpeer" configuration option is
now renamed to "counteronpeer".
* SIP: The "username" option is now renamed to "defaultuser" to match "defaultip".
These are used only before registration to call a peer with the uri
sip:defaultuser@defaultip
The "username" setting still work, but is deprecated and will not work in
the next version of Asterisk.
* chan_local.c: the comma delimiter inside the channel name has been changed to a
semicolon, in order to make the Local channel driver compatible with the comma
delimiter change in applications.
* H323: The "tos" setting has changed name to "tos_audio" and "cos" to "cos_audio"
to be compatible with settings in sip.conf. The "tos" and "cos" configuration
is deprecated and will stop working in the next release of Asterisk.
* Console: A new console channel driver, chan_console, has been added to Asterisk.
This new module can not be loaded at the same time as chan_alsa or chan_oss. The
default modules.conf only loads one of them (chan_oss by default). So, unless you
have modified your modules.conf to not use the autoload option, then you will need
to modify modules.conf to add another "noload" line to ensure that only one of
these three modules gets loaded.
Configuration:
* pbx_dundi.c: tos parameter changed to use new values. Old values like lowdelay,
lowcost and other is not acceptable now. Look into qos.tex for description of
this parameter.
Manager:
* Manager has been upgraded to version 1.1 with a lot of changes.
Please check doc/manager_1_1.txt for information
* The IAXpeers command output has been changed to more closely resemble the
output of the SIPpeers command.
* cdr_manager now reports at the "cdr" level, not at "call" You may need to
change your manager.conf to add the level to existing AMI users, if they
want to see the CDR events generated.

1106
trunk/acinclude.m4 Normal file

File diff suppressed because it is too large Load Diff

82
trunk/agi/DialAnMp3.agi Normal file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/perl
#
# Simple AGI application to play mp3's selected by a user both using
# xmms and over the phone itself.
#
$|=1;
while(<STDIN>) {
chomp;
last unless length($_);
if (/^agi_(\w+)\:\s+(.*)$/) {
$AGI{$1} = $2;
}
}
print STDERR "AGI Environment Dump:\n";
foreach $i (sort keys %AGI) {
print STDERR " -- $i = $AGI{$i}\n";
}
dbmopen(%DIGITS, "/var/lib/asterisk/mp3list", 0644) || die("Unable to open mp3list");;
sub checkresult {
my ($res) = @_;
my $retval;
$tests++;
chomp $res;
if ($res =~ /^200/) {
$res =~ /result=(-?[\w\*\#]+)/;
return $1;
} else {
return -1;
}
}
#print STDERR "1. Playing beep...\n";
#print "STREAM FILE beep \"\"\n";
#$result = <STDIN>;
#checkresult($result);
print STDERR "2. Getting song name...\n";
print "GET DATA demo-enterkeywords\n";
$result = <STDIN>;
$digitstr = checkresult($result);
if ($digitstr < 0) {
exit(1);
}
$digitstr =~ s/\*/ /g;
print STDERR "Resulting songname is $digitstr\n";
@searchwords = split (/\s+/, $digitstr);
print STDERR "Searchwords: " . join(':', @searchwords) . "\n";
foreach $key (sort keys %DIGITS) {
@words = split(/\s+/, $DIGITS{$key});
$match = 1;
foreach $search (@searchwords) {
$match = 0 unless grep(/$search/, @words);
}
if ($match > 0) {
print STDERR "File $key matches\n";
# Play a beep
print "STREAM FILE beep \"\"\n";
system("xmms", $key);
$result = <STDIN>;
if (&checkresult($result) < 0) {
exit 0;
}
print "EXEC MP3Player \"$key\"\n";
# print "WAIT FOR DIGIT 60000\n";
$result = <STDIN>;
if (&checkresult($result) < 0) {
exit 0;
}
print STDERR "Got here...\n";
}
}
print STDERR "4. Testing 'saynumber' of $digitstr...\n";
print "STREAM FILE demo-nomatch\"\"\n";
$result = <STDIN>;
checkresult($result);

52
trunk/agi/Makefile Normal file
View File

@@ -0,0 +1,52 @@
#
# Asterisk -- A telephony toolkit for Linux.
#
# Makefile for AGI-related stuff
#
# Copyright (C) 1999-2006, Digium
#
# Mark Spencer <markster@digium.com>
#
# 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 jukebox.agi
ifeq ($(OSARCH),SunOS)
LIBS+=-lsocket -lnsl
endif
ifeq ($(OSARCH),mingw32)
AGIS:=
endif
include $(ASTTOPDIR)/Makefile.rules
all: $(AGIS)
strcompat.c: ../main/strcompat.c
@cp $< $@
eagi-test: eagi-test.o strcompat.o
eagi-sphinx-test: eagi-sphinx-test.o
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
clean:
rm -f *.so *.o look eagi-test eagi-sphinx-test
rm -f .*.o.d .*.oo.d
rm -f *.s *.i
rm -f strcompat.c
ifneq ($(wildcard .*.d),)
include .*.d
endif

79
trunk/agi/agi-test.agi Normal file
View File

@@ -0,0 +1,79 @@
#!/usr/bin/perl
use strict;
$|=1;
# Setup some variables
my %AGI; my $tests = 0; my $fail = 0; my $pass = 0;
while(<STDIN>) {
chomp;
last unless length($_);
if (/^agi_(\w+)\:\s+(.*)$/) {
$AGI{$1} = $2;
}
}
print STDERR "AGI Environment Dump:\n";
foreach my $i (sort keys %AGI) {
print STDERR " -- $i = $AGI{$i}\n";
}
sub checkresult {
my ($res) = @_;
my $retval;
$tests++;
chomp $res;
if ($res =~ /^200/) {
$res =~ /result=(-?\d+)/;
if (!length($1)) {
print STDERR "FAIL ($res)\n";
$fail++;
} else {
print STDERR "PASS ($1)\n";
$pass++;
}
} else {
print STDERR "FAIL (unexpected result '$res')\n";
$fail++;
}
}
print STDERR "1. Testing 'sendfile'...";
print "STREAM FILE beep \"\"\n";
my $result = <STDIN>;
&checkresult($result);
print STDERR "2. Testing 'sendtext'...";
print "SEND TEXT \"hello world\"\n";
my $result = <STDIN>;
&checkresult($result);
print STDERR "3. Testing 'sendimage'...";
print "SEND IMAGE asterisk-image\n";
my $result = <STDIN>;
&checkresult($result);
print STDERR "4. Testing 'saynumber'...";
print "SAY NUMBER 192837465 \"\"\n";
my $result = <STDIN>;
&checkresult($result);
print STDERR "5. Testing 'waitdtmf'...";
print "WAIT FOR DIGIT 1000\n";
my $result = <STDIN>;
&checkresult($result);
print STDERR "6. Testing 'record'...";
print "RECORD FILE testagi gsm 1234 3000\n";
my $result = <STDIN>;
&checkresult($result);
print STDERR "6a. Testing 'record' playback...";
print "STREAM FILE testagi \"\"\n";
my $result = <STDIN>;
&checkresult($result);
print STDERR "================== Complete ======================\n";
print STDERR "$tests tests completed, $pass passed, $fail failed\n";
print STDERR "==================================================\n";

View File

@@ -0,0 +1,231 @@
/*
* Extended AGI test application
*
* This code is released into public domain
* without any warranty of any kind.
*
*/
/*! \file
* Extended AGI test application
*
* This code is released into public domain
* without any warranty of any kind.
*
* \ingroup agi
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/select.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#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"
#define SPHINX_PORT 3460
static int sphinx_sock = -1;
static int connect_sphinx(void)
{
struct hostent *hp;
struct sockaddr_in sin;
int res;
hp = gethostbyname(SPHINX_HOST);
if (!hp) {
fprintf(stderr, "Unable to resolve '%s'\n", SPHINX_HOST);
return -1;
}
sphinx_sock = socket(PF_INET, SOCK_STREAM, 0);
if (sphinx_sock < 0) {
fprintf(stderr, "Unable to allocate socket: %s\n", strerror(errno));
return -1;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SPHINX_PORT);
memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
if (connect(sphinx_sock, (struct sockaddr *)&sin, sizeof(sin))) {
fprintf(stderr, "Unable to connect on socket: %s\n", strerror(errno));
close(sphinx_sock);
sphinx_sock = -1;
return -1;
}
res = fcntl(sphinx_sock, F_GETFL);
if ((res < 0) || (fcntl(sphinx_sock, F_SETFL, res | O_NONBLOCK) < 0)) {
fprintf(stderr, "Unable to set flags on socket: %s\n", strerror(errno));
close(sphinx_sock);
sphinx_sock = -1;
return -1;
}
return 0;
}
static int read_environment(void)
{
char buf[256];
char *val;
/* Read environment */
for(;;) {
fgets(buf, sizeof(buf), stdin);
if (feof(stdin))
return -1;
buf[strlen(buf) - 1] = '\0';
/* Check for end of environment */
if (!strlen(buf))
return 0;
val = strchr(buf, ':');
if (!val) {
fprintf(stderr, "Invalid environment: '%s'\n", buf);
return -1;
}
*val = '\0';
val++;
val++;
/* Skip space */
fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val);
/* Load into normal environment */
setenv(buf, val, 1);
}
/* Never reached */
return 0;
}
static char *wait_result(void)
{
fd_set fds;
int res;
int max;
static char astresp[256];
static char sphinxresp[256];
char audiobuf[4096];
for (;;) {
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
FD_SET(AUDIO_FILENO, &fds);
max = AUDIO_FILENO;
if (sphinx_sock > -1) {
FD_SET(sphinx_sock, &fds);
if (sphinx_sock > max)
max = sphinx_sock;
}
/* Wait for *some* sort of I/O */
res = select(max + 1, &fds, NULL, NULL, NULL);
if (res < 0) {
fprintf(stderr, "Error in select: %s\n", strerror(errno));
return NULL;
}
if (FD_ISSET(STDIN_FILENO, &fds)) {
fgets(astresp, sizeof(astresp), stdin);
if (feof(stdin)) {
fprintf(stderr, "Got hungup on apparently\n");
return NULL;
}
astresp[strlen(astresp) - 1] = '\0';
fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp);
return astresp;
}
if (FD_ISSET(AUDIO_FILENO, &fds)) {
res = read(AUDIO_FILENO, audiobuf, sizeof(audiobuf));
if (res > 0) {
if (sphinx_sock > -1)
write(sphinx_sock, audiobuf, res);
}
}
if ((sphinx_sock > -1) && FD_ISSET(sphinx_sock, &fds)) {
res = read(sphinx_sock, sphinxresp, sizeof(sphinxresp));
if (res > 0) {
fprintf(stderr, "Oooh, Sphinx found a token: '%s'\n", sphinxresp);
return sphinxresp;
} else if (res == 0) {
fprintf(stderr, "Hrm, lost sphinx, guess we're on our own\n");
close(sphinx_sock);
sphinx_sock = -1;
}
}
}
}
static char *run_command(char *command)
{
fprintf(stdout, "%s\n", command);
return wait_result();
}
static int run_script(void)
{
char *res;
res = run_command("STREAM FILE demo-enterkeywords 0123456789*#");
if (!res) {
fprintf(stderr, "Failed to execute command\n");
return -1;
}
fprintf(stderr, "1. Result is '%s'\n", res);
res = run_command("STREAM FILE demo-nomatch 0123456789*#");
if (!res) {
fprintf(stderr, "Failed to execute command\n");
return -1;
}
fprintf(stderr, "2. Result is '%s'\n", res);
res = run_command("SAY NUMBER 23452345 0123456789*#");
if (!res) {
fprintf(stderr, "Failed to execute command\n");
return -1;
}
fprintf(stderr, "3. Result is '%s'\n", res);
res = run_command("GET DATA demo-enterkeywords");
if (!res) {
fprintf(stderr, "Failed to execute command\n");
return -1;
}
fprintf(stderr, "4. Result is '%s'\n", res);
res = run_command("STREAM FILE auth-thankyou \"\"");
if (!res) {
fprintf(stderr, "Failed to execute command\n");
return -1;
}
fprintf(stderr, "5. Result is '%s'\n", res);
return 0;
}
int main(int argc, char *argv[])
{
char *tmp;
int ver = 0;
int subver = 0;
/* Setup stdin/stdout for line buffering */
setlinebuf(stdin);
setlinebuf(stdout);
if (read_environment()) {
fprintf(stderr, "Failed to read environment: %s\n", strerror(errno));
exit(1);
}
connect_sphinx();
tmp = getenv("agi_enhanced");
if (tmp) {
if (sscanf(tmp, "%d.%d", &ver, &subver) != 2)
ver = 0;
}
if (ver < 1) {
fprintf(stderr, "No enhanced AGI services available. Use EAGI, not AGI\n");
exit(1);
}
if (run_script())
return -1;
exit(0);
}

165
trunk/agi/eagi-test.c Normal file
View File

@@ -0,0 +1,165 @@
/*
* Extended AGI test application
*
* This code is released into the public domain
* with no warranty of any kind
*/
#include "asterisk.h"
#define AUDIO_FILENO (STDERR_FILENO + 1)
/*! \file
* Extended AGI test application
*
* This code is released into the public domain
* with no warranty of any kind
*
* \ingroup agi
*/
static int read_environment(void)
{
char buf[256];
char *val;
/* Read environment */
for(;;) {
fgets(buf, sizeof(buf), stdin);
if (feof(stdin))
return -1;
buf[strlen(buf) - 1] = '\0';
/* Check for end of environment */
if (!strlen(buf))
return 0;
val = strchr(buf, ':');
if (!val) {
fprintf(stderr, "Invalid environment: '%s'\n", buf);
return -1;
}
*val = '\0';
val++;
val++;
/* Skip space */
fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val);
/* Load into normal environment */
setenv(buf, val, 1);
}
/* Never reached */
return 0;
}
static char *wait_result(void)
{
fd_set fds;
int res;
int bytes = 0;
static char astresp[256];
char audiobuf[4096];
for (;;) {
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
FD_SET(AUDIO_FILENO, &fds);
/* Wait for *some* sort of I/O */
res = select(AUDIO_FILENO + 1, &fds, NULL, NULL, NULL);
if (res < 0) {
fprintf(stderr, "Error in select: %s\n", strerror(errno));
return NULL;
}
if (FD_ISSET(STDIN_FILENO, &fds)) {
fgets(astresp, sizeof(astresp), stdin);
if (feof(stdin)) {
fprintf(stderr, "Got hungup on apparently\n");
return NULL;
}
astresp[strlen(astresp) - 1] = '\0';
fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp);
return astresp;
}
if (FD_ISSET(AUDIO_FILENO, &fds)) {
res = read(AUDIO_FILENO, audiobuf, sizeof(audiobuf));
if (res > 0) {
/* XXX Process the audio with sphinx here XXX */
#if 0
fprintf(stderr, "Got %d/%d bytes of audio\n", res, bytes);
#endif
bytes += res;
/* Prentend we detected some audio after 3 seconds */
if (bytes > 16000 * 3) {
return "Sample Message";
bytes = 0;
}
}
}
}
}
static char *run_command(char *command)
{
fprintf(stdout, "%s\n", command);
return wait_result();
}
static int run_script(void)
{
char *res;
res = run_command("STREAM FILE demo-enterkeywords 0123456789*#");
if (!res) {
fprintf(stderr, "Failed to execute command\n");
return -1;
}
fprintf(stderr, "1. Result is '%s'\n", res);
res = run_command("STREAM FILE demo-nomatch 0123456789*#");
if (!res) {
fprintf(stderr, "Failed to execute command\n");
return -1;
}
fprintf(stderr, "2. Result is '%s'\n", res);
res = run_command("SAY NUMBER 23452345 0123456789*#");
if (!res) {
fprintf(stderr, "Failed to execute command\n");
return -1;
}
fprintf(stderr, "3. Result is '%s'\n", res);
res = run_command("GET DATA demo-enterkeywords");
if (!res) {
fprintf(stderr, "Failed to execute command\n");
return -1;
}
fprintf(stderr, "4. Result is '%s'\n", res);
res = run_command("STREAM FILE auth-thankyou \"\"");
if (!res) {
fprintf(stderr, "Failed to execute command\n");
return -1;
}
fprintf(stderr, "5. Result is '%s'\n", res);
return 0;
}
int main(int argc, char *argv[])
{
char *tmp;
int ver = 0;
int subver = 0;
/* Setup stdin/stdout for line buffering */
setlinebuf(stdin);
setlinebuf(stdout);
if (read_environment()) {
fprintf(stderr, "Failed to read environment: %s\n", strerror(errno));
exit(1);
}
tmp = getenv("agi_enhanced");
if (tmp) {
if (sscanf(tmp, "%d.%d", &ver, &subver) != 2)
ver = 0;
}
if (ver < 1) {
fprintf(stderr, "No enhanced AGI services available. Use EAGI, not AGI\n");
exit(1);
}
if (run_script())
return -1;
exit(0);
}

94
trunk/agi/fastagi-test Normal file
View File

@@ -0,0 +1,94 @@
#!/usr/bin/perl
use strict;
use Socket;
use Carp;
use IO::Handle;
my $port = 4573;
$|=1;
# Setup some variables
my %AGI; my $tests = 0; my $fail = 0; my $pass = 0;
sub checkresult {
my ($res) = @_;
my $retval;
$tests++;
chomp $res;
if ($res =~ /^200/) {
$res =~ /result=(-?\d+)/;
if (!length($1)) {
print STDERR "FAIL ($res)\n";
$fail++;
} else {
print STDERR "PASS ($1)\n";
$pass++;
}
} else {
print STDERR "FAIL (unexpected result '$res')\n";
$fail++;
}
}
socket(SERVER, PF_INET, SOCK_STREAM, 0);
setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, pack("l", 1));
bind(SERVER, sockaddr_in($port, INADDR_ANY)) || die("can't bind\n");
listen(SERVER, SOMAXCONN);
for(;;) {
my $raddr = accept(CLIENT, SERVER);
my ($s, $p) = sockaddr_in($raddr);
CLIENT->autoflush(1);
while(<CLIENT>) {
chomp;
last unless length($_);
if (/^agi_(\w+)\:\s+(.*)$/) {
$AGI{$1} = $2;
}
}
print STDERR "AGI Environment Dump from $s:$p --\n";
foreach my $i (sort keys %AGI) {
print STDERR " -- $i = $AGI{$i}\n";
}
print STDERR "1. Testing 'sendfile'...";
print CLIENT "STREAM FILE beep \"\"\n";
my $result = <CLIENT>;
&checkresult($result);
print STDERR "2. Testing 'sendtext'...";
print CLIENT "SEND TEXT \"hello world\"\n";
my $result = <CLIENT>;
&checkresult($result);
print STDERR "3. Testing 'sendimage'...";
print CLIENT "SEND IMAGE asterisk-image\n";
my $result = <CLIENT>;
&checkresult($result);
print STDERR "4. Testing 'saynumber'...";
print CLIENT "SAY NUMBER 192837465 \"\"\n";
my $result = <CLIENT>;
&checkresult($result);
print STDERR "5. Testing 'waitdtmf'...";
print CLIENT "WAIT FOR DIGIT 1000\n";
my $result = <CLIENT>;
&checkresult($result);
print STDERR "6. Testing 'record'...";
print CLIENT "RECORD FILE testagi gsm 1234 3000\n";
my $result = <CLIENT>;
&checkresult($result);
print STDERR "6a. Testing 'record' playback...";
print CLIENT "STREAM FILE testagi \"\"\n";
my $result = <CLIENT>;
&checkresult($result);
close(CLIENT);
print STDERR "================== Complete ======================\n";
print STDERR "$tests tests completed, $pass passed, $fail failed\n";
print STDERR "==================================================\n";
}

488
trunk/agi/jukebox.agi Executable file
View File

@@ -0,0 +1,488 @@
#!/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;
}
}

44
trunk/agi/numeralize Normal file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/perl
#
# Build a database linking filenames to their numerical representations
# using a keypad for the DialAnMp3 application
#
$mp3dir="/usr/media/mpeg3";
dbmopen(%DIGITS, "/var/lib/asterisk/mp3list", 0644) || die("Unable to open mp3list");;
sub process_dir {
my ($dir) = @_;
my $file;
my $digits;
my @entries;
opendir(DIR, $dir);
@entries = readdir(DIR);
closedir(DIR);
foreach $_ (@entries) {
if (!/^\./) {
$file = "$dir/$_";
if (-d "$file") {
process_dir("$file");
} else {
$digits = $_;
$digits =~ s/[^ \w]+//g;
$digits =~ s/\_/ /g;
$digits =~ tr/[a-z]/[A-Z]/;
$digits =~ tr/[A-C]/2/;
$digits =~ tr/[D-F]/3/;
$digits =~ tr/[G-I]/4/;
$digits =~ tr/[J-L]/5/;
$digits =~ tr/[M-O]/6/;
$digits =~ tr/[P-S]/7/;
$digits =~ tr/[T-V]/8/;
$digits =~ tr/[W-Z]/9/;
$digits =~ s/\s+/ /;
print "File: $file, digits: $digits\n";
$DIGITS{$file} = $digits;
}
}
}
}
process_dir($mp3dir);

41
trunk/apps/Makefile Normal file
View File

@@ -0,0 +1,41 @@
#
# Asterisk -- A telephony toolkit for Linux.
#
# Makefile for PBX applications
#
# Copyright (C) 1999-2006, Digium, Inc.
#
# This program is free software, distributed under the terms of
# the GNU General Public License
#
-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
MODULE_PREFIX=app
MENUSELECT_CATEGORY=APPS
MENUSELECT_DESCRIPTION=Applications
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)
endif
ifeq (SunOS,$(shell uname))
MENUSELECT_DEPENDS_app_chanspy+=RT
RT_LIB=-lrt
endif
all: _all
include $(ASTTOPDIR)/Makefile.moddir_rules
ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
LIBS+= -lres_features.so -lres_ael_share.so -lres_monitor.so -lres_speech.so
LIBS+= -lres_smdi.so
endif

1581
trunk/apps/app_adsiprog.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,813 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2004 - 2005 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>
*
* *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
*
* Use at your own risk. Please consult the GNU GPL license document included with Asterisk. *
*
* *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <math.h>
#include <sys/wait.h>
#include <sys/time.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/ulaw.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"
struct event_node{
char data[17];
struct event_node *next;
};
typedef struct event_node event_node_t;
static char *app = "AlarmReceiver";
static char *synopsis = "Provide support for receiving 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";
/* Config Variables */
static int fdtimeout = 2000;
static int sdtimeout = 200;
static int toneloudness = 4096;
static int log_individual_events = 0;
static char event_spool_dir[128] = {'\0'};
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";
/*
* Attempt to access a database variable and increment it,
* provided that the user defined db-family in alarmreceiver.conf
* The alarmreceiver app will write statistics to a few variables
* in this family if it is defined. If the new key doesn't exist in the
* family, then create it and set its value to 1.
*/
static void database_increment( char *key )
{
int res = 0;
unsigned v;
char value[16];
if (ast_strlen_zero(db_family))
return; /* If not defined, don't do anything */
res = ast_db_get(db_family, key, value, sizeof(value) - 1);
if(res){
ast_verb(4, "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
/* Guess we have to create it */
res = ast_db_put(db_family, key, "1");
return;
}
sscanf(value, "%u", &v);
v++;
if(option_verbose >= 4)
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: New value for %s: %u\n", key, v);
snprintf(value, sizeof(value), "%u", v);
res = ast_db_put(db_family, key, value);
if((res)&&(option_verbose >= 4))
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: database_increment write error\n");
return;
}
/*
* Build a MuLaw data block for a single frequency tone
*/
static void make_tone_burst(unsigned char *data, float freq, float loudness, int len, int *x)
{
int i;
float val;
for(i = 0; i < len; i++){
val = loudness * sin((freq * 2.0 * M_PI * (*x)++)/8000.0);
data[i] = AST_LIN2MU((int)val);
}
/* wrap back around from 8000 */
if (*x >= 8000) *x = 0;
return;
}
/*
* Send a single tone burst for a specifed duration and frequency.
* Returns 0 if successful
*/
static int send_tone_burst(struct ast_channel *chan, float freq, int duration, int tldn)
{
int res = 0;
int i = 0;
int x = 0;
struct ast_frame *f, wf;
struct {
unsigned char offset[AST_FRIENDLY_OFFSET];
unsigned char buf[640];
} tone_block;
for(;;)
{
if (ast_waitfor(chan, -1) < 0){
res = -1;
break;
}
f = ast_read(chan);
if (!f){
res = -1;
break;
}
if (f->frametype == AST_FRAME_VOICE) {
wf.frametype = AST_FRAME_VOICE;
wf.subclass = AST_FORMAT_ULAW;
wf.offset = AST_FRIENDLY_OFFSET;
wf.mallocd = 0;
wf.data = tone_block.buf;
wf.datalen = f->datalen;
wf.samples = wf.datalen;
make_tone_burst(tone_block.buf, freq, (float) tldn, wf.datalen, &x);
i += wf.datalen / 8;
if (i > duration) {
ast_frfree(f);
break;
}
if (ast_write(chan, &wf)){
ast_verb(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;
}
}
ast_frfree(f);
}
return res;
}
/*
* 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
* digits.
*
* Returns 0 if all digits successfully received.
* Returns 1 if a digit time out occurred
* Returns -1 if the caller hung up or there was a channel error.
*
*/
static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int length, int fdto, int sdto)
{
int res = 0;
int i = 0;
int r;
struct ast_frame *f;
struct timeval lastdigittime;
lastdigittime = ast_tvnow();
for(;;){
/* if outa time, leave */
if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) >
((i > 0) ? sdto : fdto)){
if(option_verbose >= 4)
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: DTMF Digit Timeout on %s\n", chan->name);
ast_debug(1,"AlarmReceiver: DTMF timeout on chan %s\n",chan->name);
res = 1;
break;
}
if ((r = ast_waitfor(chan, -1) < 0)) {
ast_debug(1, "Waitfor returned %d\n", r);
continue;
}
f = ast_read(chan);
if (f == NULL){
res = -1;
break;
}
/* If they hung up, leave */
if ((f->frametype == AST_FRAME_CONTROL) &&
(f->subclass == AST_CONTROL_HANGUP)){
ast_frfree(f);
res = -1;
break;
}
/* if not DTMF, just do it again */
if (f->frametype != AST_FRAME_DTMF){
ast_frfree(f);
continue;
}
digit_string[i++] = f->subclass; /* save digit */
ast_frfree(f);
/* If we have all the digits we expect, leave */
if(i >= length)
break;
lastdigittime = ast_tvnow();
}
digit_string[i] = '\0'; /* Nul terminate the end of the digit string */
return res;
}
/*
* Write the metadata to the log file
*/
static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan)
{
int res = 0;
struct timeval t;
struct ast_tm now;
char *cl,*cn;
char workstring[80];
char timestamp[80];
/* Extract the caller ID location */
if (chan->cid.cid_num)
ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring));
workstring[sizeof(workstring) - 1] = '\0';
ast_callerid_parse(workstring, &cn, &cl);
if (cl)
ast_shrink_phone_number(cl);
/* Get the current time */
t = ast_tvnow();
ast_localtime(&t, &now, NULL);
/* Format the time */
ast_strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
res = fprintf(logfile, "\n\n[metadata]\n\n");
if(res >= 0)
res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
if(res >= 0)
res = fprintf(logfile, "CALLINGFROM=%s\n", (!cl) ? "<unknown>" : cl);
if(res >- 0)
res = fprintf(logfile, "CALLERNAME=%s\n", (!cn) ? "<unknown>" : cn);
if(res >= 0)
res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
if(res >= 0)
res = fprintf(logfile, "[events]\n\n");
if(res < 0){
if (option_verbose >= 3 )
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't write metadata\n");
ast_debug(1,"AlarmReceiver: can't write metadata\n");
}
else
res = 0;
return res;
}
/*
* Write a single event to the log file
*/
static int write_event( FILE *logfile, event_node_t *event)
{
int res = 0;
if( fprintf(logfile, "%s\n", event->data) < 0)
res = -1;
return res;
}
/*
* If we are configured to log events, do so here.
*
*/
static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event)
{
int res = 0;
char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
int fd;
FILE *logfile;
event_node_t *elp = event;
if (!ast_strlen_zero(event_spool_dir)) {
/* Make a template */
ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
/* Make the temporary file */
fd = mkstemp(workstring);
if(fd == -1) {
if (option_verbose >= 3)
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't make temporary file\n");
ast_debug(1,"AlarmReceiver: can't make temporary file\n");
res = -1;
}
if(!res){
logfile = fdopen(fd, "w");
if(logfile){
/* Write the file */
res = write_metadata(logfile, signalling_type, chan);
if(!res)
while((!res) && (elp != NULL)){
res = write_event(logfile, elp);
elp = elp->next;
}
if(!res){
if(fflush(logfile) == EOF)
res = -1;
if(!res){
if(fclose(logfile) == EOF)
res = -1;
}
}
}
else
res = -1;
}
}
return res;
}
/*
* This function implements the logic to receive the Ademco contact ID format.
*
* The function will return 0 when the caller hangs up, else a -1 if there was a problem.
*/
static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
{
int i,j;
int res = 0;
int checksum;
char event[17];
event_node_t *enew, *elp;
int got_some_digits = 0;
int events_received = 0;
int ack_retries = 0;
static char digit_map[15] = "0123456789*#ABC";
static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15};
database_increment("calls-received");
/* Wait for first event */
if(option_verbose >= 4)
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for first event from panel\n");
while(res >= 0){
if(got_some_digits == 0){
/* Send ACK tone sequence */
if(option_verbose >= 4)
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
res = send_tone_burst(chan, 1400.0, 100, tldn);
if(!res)
res = ast_safe_sleep(chan, 100);
if(!res){
if(option_verbose >= 4)
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
res = send_tone_burst(chan, 2300.0, 100, tldn);
}
}
if( res >= 0)
res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
if (res < 0){
if(events_received == 0)
/* Hangup with no events received should be logged in the DB */
database_increment("no-events-received");
else{
if(ack_retries){
if(option_verbose >= 4)
ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
database_increment("ack-retries");
}
}
if(option_verbose >= 4)
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: App exiting...\n");
res = -1;
break;
}
if(res != 0){
/* Didn't get all of the digits */
if(option_verbose >= 2)
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;
ack_retries++;
}
continue;
}
got_some_digits = 1;
ast_verb(2, "AlarmReceiver: Received Event %s\n", event);
ast_debug(1, "AlarmReceiver: Received event: %s\n", event);
/* Calculate checksum */
for(j = 0, checksum = 0; j < 16; j++){
for(i = 0 ; i < sizeof(digit_map) ; i++){
if(digit_map[i] == event[j])
break;
}
if(i == 16)
break;
checksum += digit_weights[i];
}
if(i == 16){
ast_verb(2, "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
continue; /* Bad character */
}
/* Checksum is mod(15) of the total */
checksum = checksum % 15;
if (checksum) {
database_increment("checksum-errors");
ast_verb(2, "AlarmReceiver: Nonzero checksum\n");
ast_debug(1, "AlarmReceiver: Nonzero checksum\n");
continue;
}
/* Check the message type for correctness */
if(strncmp(event + 4, "18", 2)){
if(strncmp(event + 4, "98", 2)){
database_increment("format-errors");
ast_verb(2, "AlarmReceiver: Wrong message type\n");
ast_debug(1, "AlarmReceiver: Wrong message type\n");
continue;
}
}
events_received++;
/* Queue the Event */
if (!(enew = ast_calloc(1, sizeof(*enew)))) {
res = -1;
break;
}
enew->next = NULL;
ast_copy_string(enew->data, event, sizeof(enew->data));
/*
* Insert event onto end of list
*/
if(*ehead == NULL){
*ehead = enew;
}
else{
for(elp = *ehead; elp->next != NULL; elp = elp->next)
;
elp->next = enew;
}
if(res > 0)
res = 0;
/* Let the user have the option of logging the single event before sending the kissoff tone */
if((res == 0) && (log_individual_events))
res = log_events(chan, ADEMCO_CONTACT_ID, enew);
/* Wait 200 msec before sending kissoff */
if(res == 0)
res = ast_safe_sleep(chan, 200);
/* Send the kissoff tone */
if(res == 0)
res = send_tone_burst(chan, 1400.0, 900, tldn);
}
return res;
}
/*
* This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
* This function will always return 0.
*/
static int alarmreceiver_exec(struct ast_channel *chan, void *data)
{
int res = 0;
event_node_t *elp, *efree;
char signalling_type[64] = "";
event_node_t *event_head = NULL;
/* Set write and read formats to ULAW */
ast_verb(4, "AlarmReceiver: Setting read and write formats to ULAW\n");
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);
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);
return -1;
}
/* Set default values for this invocation of the application */
ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
/* Answer the channel if it is not already */
ast_verb(4, "AlarmReceiver: Answering channel\n");
if (chan->_state != AST_STATE_UP) {
if ((res = ast_answer(chan)))
return -1;
}
/* Wait for the connection to settle post-answer */
ast_verb(4, "AlarmReceiver: Waiting for connection to stabilize\n");
res = ast_safe_sleep(chan, 1250);
/* Attempt to receive the events */
if(!res){
/* Determine the protocol to receive in advance */
/* Note: Ademco contact is the only one supported at this time */
/* Others may be added later */
if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
else
res = -1;
}
/* Events queued by receiver, write them all out here if so configured */
if((!res) && (log_individual_events == 0)){
res = log_events(chan, signalling_type, event_head);
}
/*
* Do we exec a command line at the end?
*/
if((!res) && (!ast_strlen_zero(event_app)) && (event_head)){
ast_debug(1,"Alarmreceiver: executing: %s\n", event_app);
ast_safe_system(event_app);
}
/*
* Free up the data allocated in our linked list
*/
for(elp = event_head; (elp != NULL);){
efree = elp;
elp = elp->next;
ast_free(efree);
}
return 0;
}
/*
* Load the configuration from the configuration file
*/
static int load_config(void)
{
struct ast_config *cfg;
const char *p;
struct ast_flags config_flags = { 0 };
/* Read in the config file */
cfg = ast_config_load(ALMRCV_CONFIG, config_flags);
if(!cfg){
if(option_verbose >= 4)
ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: No config file\n");
return 0;
}
else{
p = ast_variable_retrieve(cfg, "general", "eventcmd");
if(p){
ast_copy_string(event_app, p, sizeof(event_app));
event_app[sizeof(event_app) - 1] = '\0';
}
p = ast_variable_retrieve(cfg, "general", "loudness");
if(p){
toneloudness = atoi(p);
if(toneloudness < 100)
toneloudness = 100;
if(toneloudness > 8192)
toneloudness = 8192;
}
p = ast_variable_retrieve(cfg, "general", "fdtimeout");
if(p){
fdtimeout = atoi(p);
if(fdtimeout < 1000)
fdtimeout = 1000;
if(fdtimeout > 10000)
fdtimeout = 10000;
}
p = ast_variable_retrieve(cfg, "general", "sdtimeout");
if(p){
sdtimeout = atoi(p);
if(sdtimeout < 110)
sdtimeout = 110;
if(sdtimeout > 4000)
sdtimeout = 4000;
}
p = ast_variable_retrieve(cfg, "general", "logindividualevents");
if(p){
log_individual_events = ast_true(p);
}
p = ast_variable_retrieve(cfg, "general", "eventspooldir");
if(p){
ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
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));
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));
db_family[sizeof(db_family) - 1] = '\0';
}
ast_config_destroy(cfg);
}
return 1;
}
/*
* These functions are required to implement an Asterisk App.
*/
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
if(load_config()) {
if (ast_register_application(app, alarmreceiver_exec, synopsis, descrip))
return AST_MODULE_LOAD_FAILURE;
return AST_MODULE_LOAD_SUCCESS;
}
else
return AST_MODULE_LOAD_DECLINE;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");

418
trunk/apps/app_amd.c Normal file
View File

@@ -0,0 +1,418 @@
/*
* 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.
*/
/*! \file
*
* \brief Answering machine detection
*
* \author Claude Klimos (claude.klimos@aheeva.com)
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "asterisk/lock.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],[|maximumWordLength])\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"
"- 'maximumWordLength' is the maximum duration of a word to accept. If exceeded then MACHINE\n"
"This application sets the following channel variables 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"
" MAXWORDLENGTH-<%d consecutiveVoiceDuration>\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;
static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
static 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;
int inInitialSilence = 1;
int inGreeting = 0;
int voiceDuration = 0;
int silenceDuration = 0;
int iTotalTime = 0;
int iWordsCount = 0;
int currentState = STATE_IN_SILENCE;
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 maximumWordLength = dfltMaximumWordLength;
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);
AST_APP_ARG(argMaximumWordLength);
);
ast_verb(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);
if (!ast_strlen_zero(args.argMaximumWordLength))
maximumWordLength = atoi(args.argMaximumWordLength);
} else {
ast_debug(1, "AMD using the default parameters.\n");
}
/* Now we're ready to roll! */
ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
"totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
/* 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, totalAnalysisTime)) > -1) {
/* If we fail to read in a frame, that means they hung up */
if (!(f = ast_read(chan))) {
ast_verb(3, "AMD: Channel [%s]. HANGUP\n", chan->name);
ast_debug(1, "Got hangup\n");
strcpy(amdStatus, "HANGUP");
break;
}
if (f->frametype == AST_FRAME_VOICE) {
/* If the total time exceeds the analysis time then give up as we are not too sure */
framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
iTotalTime += framelength;
if (iTotalTime >= totalAnalysisTime) {
ast_verb(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 */
dspsilence = 0;
ast_dsp_silence(silenceDetector, f, &dspsilence);
if (dspsilence) {
silenceDuration = dspsilence;
if (silenceDuration >= betweenWordsSilence) {
if (currentState != STATE_IN_SILENCE ) {
previousState = currentState;
ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", chan->name);
}
/* Find words less than word duration */
if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", chan->name, consecutiveVoiceDuration);
}
currentState = STATE_IN_SILENCE;
consecutiveVoiceDuration = 0;
}
if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
chan->name, silenceDuration, initialSilence);
ast_frfree(f);
strcpy(amdStatus , "MACHINE");
sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
break;
}
if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
chan->name, silenceDuration, afterGreetingSilence);
ast_frfree(f);
strcpy(amdStatus , "HUMAN");
sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
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++;
ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", chan->name, iWordsCount);
previousState = currentState;
currentState = STATE_IN_WORD;
}
if (consecutiveVoiceDuration >= maximumWordLength){
ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", chan->name, consecutiveVoiceDuration);
ast_frfree(f);
strcpy(amdStatus , "MACHINE");
sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
break;
}
if (iWordsCount >= maximumNumberOfWords) {
ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", chan->name, iWordsCount);
ast_frfree(f);
strcpy(amdStatus , "MACHINE");
sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
break;
}
if (inGreeting == 1 && voiceDuration >= greeting) {
ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", chan->name, voiceDuration, greeting);
ast_frfree(f);
strcpy(amdStatus , "MACHINE");
sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
break;
}
if (voiceDuration >= minimumWordLength ) {
if (silenceDuration > 0)
ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", chan->name, silenceDuration);
silenceDuration = 0;
}
if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0){
/* Only go in here once to change the greeting flag when we detect the 1st word */
if (silenceDuration > 0)
ast_verb(3, "AMD: Channel [%s]. Before Greeting Time: silenceDuration: %d voiceDuration: %d\n", chan->name, silenceDuration, voiceDuration);
inInitialSilence = 0;
inGreeting = 1;
}
}
}
ast_frfree(f);
}
if (!res) {
/* It took too long to get a frame back. Giving up. */
ast_verb(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)
{
isAnsweringMachine(chan, data);
return 0;
}
static int load_config(int reload)
{
struct ast_config *cfg = NULL;
char *cat = NULL;
struct ast_variable *var = NULL;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
if (!(cfg = ast_config_load("amd.conf", config_flags))) {
ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
return -1;
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
return 0;
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 if (!strcasecmp(var->name, "maximum_word_length")) {
dfltMaximumWordLength = 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);
ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
"totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
return 0;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
if (load_config(0))
return AST_MODULE_LOAD_DECLINE;
if (ast_register_application(app, amd_exec, synopsis, descrip))
return AST_MODULE_LOAD_FAILURE;
return AST_MODULE_LOAD_SUCCESS;
}
static int reload(void)
{
if (load_config(1))
return AST_MODULE_LOAD_DECLINE;
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
.load = load_module,
.unload = unload_module,
.reload = reload,
);

View File

@@ -0,0 +1,212 @@
/*
* 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 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/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/astdb.h"
#include "asterisk/utils.h"
enum {
OPT_ACCOUNT = (1 << 0),
OPT_DATABASE = (1 << 1),
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('m', OPT_MULTIPLE),
AST_APP_OPTION('r', OPT_REMOVE),
});
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.\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"
" 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"
;
static int auth_exec(struct ast_channel *chan, void *data)
{
int res = 0, retries, maxdigits;
char passwd[256], *prompt = "agent-pass", *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)) {
ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
return -1;
}
if (chan->_state != AST_STATE_UP) {
if ((res = ast_answer(chan)))
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;
}
/* Start asking for password */
for (retries = 0; retries < 3; retries++) {
if ((res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0)) < 0)
break;
res = 0;
if (arglist.password[0] == '/') {
if (ast_test_flag(&flags,OPT_DATABASE)) {
char tmp[256];
/* Compare against a database key */
if (!ast_db_get(arglist.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);
break;
}
} else {
/* Compare against a file */
FILE *f;
char buf[256] = "", md5passwd[33] = "", *md5secret = NULL;
if (!(f = fopen(arglist.password, "r"))) {
ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
continue;
}
while (!feof(f)) {
fgets(buf, sizeof(buf), f);
if (!feof(f) && !ast_strlen_zero(buf)) {
buf[strlen(buf) - 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;
}
}
}
}
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;
}
}
}
} else {
/* Compare against a fixed password */
if (!strcmp(passwd, arglist.password))
break;
}
prompt = "auth-incorrect";
}
if ((retries < 3) && !res) {
if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE))
ast_cdr_setaccount(chan, passwd);
if (!(res = ast_streamfile(chan, "auth-thankyou", chan->language)))
res = ast_waitstream(chan, "");
} else {
if (!ast_streamfile(chan, "vm-goodbye", chan->language))
res = ast_waitstream(chan, "");
res = -1;
}
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
if (ast_register_application(app, auth_exec, synopsis, descrip))
return AST_MODULE_LOAD_FAILURE;
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");

63
trunk/apps/app_cdr.c Normal file
View File

@@ -0,0 +1,63 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* 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.
*/
/*! \file
*
* \brief Applications connected with CDR engine
*
* \author Martin Pycko <martinp@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/channel.h"
#include "asterisk/module.h"
static char *nocdr_descrip =
" NoCDR(): This application will tell Asterisk not to maintain a CDR for the\n"
"current call.\n";
static char *nocdr_app = "NoCDR";
static char *nocdr_synopsis = "Tell Asterisk to not maintain a CDR for the current call";
static int nocdr_exec(struct ast_channel *chan, void *data)
{
if (chan->cdr)
ast_set_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED);
return 0;
}
static int unload_module(void)
{
return ast_unregister_application(nocdr_app);
}
static int load_module(void)
{
if (ast_register_application(nocdr_app, nocdr_exec, nocdr_synopsis, nocdr_descrip))
return AST_MODULE_LOAD_FAILURE;
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Tell Asterisk to not maintain a CDR for the current call");

View File

@@ -0,0 +1,157 @@
/*
* 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
*
* \brief Check if Channel is Available
*
* \author Mark Spencer <markster@digium.com>
* \author James Golovich <james@gnuinter.net>
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/ioctl.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/devicestate.h"
static char *app = "ChanIsAvail";
static char *synopsis = "Check channel availability";
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.\n"
" Options:\n"
" s - Consider the channel unavailable if the channel is in use at all.\n"
" t - Simply checks if specified channels exist in the channel list\n"
" (implies option s).\n"
"This application sets the following channel variable upon completion:\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";
static int chanavail_exec(struct ast_channel *chan, void *data)
{
int res=-1, inuse=-1, option_state=0, string_compare=0;
int status;
char *info, tmp[512], trychan[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)) {
ast_log(LOG_WARNING, "ChanIsAvail requires an argument (Zap/1&Zap/2)\n");
return -1;
}
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, 't'))
string_compare = 1;
}
peers = args.reqchans;
if (peers) {
cur = peers;
do {
/* remember where to start next time */
rest = strchr(cur, '&');
if (rest) {
*rest = 0;
rest++;
}
tech = cur;
number = strchr(tech, '/');
if (!number) {
ast_log(LOG_WARNING, "ChanIsAvail argument takes format ([technology]/[device])\n");
return -1;
}
*number = '\0';
number++;
if (string_compare) {
/* ast_parse_device_state checks for "SIP/1234" as a channel name.
ast_device_state will ask the SIP driver for the channel state. */
snprintf(trychan, sizeof(trychan), "%s/%s",cur,number);
status = inuse = ast_parse_device_state(trychan);
} else 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))) {
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", "");
}
return 0;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, chanavail_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Check channel availability");

View File

@@ -0,0 +1,93 @@
/*
* 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 "asterisk/file.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"
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;
char *info;
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;
}
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;
}
res = ast_parseable_goto(chan2, args.label);
ast_channel_unlock(chan2);
quit:
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, asyncgoto_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Redirects a given channel to a dialplan target");

730
trunk/apps/app_chanspy.c Normal file
View File

@@ -0,0 +1,730 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
* Copyright (C) 2005 - 2006, 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>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <ctype.h>
#include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/audiohook.h"
#include "asterisk/features.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
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"
" Note: The X option supersedes the three features above in that if a valid\n"
" single digit extension exists in the correct context ChanSpy will\n"
" exit to it. This also disables choosing a channel based on 'chanprefix'\n"
" and a digit sequence.\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' in an optional : delimited list.\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"
" o - Only listen to audio coming from this channel.\n"
" X - Allow the user to exit ChanSpy to a valid single digit\n"
" numeric extension in the current context or the context\n"
" specified by the SPY_EXIT_CONTEXT channel variable. The\n"
" name of the last channel that was spied on will be stored\n"
" in the SPY_CHANNEL variable.\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"
" Note: The X option superseeds the two features above in that if a valid\n"
" single digit extension exists in the correct context it ChanSpy will\n"
" exit to it.\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' in an optional : delimited list.\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"
" o - Only listen to audio coming from this channel.\n"
" X - Allow the user to exit ChanSpy to a valid single digit\n"
" numeric extension in the current context or the context\n"
" specified by the SPY_EXIT_CONTEXT channel variable. The\n"
" name of the last channel that was spied on will be stored\n"
" in the SPY_CHANNEL variable.\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 */
OPTION_READONLY = (1 << 7), /* Don't mix the two channels */
OPTION_EXIT = (1 << 8), /* Exit to a valid single digit extension */
} 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),
AST_APP_OPTION('o', OPTION_READONLY),
AST_APP_OPTION('X', OPTION_EXIT),
});
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 = NULL;
ast_audiohook_lock(&csth->spy_audiohook);
if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
/* Channel is already gone more than likely */
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;
if (ast_write(chan, f)) {
ast_frfree(f);
return -1;
}
if (csth->fd)
write(csth->fd, f->data, f->datalen);
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, struct ast_channel *spychan, struct ast_audiohook *audiohook)
{
int res = 0;
struct ast_channel *peer = NULL;
ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan->name, chan->name);
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;
}
static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd,
const struct ast_flags *flags, char *exitcontext)
{
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;
if (ast_check_hangup(chan) || ast_check_hangup(spyee))
return 0;
name = ast_strdupa(spyee->name);
ast_verb(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, chan, &csth.spy_audiohook)) {
ast_audiohook_destroy(&csth.spy_audiohook);
return 0;
}
if (ast_test_flag(flags, OPTION_WHISPER)) {
ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
start_spying(spyee, chan, &csth.whisper_audiohook);
}
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 (ast_test_flag(flags, OPTION_EXIT)) {
char tmp[2];
tmp[0] = res;
tmp[1] = '\0';
if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
running = -2;
break;
} else {
ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
}
} else if (res >= '0' && res <= '9') {
inp[x++] = res;
}
if (res == '*') {
running = 0;
break;
} else if (res == '#') {
if (!ast_strlen_zero(inp)) {
running = atoi(inp);
break;
}
(*volfactor)++;
if (*volfactor > 4)
*volfactor = -4;
ast_verb(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;
}
}
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);
ast_verb(2, "Done Spying on channel %s\n", name);
return running;
}
static struct ast_channel *next_channel(const struct ast_channel *last, const char *spec,
const char *exten, const char *context)
{
struct ast_channel *this;
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) {
ast_channel_unlock(this);
if (!strncmp(this->name, "Zap/pseudo", 10))
goto redo;
}
return this;
}
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)
{
struct ast_channel *peer, *prev, *next;
char nameprefix[AST_NAME_STRLEN];
char peer_name[AST_NAME_STRLEN + 5];
char exitcontext[AST_MAX_CONTEXT] = "";
signed char zero_volume = 0;
int waitms;
int res;
char *ptr;
int num;
if (ast_test_flag(flags, OPTION_EXIT)) {
const char *c;
if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT")))
ast_copy_string(exitcontext, c, sizeof(exitcontext));
else if (!ast_strlen_zero(chan->macrocontext))
ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
else
ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
}
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 (;;) {
if (!ast_test_flag(flags, OPTION_QUIET)) {
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;
}
if (!ast_strlen_zero(exitcontext)) {
char tmp[2];
tmp[0] = res;
tmp[1] = '\0';
if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
goto exit;
else
ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
}
}
res = ast_waitfordigit(chan, waitms);
if (res < 0) {
ast_clear_flag(chan, AST_FLAG_SPYING);
break;
}
if (!ast_strlen_zero(exitcontext)) {
char tmp[2];
tmp[0] = res;
tmp[1] = '\0';
if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
goto exit;
else
ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
}
/* reset for the next loop around, unless overridden later */
waitms = 100;
peer = prev = next = NULL;
for (peer = next_channel(peer, spec, exten, context);
peer;
prev = peer, peer = next ? next : next_channel(peer, spec, exten, context), next = NULL) {
const char *group;
int igrp = !mygroup;
char *groups[25];
int num_groups = 0;
char *dup_group;
int x;
char *s;
if (peer == prev)
break;
if (peer == chan)
continue;
if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer))
continue;
if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING))
continue;
if (mygroup) {
if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
dup_group = ast_strdupa(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)
continue;
strcpy(peer_name, "spy-");
strncat(peer_name, peer->name, AST_NAME_STRLEN);
ptr = strchr(peer_name, '/');
*ptr++ = '\0';
for (s = peer_name; s < ptr; s++)
*s = tolower(*s);
if (!ast_test_flag(flags, OPTION_QUIET)) {
if (ast_fileexists(peer_name, NULL, NULL) != -1) {
res = ast_streamfile(chan, peer_name, chan->language);
if (!res)
res = ast_waitstream(chan, "");
if (res)
break;
} else
res = ast_say_character_str(chan, peer_name, "", chan->language);
if ((num = atoi(ptr)))
ast_say_digits(chan, atoi(ptr), "", chan->language);
}
waitms = 5000;
res = channel_spy(chan, peer, &volfactor, fd, flags, exitcontext);
if (res == -1) {
goto exit;
} else if (res == -2) {
res = 0;
goto exit;
} else if (res > 1 && spec) {
snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
ast_channel_unlock(next);
} else {
/* stay on this channel */
next = peer;
}
peer = NULL;
}
}
}
exit:
ast_clear_flag(chan, AST_FLAG_SPYING);
ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
return res;
}
static int chanspy_exec(struct ast_channel *chan, void *data)
{
char *mygroup = NULL;
char *recbase = NULL;
int fd = 0;
struct ast_flags flags;
int oldwf = 0;
int volfactor = 0;
int res;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(spec);
AST_APP_ARG(options);
);
char *opts[OPT_ARG_ARRAY_SIZE];
data = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, data);
if (args.spec && !strcmp(args.spec, "all"))
args.spec = NULL;
if (args.options) {
ast_app_parse_options(spy_opts, &flags, opts, args.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], "%d", &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");
return -1;
}
if (recbase) {
char filename[512];
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, AST_FILE_MODE)) <= 0) {
ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
fd = 0;
}
}
res = common_exec(chan, &flags, volfactor, fd, mygroup, args.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");
return res;
}
static int extenspy_exec(struct ast_channel *chan, void *data)
{
char *ptr, *exten = NULL;
char *mygroup = NULL;
char *recbase = NULL;
int fd = 0;
struct ast_flags flags;
int oldwf = 0;
int volfactor = 0;
int res;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(context);
AST_APP_ARG(options);
);
data = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, data);
if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
exten = args.context;
*ptr++ = '\0';
args.context = ptr;
}
if (ast_strlen_zero(args.context))
args.context = ast_strdupa(chan->context);
if (args.options) {
char *opts[OPT_ARG_ARRAY_SIZE];
ast_app_parse_options(spy_opts, &flags, opts, args.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], "%d", &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");
return -1;
}
if (recbase) {
char filename[512];
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, AST_FILE_MODE)) <= 0) {
ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
fd = 0;
}
}
res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, args.context);
if (fd)
close(fd);
if (oldwf && ast_set_write_format(chan, oldwf) < 0)
ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
return res;
}
static int unload_module(void)
{
int res = 0;
res |= ast_unregister_application(app_chan);
res |= ast_unregister_application(app_ext);
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");

View File

@@ -0,0 +1,168 @@
/*
* 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 Trivial application to control playback of a sound file
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/module.h"
static const char *app = "ControlPlayback";
static const 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"
" o(#) - Start at # ms from the beginning of the file.\n"
"This application sets the following channel variables upon completion:\n"
" CPLAYBACKSTATUS - This variable contains the status of the attempt as a text\n"
" string, one of: SUCCESS | USERSTOPPED | ERROR\n"
" CPLAYBACKOFFSET - This contains the offset in ms into the file where\n"
" playback was at when it stopped. -1 is end of file.\n"
" CPLAYBACKSTOPKEY - If the playback is stopped by the user this variable contains\n"
" the key that was pressed.\n";
enum {
OPT_OFFSET = (1 << 1),
};
enum {
OPT_ARG_OFFSET = 0,
/* must stay as the last entry ... */
OPT_ARG_ARRAY_LEN,
};
AST_APP_OPTIONS(cpb_opts, BEGIN_OPTIONS
AST_APP_OPTION_ARG('o', OPT_OFFSET, OPT_ARG_OFFSET),
END_OPTIONS );
static int is_on_phonepad(char key)
{
return key == 35 || key == 42 || (key >= 48 && key <= 57);
}
static int controlplayback_exec(struct ast_channel *chan, void *data)
{
int res = 0;
int skipms = 0;
long offsetms = 0;
char offsetbuf[20];
char stopkeybuf[2];
char *tmp;
struct ast_flags opts = { 0, };
char *opt_args[OPT_ARG_ARRAY_LEN];
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(filename);
AST_APP_ARG(skip);
AST_APP_ARG(fwd);
AST_APP_ARG(rev);
AST_APP_ARG(stop);
AST_APP_ARG(pause);
AST_APP_ARG(restart);
AST_APP_ARG(options);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
return -1;
}
tmp = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, tmp);
if (args.argc < 1) {
ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
return -1;
}
skipms = args.skip ? (atoi(args.skip) ? atoi(args.skip) : 3000) : 3000;
if (!args.fwd || !is_on_phonepad(*args.fwd))
args.fwd = "#";
if (!args.rev || !is_on_phonepad(*args.rev))
args.rev = "*";
if (args.stop && !is_on_phonepad(*args.stop))
args.stop = NULL;
if (args.pause && !is_on_phonepad(*args.pause))
args.pause = NULL;
if (args.restart && !is_on_phonepad(*args.restart))
args.restart = NULL;
if (args.options) {
ast_app_parse_options(cpb_opts, &opts, opt_args, args.options);
if (ast_test_flag(&opts, OPT_OFFSET))
offsetms = atol(opt_args[OPT_ARG_OFFSET]);
}
res = ast_control_streamfile(chan, args.filename, args.fwd, args.rev, args.stop, args.pause, args.restart, skipms, &offsetms);
/* If we stopped on one of our stop keys, return 0 */
if (res > 0 && args.stop && strchr(args.stop, res)) {
pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "USERSTOPPED");
snprintf(stopkeybuf, sizeof(stopkeybuf), "%c", res);
pbx_builtin_setvar_helper(chan, "CPLAYBACKSTOPKEY", stopkeybuf);
res = 0;
} else {
if (res < 0) {
res = 0;
pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "ERROR");
} else
pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "SUCCESS");
}
snprintf(offsetbuf, sizeof(offsetbuf), "%ld", offsetms);
pbx_builtin_setvar_helper(chan, "CPLAYBACKOFFSET", offsetbuf);
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, controlplayback_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Control Playback Application");

139
trunk/apps/app_db.c Normal file
View File

@@ -0,0 +1,139 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
* Copyright (C) 2003, Jefferson Noxon
*
* Mark Spencer <markster@digium.com>
* 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.
*/
/*! \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 "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/astdb.h"
#include "asterisk/lock.h"
/*! \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";
static char *dt_descrip =
" DBdeltree(family[/keytree]): This application will delete a family or keytree\n"
"from the Asterisk database\n";
static char *d_app = "DBdel";
static char *dt_app = "DBdeltree";
static char *d_synopsis = "Delete a key from the database";
static char *dt_synopsis = "Delete a family or keytree from the database";
static int deltree_exec(struct ast_channel *chan, void *data)
{
char *argv, *family, *keytree;
argv = ast_strdupa(data);
if (strchr(argv, '/')) {
family = strsep(&argv, "/");
keytree = strsep(&argv, "\0");
if (!family || !keytree) {
ast_debug(1, "Ignoring; Syntax error in argument\n");
return 0;
}
if (ast_strlen_zero(keytree))
keytree = 0;
} else {
family = argv;
keytree = 0;
}
if (keytree)
ast_verb(3, "DBdeltree: family=%s, keytree=%s\n", family, keytree);
else
ast_verb(3, "DBdeltree: family=%s\n", family);
if (ast_db_deltree(family, keytree))
ast_verb(3, "DBdeltree: Error deleting key from database.\n");
return 0;
}
static int del_exec(struct ast_channel *chan, void *data)
{
char *argv, *family, *key;
static int deprecation_warning = 0;
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");
}
argv = ast_strdupa(data);
if (strchr(argv, '/')) {
family = strsep(&argv, "/");
key = strsep(&argv, "\0");
if (!family || !key) {
ast_debug(1, "Ignoring; Syntax error in argument\n");
return 0;
}
ast_verb(3, "DBdel: family=%s, key=%s\n", family, key);
if (ast_db_del(family, key))
ast_verb(3, "DBdel: Error deleting key from database.\n");
} else {
ast_debug(1, "Ignoring, no parameters\n");
}
return 0;
}
static int unload_module(void)
{
int retval;
retval = ast_unregister_application(dt_app);
retval |= ast_unregister_application(d_app);
return retval;
}
static 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);
return retval;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Database Access Functions");

2047
trunk/apps/app_dial.c Normal file

File diff suppressed because it is too large Load Diff

338
trunk/apps/app_dictate.c Normal file
View File

@@ -0,0 +1,338 @@
/*
* 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 <sys/stat.h>
#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
#include "asterisk/file.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/say.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;
int ffactor = 320 * 80,
res = 0,
done = 0,
oldr = 0,
lastop = 0,
samples = 0,
speed = 1,
digit = 0,
len = 0,
maxlen = 0,
mode = 0;
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");
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 = "";
}
ast_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, AST_FILE_MODE);
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, 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, AST_FILE_MODE);
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);
}
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");

View File

@@ -0,0 +1,172 @@
/*
* 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 "asterisk/file.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"
#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\n"
"pickup any ringing channel that is calling the specified extension. If no\n"
"context is specified, the current context will be used. If you use the special\n"
"string \"PICKUPMARK\" for the context parameter, for example 10@PICKUPMARK,\n"
"this application tries to find a channel which has defined a ${PICKUPMARK}\n"
"channel variable with the same value as \"extension\" (in this example, \"10\").\n"
"When no parameter is specified, the application will pickup a channel matching\n"
"the pickup group of the active channel.";
/* Perform actual pickup between two channels */
static int pickup_do(struct ast_channel *chan, struct ast_channel *target)
{
int res = 0;
ast_debug(1, "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))
return 1;
else
return 0;
}
/* Attempt to pick up specified extension with context */
static int pickup_by_exten(struct ast_channel *chan, const char *exten, const 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) &&
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, const 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;
char *tmp = ast_strdupa(data);
char *exten = NULL, *context = NULL;
if (ast_strlen_zero(data)) {
res = ast_pickup_call(chan);
return res;
}
/* Parse extension (and context if there) */
while (!ast_strlen_zero(tmp) && (exten = strsep(&tmp, "&"))) {
if ((context = strchr(exten, '@')))
*context++ = '\0';
if (!ast_strlen_zero(context) && !strcasecmp(context, PICKUPMARK)) {
if (!pickup_by_mark(chan, exten))
break;
} else {
if (!pickup_by_exten(chan, exten, !ast_strlen_zero(context) ? context : chan->context))
break;
}
ast_log(LOG_NOTICE, "No target channel found for %s.\n", exten);
}
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");

842
trunk/apps/app_directory.c Normal file
View File

@@ -0,0 +1,842 @@
/*
* 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 Provide a directory of extensions
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <ctype.h>
#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
#include "asterisk/file.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/say.h"
#include "asterisk/app.h"
#ifdef ODBC_STORAGE
#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 *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"
" m - Instead of reading each name sequentially and asking for confirmation,\n"
" create a menu of up to 8 names.\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"
/* How many digits to read in */
#define NUMDIGITS 3
enum {
OPT_LISTBYFIRSTNAME = (1 << 0),
OPT_SAYEXTENSION = (1 << 1),
OPT_FROMVOICEMAIL = (1 << 2),
OPT_SELECTFROMMENU = (1 << 3),
} directory_option_flags;
struct directory_item {
char exten[AST_MAX_EXTENSION + 1];
char name[AST_MAX_EXTENSION + 1];
char key[50]; /* Text to order items. Either lastname+firstname or firstname+lastname */
AST_LIST_ENTRY(directory_item) entry;
};
AST_APP_OPTIONS(directory_app_options, {
AST_APP_OPTION('f', OPT_LISTBYFIRSTNAME),
AST_APP_OPTION('e', OPT_SAYEXTENSION),
AST_APP_OPTION('v', OPT_FROMVOICEMAIL),
AST_APP_OPTION('m', OPT_SELECTFROMMENU),
});
#ifdef ODBC_STORAGE
struct generic_prepare_struct {
const char *sql;
const char *param;
};
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, AST_FILE_MODE);
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 int compare(const char *text, const char *template)
{
char digit;
while (*template) {
digit = toupper(*text++);
switch (digit) {
case 0:
return -1;
case '1':
digit = '1';
break;
case '2':
case 'A':
case 'B':
case 'C':
digit = '2';
break;
case '3':
case 'D':
case 'E':
case 'F':
digit = '3';
break;
case '4':
case 'G':
case 'H':
case 'I':
digit = '4';
break;
case '5':
case 'J':
case 'K':
case 'L':
digit = '5';
break;
case '6':
case 'M':
case 'N':
case 'O':
digit = '6';
break;
case '7':
case 'P':
case 'Q':
case 'R':
case 'S':
digit = '7';
break;
case '8':
case 'T':
case 'U':
case 'V':
digit = '8';
break;
case '9':
case 'W':
case 'X':
case 'Y':
case 'Z':
digit = '9';
break;
default:
if (digit > ' ')
return -1;
continue;
}
if (*template++ != digit)
return -1;
}
return 0;
}
/* play name of mailbox owner.
* returns: -1 for bad or missing extension
* '1' for selected entry from directory
* '*' for skipped entry from directory
*/
static int play_mailbox_owner(struct ast_channel *chan, const char *context,
const char *ext, const char *name, struct ast_flags *flags)
{
int res = 0;
char fn[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
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
if (ast_fileexists(fn, NULL, chan->language) > 0) {
res = ast_stream_and_wait(chan, fn, AST_DIGIT_ANY);
ast_stopstream(chan);
/* If Option 'e' was specified, also read the extension number with the name */
if (ast_test_flag(flags, OPT_SAYEXTENSION)) {
ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
}
} else {
res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, chan->language);
if (!ast_strlen_zero(name) && ast_test_flag(flags, OPT_SAYEXTENSION)) {
ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
}
}
#ifdef ODBC_STORAGE
ast_filedelete(fn, NULL);
#endif
return res;
}
static int select_entry(struct ast_channel *chan, const char *context, const char *dialcontext, const struct directory_item *item, struct ast_flags *flags)
{
ast_debug(1, "Selecting '%s' - %s@%s\n", item->name, item->exten, dialcontext);
if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
/* We still want to set the exten though */
ast_copy_string(chan->exten, item->exten, sizeof(chan->exten));
} else if (ast_goto_if_exists(chan, dialcontext, item->exten, 1)) {
ast_log(LOG_WARNING,
"Can't find extension '%s' in context '%s'. "
"Did you pass the wrong context to Directory?\n",
item->exten, dialcontext);
return -1;
}
return 0;
}
static int select_item_seq(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags)
{
struct directory_item *item, **ptr;
int i, res, loop;
for (ptr = items, i = 0; i < count; i++, ptr++) {
item = *ptr;
for (loop = 3 ; loop > 0; loop--) {
res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
if (!res)
res = ast_stream_and_wait(chan, "dir-instr", AST_DIGIT_ANY);
if (!res)
res = ast_waitfordigit(chan, 3000);
ast_stopstream(chan);
if (res == '1') { /* Name selected */
return select_entry(chan, context, dialcontext, item, flags) ? -1 : 1;
} else if (res == '*') {
/* Skip to next match in list */
break;
}
if (res < 0)
return -1;
res = 0;
}
}
/* Nothing was selected */
return 0;
}
static int select_item_menu(struct ast_channel *chan, struct directory_item **items, int count, const char *context, const char *dialcontext, struct ast_flags *flags)
{
struct directory_item **block, *item;
int i, limit, res = 0;
char buf[9];
for (block = items; count; block += limit, count -= limit) {
limit = count;
if (limit > 8)
limit = 8;
for (i = 0; i < limit && !res; i++) {
item = block[i];
snprintf(buf, sizeof(buf), "digits/%d", i + 1);
/* Press <num> for <name>, [ extension <ext> ] */
res = ast_streamfile(chan, "dir-multi1", chan->language);
if (!res)
res = ast_waitstream(chan, AST_DIGIT_ANY);
if (!res)
res = ast_streamfile(chan, buf, chan->language);
if (!res)
res = ast_waitstream(chan, AST_DIGIT_ANY);
if (!res)
res = ast_streamfile(chan, "dir-multi2", chan->language);
if (!res)
res = ast_waitstream(chan, AST_DIGIT_ANY);
if (!res)
res = play_mailbox_owner(chan, context, item->exten, item->name, flags);
if (!res)
res = ast_waitstream(chan, AST_DIGIT_ANY);
if (!res)
res = ast_waitfordigit(chan, 800);
}
/* Press "9" for more names. */
if (!res && count > limit) {
res = ast_streamfile(chan, "dir-multi9", chan->language);
if (!res)
res = ast_waitstream(chan, AST_DIGIT_ANY);
}
if (!res) {
res = ast_waitfordigit(chan, 3000);
}
if (res && res > '0' && res < '1' + limit) {
return select_entry(chan, context, dialcontext, block[res - '1'], flags) ? -1 : 1;
}
if (res < 0)
return -1;
res = 0;
}
/* Nothing was selected */
return 0;
}
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];
struct ast_flags config_flags = { 0 };
/* Load flat file config. */
cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
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, "", 99999);
if (!cat) {
ast_log(LOG_WARNING, "Out of memory\n");
ast_config_destroy(cfg);
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 check_match(struct directory_item **result, const char *item_fullname, const char *item_ext, const char *pattern_ext, int use_first_name)
{
struct directory_item *item;
const char *key = NULL;
int namelen;
/* Set key to last name or first name depending on search mode */
if (!use_first_name)
key = strchr(item_fullname, ' ');
if (key)
key++;
else
key = item_fullname;
if (compare(key, pattern_ext))
return 0;
/* Match */
item = ast_calloc(1, sizeof(*item));
if (!item)
return -1;
ast_copy_string(item->name, item_fullname, sizeof(item->name));
ast_copy_string(item->exten, item_ext, sizeof(item->exten));
ast_copy_string(item->key, key, sizeof(item->key));
if (key != item_fullname) {
/* Key is the last name. Append first name to key in order to sort Last,First */
namelen = key - item_fullname - 1;
if (namelen > sizeof(item->key) - strlen(item->key) - 1)
namelen = sizeof(item->key) - strlen(item->key) - 1;
strncat(item->key, item_fullname, namelen);
}
*result = item;
return 1;
}
typedef AST_LIST_HEAD_NOLOCK(, directory_item) itemlist;
static int search_directory(const char *context, struct ast_config *vmcfg, struct ast_config *ucfg, const char *ext, int use_first_name, itemlist *alist)
{
struct ast_variable *v;
char buf[AST_MAX_EXTENSION + 1], *pos, *bufptr, *cat;
struct directory_item *item;
int res;
ast_debug(2, "Pattern: %s\n", ext);
for (v = ast_variable_browse(vmcfg, context); v; v = v->next) {
/* Ignore hidden */
if (strcasestr(v->value, "hidefromdir=yes"))
continue;
ast_copy_string(buf, v->value, sizeof(buf));
bufptr = buf;
/* password,Full Name,email,pager,options */
strsep(&bufptr, ",");
pos = strsep(&bufptr, ",");
res = check_match(&item, pos, v->name, ext, use_first_name);
if (!res)
continue;
else if (res < 0)
return -1;
AST_LIST_INSERT_TAIL(alist, item, entry);
}
if (ucfg) {
for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
const char *pos;
if (!strcasecmp(cat, "general"))
continue;
if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory")))
continue;
/* Find all candidate extensions */
pos = ast_variable_retrieve(ucfg, cat, "fullname");
if (!pos)
continue;
res = check_match(&item, pos, cat, ext, use_first_name);
if (!res)
continue;
else if (res < 0)
return -1;
AST_LIST_INSERT_TAIL(alist, item, entry);
}
}
return 0;
}
static void sort_items(struct directory_item **sorted, int count)
{
int reordered, i;
struct directory_item **ptr, *tmp;
if (count < 2)
return;
/* Bubble-sort items by the key */
do {
reordered = 0;
for (ptr = sorted, i = 0; i < count - 1; i++, ptr++) {
if (strcasecmp(ptr[0]->key, ptr[1]->key) > 0) {
tmp = ptr[0];
ptr[0] = ptr[1];
ptr[1] = tmp;
reordered++;
}
}
} while (reordered);
}
static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
{
if (!ast_goto_if_exists(chan, dialcontext, ext, 1) ||
(!ast_strlen_zero(chan->macrocontext) &&
!ast_goto_if_exists(chan, chan->macrocontext, ext, 1))) {
return 0;
} else {
ast_log(LOG_WARNING, "Can't find extension '%s' in current context. "
"Not Exiting the Directory!\n", ext);
return -1;
}
}
static int do_directory(struct ast_channel *chan, struct ast_config *vmcfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, struct ast_flags *flags)
{
/* Read in the first three digits.. "digit" is the first digit, already read */
int res = 0;
itemlist alist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
struct directory_item *item, **ptr, **sorted = NULL;
int count, i;
char ext[NUMDIGITS + 1] = "";
if (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' && !goto_exten(chan, dialcontext, "o")) {
return 0;
}
if (digit == '*' && !goto_exten(chan, dialcontext, "a")) {
return 0;
}
ext[0] = digit;
if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0)
return -1;
res = search_directory(context, vmcfg, ucfg, ext, ast_test_flag(flags, OPT_LISTBYFIRSTNAME), &alist);
if (res)
goto exit;
/* Count items in the list */
count = 0;
AST_LIST_TRAVERSE(&alist, item, entry) {
count++;
}
if (count < 1) {
res = ast_streamfile(chan, "dir-nomatch", chan->language);
goto exit;
}
/* Create plain array of pointers to items (for sorting) */
sorted = ast_calloc(count, sizeof(*sorted));
ptr = sorted;
AST_LIST_TRAVERSE(&alist, item, entry) {
*ptr++ = item;
}
/* Sort items */
sort_items(sorted, count);
if (option_debug) {
ast_debug(2, "Listing matching entries:\n");
for (ptr = sorted, i = 0; i < count; i++, ptr++) {
ast_log(LOG_DEBUG, "%s: %s\n", ptr[0]->exten, ptr[0]->name);
}
}
if (ast_test_flag(flags, OPT_SELECTFROMMENU)) {
/* Offer multiple entries at the same time */
res = select_item_menu(chan, sorted, count, context, dialcontext, flags);
} else {
/* Offer entries one by one */
res = select_item_seq(chan, sorted, count, context, dialcontext, flags);
}
if (!res) {
res = ast_streamfile(chan, "dir-nomore", chan->language);
}
exit:
if (sorted)
ast_free(sorted);
while ((item = AST_LIST_REMOVE_HEAD(&alist, entry)))
ast_free(item);
return res;
}
static int directory_exec(struct ast_channel *chan, void *data)
{
int res = 0;
struct ast_config *cfg, *ucfg;
const char *dirintro;
char *parse, *opts[0];
struct ast_flags flags = { 0 };
struct ast_flags config_flags = { 0 };
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(vmcontext);
AST_APP_ARG(dialcontext);
AST_APP_ARG(options);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Directory requires an argument (context[,dialcontext])\n");
return -1;
}
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
return -1;
if (ast_strlen_zero(args.dialcontext))
args.dialcontext = args.vmcontext;
cfg = realtime_directory(args.vmcontext);
if (!cfg) {
ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
return -1;
}
ucfg = ast_config_load("users.conf", config_flags);
dirintro = ast_variable_retrieve(cfg, args.vmcontext, "directoryintro");
if (ast_strlen_zero(dirintro))
dirintro = ast_variable_retrieve(cfg, "general", "directoryintro");
if (ast_strlen_zero(dirintro))
dirintro = ast_test_flag(&flags, OPT_LISTBYFIRSTNAME) ? "dir-intro-fn" : "dir-intro";
if (chan->_state != AST_STATE_UP)
res = ast_answer(chan);
for (;;) {
if (!res)
res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
ast_stopstream(chan);
if (!res)
res = ast_waitfordigit(chan, 5000);
if (res <= 0)
break;
res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, &flags);
if (res)
break;
res = ast_waitstream(chan, AST_DIGIT_ANY);
ast_stopstream(chan);
if (res)
break;
}
if (ucfg)
ast_config_destroy(ucfg);
ast_config_destroy(cfg);
return res < 0 ? -1 : 0;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(app);
return res;
}
static int load_module(void)
{
#ifdef ODBC_STORAGE
struct ast_flags config_flags = { 0 };
struct ast_config *cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
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");

367
trunk/apps/app_disa.c Normal file
View File

@@ -0,0 +1,367 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
*
* 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
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <math.h>
#include <sys/time.h>
#include "asterisk/lock.h"
#include "asterisk/file.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 *app = "DISA";
static char *synopsis = "DISA (Direct Inward System Access)";
static char *descrip =
"DISA(<numeric passcode>[,<context>[,<cid>[,mailbox[,options]]]]) or\n"
"DISA(<filename>[,,,,options])\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"
"system dialtone within <context> on which a call may be placed. If the user\n"
"enters an invalid extension and extension \"i\" exists in the specified\n"
"context, it will be used.\n"
"\n"
"If you need to present a DISA dialtone without entering a password, simply\n"
"set <passcode> to \"no-password\".\n"
"\n"
"Be aware that using this may compromise the security of your PBX.\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.\n"
"\n"
"The file that contains the passcodes (if used) allows a complete\n"
"specification of all of the same arguments available on the command\n"
"line, with the sole exception of the options. The file may contain blank\n"
"lines, or comments starting with \"#\" or \";\".\n"
"\n"
"<context> specifies the dialplan context in which the user-entered extension\n"
"will be matched. If no context is specified, the DISA application defaults\n"
"the context to \"disa\". Presumably a normal system will have a special\n"
"context set up for DISA use with some or a lot of restrictions.\n"
"\n"
"<cid> specifies a new (different) callerid to be used for this call.\n"
"\n"
"<mailbox[@context]> will cause a stutter-dialtone (indication \"dialrecall\")\n"
"to be used, if the specified mailbox contains any new messages.\n"
"\n"
"The following options are available:\n"
" n - the DISA application will not answer initially.\n"
" p - the extension entered will be considered complete when a '#' is entered.\n";
enum {
NOANSWER_FLAG = (1 << 0),
POUND_TO_END_FLAG = (1 << 1),
} option_flags;
AST_APP_OPTIONS(app_opts, {
AST_APP_OPTION('n', NOANSWER_FLAG),
AST_APP_OPTION('p', POUND_TO_END_FLAG),
});
static void play_dialtone(struct ast_channel *chan, char *mailbox)
{
const struct ind_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");
if (ts)
ast_playtones_start(chan, 0, ts->data, 0);
else
ast_tonepair_start(chan, 350, 440, 0, 0);
}
static int disa_exec(struct ast_channel *chan, void *data)
{
int i = 0, j, k = 0, did_ignore = 0, special_noanswer = 0;
int firstdigittimeout = (chan->pbx ? chan->pbx->rtimeout * 1000 : 20000);
int digittimeout = (chan->pbx ? chan->pbx->dtimeout * 1000 : 10000);
struct ast_flags flags;
char *tmp, exten[AST_MAX_EXTENSION] = "", acctcode[20]="";
char pwline[256];
char ourcidname[256],ourcidnum[256];
struct ast_frame *f;
struct timeval lastdigittime;
int res;
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(options);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n");
return -1;
}
ast_debug(1, "Digittimeout: %d\n", digittimeout);
ast_debug(1, "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 = "";
if (!ast_strlen_zero(args.options))
ast_app_parse_options(app_opts, &flags, NULL, args.options);
ast_debug(1, "Mailbox: %s\n",args.mailbox);
if (!ast_test_flag(&flags, NOANSWER_FLAG)) {
if (chan->_state != AST_STATE_UP) {
/* answer */
ast_answer(chan);
}
} else
special_noanswer = 1;
ast_debug(1, "Context: %s\n",args.context);
if (!strcasecmp(args.passcode, "no-password")) {
k |= 1; /* We have the password */
ast_debug(1, "DISA no-password login success\n");
}
lastdigittime = ast_tvnow();
play_dialtone(chan, args.mailbox);
ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
for (;;) {
/* if outa time, give em reorder */
if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > ((k&2) ? digittimeout : firstdigittimeout)) {
ast_debug(1,"DISA %s entry timeout on chan %s\n",
((k&1) ? "extension" : "password"),chan->name);
break;
}
if ((res = ast_waitfor(chan, -1) < 0)) {
ast_debug(1, "Waitfor returned %d\n", res);
continue;
}
if (!(f = ast_read(chan))) {
ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
return -1;
}
if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
ast_frfree(f);
ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
return -1;
}
/* If the frame coming in is not DTMF, just drop it and continue */
if (f->frametype != AST_FRAME_DTMF) {
ast_frfree(f);
continue;
}
j = f->subclass; /* save digit */
ast_frfree(f);
if (!i) {
k |= 2; /* We have the first digit */
ast_playtones_stop(chan);
}
lastdigittime = ast_tvnow();
/* 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 */
/* see if this is an integer */
if (sscanf(args.passcode,"%d",&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_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
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_debug(1, "Mailbox: %s\n",args.mailbox);
/* password must be in valid format (numeric) */
if (sscanf(args.passcode,"%d", &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;
}
}
fclose(fp);
}
/* compare the two */
if (strcmp(exten,args.passcode)) {
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_debug(1,"DISA on chan %s password is good\n",chan->name);
play_dialtone(chan, args.mailbox);
k|=1; /* In number mode */
i = 0; /* re-set buffer pointer */
exten[sizeof(acctcode)] = 0;
ast_copy_string(acctcode, exten, sizeof(acctcode));
exten[0] = 0;
ast_debug(1,"Successful DISA log-in on chan %s\n", chan->name);
continue;
}
} else {
if (j == '#') { /* end of extension */
break;
}
}
exten[i++] = j; /* save digit */
exten[i] = 0;
if (!(k&1))
continue; /* if getting password, continue doing it */
/* if this exists */
/* user wants end of number, remove # */
if (ast_test_flag(&flags, POUND_TO_END_FLAG) && j == '#') {
exten[--i] = 0;
break;
}
if (ast_ignore_pattern(args.context, exten)) {
play_dialtone(chan, "");
did_ignore = 1;
} else
if (did_ignore) {
ast_playtones_stop(chan);
did_ignore = 0;
}
/* if can do some more, do it */
if (!ast_matchmore_extension(chan,args.context,exten,1, chan->cid.cid_num)) {
break;
}
}
}
ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
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);
return 0;
}
}
/* Received invalid, but no "i" extension exists in the given context */
reorder:
/* Play congestion for a bit */
ast_indicate(chan, AST_CONTROL_CONGESTION);
ast_safe_sleep(chan, 10*1000);
ast_playtones_stop(chan);
return -1;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static 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");

160
trunk/apps/app_dumpchan.c Normal file
View File

@@ -0,0 +1,160 @@
/*
* 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 "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/channel.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"
"CallerIDNum= %s\n"
"CallerIDName= %s\n"
"DNIDDigits= %s\n"
"RDNIS= %s\n"
"Language= %s\n"
"State= %s (%d)\n"
"Rings= %d\n"
"NativeFormat= %s\n"
"WriteFormat= %s\n"
"ReadFormat= %s\n"
"RawWriteFormat= %s\n"
"RawReadFormat= %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)"),
c->language,
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),
ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->rawwriteformat),
ast_getformatname_multiple(formatbuf, sizeof(formatbuf), c->rawreadformat),
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_str *vars = ast_str_alloca(BUFSIZ * 4); /* XXX very large! */
char info[1024];
int level = 0;
static char *line = "================================================================================";
if (!ast_strlen_zero(data))
level = atoi(data);
pbx_builtin_serialize_variables(chan, &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->str, line);
return 0;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
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");

86
trunk/apps/app_echo.c Normal file
View File

@@ -0,0 +1,86 @@
/*
* 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 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/file.h"
#include "asterisk/module.h"
#include "asterisk/channel.h"
static char *app = "Echo";
static char *synopsis = "Echo audio, video, or DTMF back to the calling party";
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";
static int echo_exec(struct ast_channel *chan, void *data)
{
int res = -1;
int format;
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)
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;
}
ast_frfree(f);
}
end:
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, echo_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Echo Application");

221
trunk/apps/app_exec.c Normal file
View File

@@ -0,0 +1,221 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (c) 2004 - 2005, Tilghman Lesher. All rights reserved.
* Portions copyright (c) 2006, Philipp Dunkel.
*
* Tilghman Lesher <app_exec__v002@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 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 "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.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 *app_exec = "Exec";
static char *exec_synopsis = "Executes dialplan application";
static char *exec_descrip =
" 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";
static char *app_tryexec = "TryExec";
static char *tryexec_synopsis = "Executes dialplan application, always returning";
static char *tryexec_descrip =
" 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 one of:\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";
static char *app_execif = "ExecIf";
static char *execif_synopsis = "Executes dialplan application, conditionally";
static char *execif_descrip =
" ExecIF (<expr>?<appiftrue>(<args>)[:<appiffalse>(<args>)])\n"
"If <expr> is true, execute and return the result of <appiftrue>(<args>).\n"
"If <expr> is true, but <appiftrue> is not found, then the application\n"
"will return a non-zero value.\n";
static int exec_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char *s, *appname, *endargs, args[MAXRESULT];
struct ast_app *app;
if (ast_strlen_zero(data))
return 0;
s = ast_strdupa(data);
args[0] = 0;
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);
} else {
ast_log(LOG_WARNING, "Could not find application (%s)\n", appname);
res = -1;
}
}
return res;
}
static int tryexec_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char *s, *appname, *endargs, args[MAXRESULT];
struct ast_app *app;
if (ast_strlen_zero(data))
return 0;
s = ast_strdupa(data);
args[0] = 0;
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");
}
}
return 0;
}
static int execif_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char *truedata = NULL, *falsedata = NULL, *end;
struct ast_app *app = NULL;
AST_DECLARE_APP_ARGS(expr,
AST_APP_ARG(expr);
AST_APP_ARG(remainder);
);
AST_DECLARE_APP_ARGS(apps,
AST_APP_ARG(t);
AST_APP_ARG(f);
);
char *parse = ast_strdupa(data);
AST_NONSTANDARD_APP_ARGS(expr, parse, '?');
if (ast_strlen_zero(expr.remainder)) {
ast_log(LOG_ERROR, "Usage: ExecIf(<expr>?<appiftrue>(<args>)[:<appiffalse>(<args)])\n");
return -1;
}
AST_NONSTANDARD_APP_ARGS(apps, expr.remainder, ':');
if (apps.t && (truedata = strchr(apps.t, '('))) {
*truedata++ = '\0';
if ((end = strrchr(truedata, ')')))
*end = '\0';
}
if (apps.f && (falsedata = strchr(apps.f, '('))) {
*falsedata++ = '\0';
if ((end = strrchr(falsedata, ')')))
*end = '\0';
}
if (pbx_checkcondition(expr.expr)) {
if (!ast_strlen_zero(apps.t) && (app = pbx_findapp(apps.t))) {
res = pbx_exec(chan, app, S_OR(truedata, ""));
} else {
ast_log(LOG_WARNING, "Could not find application! (%s)\n", apps.t);
res = -1;
}
} else if (!ast_strlen_zero(apps.f)) {
if ((app = pbx_findapp(apps.f))) {
res = pbx_exec(chan, app, S_OR(falsedata, ""));
} else {
ast_log(LOG_WARNING, "Could not find application! (%s)\n", apps.f);
res = -1;
}
}
return res;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(app_exec);
res |= ast_unregister_application(app_tryexec);
res |= ast_unregister_application(app_execif);
return res;
}
static int load_module(void)
{
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;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Executes dialplan applications");

View File

@@ -0,0 +1,575 @@
/*
* 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
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.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"
static const char *app = "ExternalIVR";
static const char *synopsis = "Interfaces with an external IVR application";
static const char *descrip =
" ExternalIVR(command[,arg[,arg...]]): Forks an 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);
if (option_debug)
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);
ast_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 playlist_entry *entry;
int child_stdin[2] = { 0,0 };
int child_stdout[2] = { 0,0 };
int child_stderr[2] = { 0,0 };
int res = -1;
int gen_active = 0;
int pid;
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;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(cmd)[32];
);
sigfillset(&fullset);
pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
u->abort_current_sound = 0;
u->chan = chan;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
return -1;
}
buf = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, buf);
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;
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(args.cmd[0], args.cmd);
fprintf(stderr, "Failed to execute '%s': %s\n", args.cmd[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;
}
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);
ast_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);
ast_free(entry);
}
if (!u->playing_silence)
u->abort_current_sound = 1;
AST_LIST_UNLOCK(&u->playlist);
}
} else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == 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);
if (option_debug)
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);
ast_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] == 'E') {
ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
send_child_event(child_events, 'E', NULL, chan);
res = 0;
break;
} 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);
res = -1;
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 (input[0] == 'V') {
char *c;
c = strchr(&input[2], '=');
if (!c) {
send_child_event(child_events, 'Z', NULL, chan);
} else {
*c++ = '\0';
pbx_builtin_setvar_helper(chan, &input[2], c);
}
}
} else if (ready_fd == child_errors_fd) {
char input[1024];
if (exception || 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 (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)))
ast_free(entry);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
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");

523
trunk/apps/app_festival.c Normal file
View File

@@ -0,0 +1,523 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* 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.
*/
/*! \file
*
* \brief Connect to festival
*
* \author Christos Ricudis <ricudis@itc.auth.gr>
*
* \extref The Festival Speech Synthesis System - http://www.cstr.ed.ac.uk/projects/festival/
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include "asterisk/file.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/app.h"
#define FESTIVAL_CONFIG "festival.conf"
#define MAXLEN 180
#define MAXFESTLEN 2048
static char *app = "Festival";
static char *synopsis = "Say text to the user";
static char *descrip =
" Festival(text[,intkeys]): Connect to Festival, send the argument, get back the waveform,\n"
"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";
static char *socket_receive_file_to_buff(int fd, int *size)
{
/* Receive file (probably a waveform file) from socket using
* Festival key stuff technique, but long winded I know, sorry
* but will receive any file without closing the stream or
* using OOB data
*/
static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's key */
char *buff, *tmp;
int bufflen;
int n,k,i;
char c;
bufflen = 1024;
if (!(buff = ast_malloc(bufflen)))
return NULL;
*size = 0;
for (k = 0; file_stuff_key[k] != '\0';) {
n = read(fd, &c, 1);
if (n == 0)
break; /* hit stream eof before end of file */
if ((*size) + k + 1 >= bufflen) {
/* +1 so you can add a terminating NULL if you want */
bufflen += bufflen / 4;
if (!(tmp = ast_realloc(buff, bufflen))) {
ast_free(buff);
return NULL;
}
buff = tmp;
}
if (file_stuff_key[k] == c)
k++;
else if ((c == 'X') && (file_stuff_key[k+1] == '\0')) {
/* It looked like the key but wasn't */
for (i = 0; i < k; i++, (*size)++)
buff[*size] = file_stuff_key[i];
k = 0;
/* omit the stuffed 'X' */
} else {
for (i = 0; i < k; i++, (*size)++)
buff[*size] = file_stuff_key[i];
k = 0;
buff[*size] = c;
(*size)++;
}
}
return buff;
}
static int send_waveform_to_fd(char *waveform, int length, int fd)
{
int res;
int x;
#ifdef __PPC__
char c;
#endif
sigset_t fullset, oldset;
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;
}
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);
#ifdef __PPC__
for (x = 0; x < length; x += 2) {
c = *(waveform + x + 1);
*(waveform + x + 1) = *(waveform + x);
*(waveform + x) = c;
}
#endif
write(fd, waveform, length);
close(fd);
exit(0);
}
static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, int length, char *intkeys)
{
int res = 0;
int fds[2];
int pid = -1;
int needed = 0;
int owriteformat;
struct ast_frame *f;
struct myframe {
struct ast_frame f;
char offset[AST_FRIENDLY_OFFSET];
char frdata[2048];
} myf = {
.f = { 0, },
};
if (pipe(fds)) {
ast_log(LOG_WARNING, "Unable to create pipe\n");
return -1;
}
/* Answer if it's not already going */
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) {
ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
return -1;
}
res = send_waveform_to_fd(waveform, length, fds[1]);
if (res >= 0) {
pid = res;
/* Order is important -- there's almost always going to be mp3... we want to prioritize the
user */
for (;;) {
res = ast_waitfor(chan, 1000);
if (res < 1) {
res = -1;
break;
}
f = ast_read(chan);
if (!f) {
ast_log(LOG_WARNING, "Null frame == hangup() detected\n");
res = -1;
break;
}
if (f->frametype == AST_FRAME_DTMF) {
ast_debug(1, "User pressed a key\n");
if (intkeys && strchr(intkeys, f->subclass)) {
res = f->subclass;
ast_frfree(f);
break;
}
}
if (f->frametype == AST_FRAME_VOICE) {
/* Treat as a generator */
needed = f->samples * 2;
if (needed > sizeof(myf.frdata)) {
ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n",
(int)sizeof(myf.frdata) / 2, needed/2);
needed = sizeof(myf.frdata);
}
res = read(fds[0], myf.frdata, needed);
if (res > 0) {
myf.f.frametype = AST_FRAME_VOICE;
myf.f.subclass = AST_FORMAT_SLINEAR;
myf.f.datalen = res;
myf.f.samples = res / 2;
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 */
ast_debug(1, "Last frame\n");
res = 0;
ast_frfree(f);
break;
}
} else {
ast_debug(1, "No more waveform\n");
res = 0;
}
}
ast_frfree(f);
}
}
close(fds[0]);
close(fds[1]);
#if 0
if (pid > -1)
kill(pid, SIGKILL);
#endif
if (!res && owriteformat)
ast_set_write_format(chan, owriteformat);
return res;
}
static int festival_exec(struct ast_channel *chan, void *vdata)
{
int usecache;
int res = 0;
struct sockaddr_in serv_addr;
struct hostent *serverhost;
struct ast_hostent ahp;
int fd;
FILE *fs;
const char *host;
const char *cachedir;
const char *temp;
const char *festivalcommand;
int port = 1314;
int n;
char ack[4];
char *waveform;
int filesize;
int wave;
char bigstring[MAXFESTLEN];
int i;
struct MD5Context md5ctx;
unsigned char MD5Res[16];
char MD5Hex[33] = "";
char koko[4] = "";
char cachefile[MAXFESTLEN]="";
int readcache = 0;
int writecache = 0;
int strln;
int fdesc = -1;
char buffer[16384];
int seekpos = 0;
char *data;
struct ast_config *cfg;
char *newfestivalcommand;
struct ast_flags config_flags = { 0 };
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(text);
AST_APP_ARG(interrupt);
);
if (ast_strlen_zero(vdata)) {
ast_log(LOG_WARNING, "festival requires an argument (text)\n");
return -1;
}
cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
if (!cfg) {
ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG);
return -1;
}
if (!(host = ast_variable_retrieve(cfg, "general", "host"))) {
host = "localhost";
}
if (!(temp = ast_variable_retrieve(cfg, "general", "port"))) {
port = 1314;
} else {
port = atoi(temp);
}
if (!(temp = ast_variable_retrieve(cfg, "general", "usecache"))) {
usecache = 0;
} else {
usecache = ast_true(temp);
}
if (!(cachedir = ast_variable_retrieve(cfg, "general", "cachedir"))) {
cachedir = "/tmp/";
}
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);
AST_STANDARD_APP_ARGS(args, data);
if (args.interrupt && !strcasecmp(args.interrupt, "any"))
args.interrupt = AST_DIGIT_ANY;
ast_debug(1, "Text passed to festival server : %s\n", args.text);
/* Connect to local festival server */
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
ast_log(LOG_WARNING, "festival_client: can't get socket\n");
ast_config_destroy(cfg);
return -1;
}
memset(&serv_addr, 0, sizeof(serv_addr));
if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) {
/* its a name rather than an ipnum */
serverhost = ast_gethostbyname(host, &ahp);
if (serverhost == NULL) {
ast_log(LOG_WARNING, "festival_client: gethostbyname failed\n");
ast_config_destroy(cfg);
return -1;
}
memmove(&serv_addr.sin_addr, serverhost->h_addr, serverhost->h_length);
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
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);
return -1;
}
/* Compute MD5 sum of string */
MD5Init(&md5ctx);
MD5Update(&md5ctx, (unsigned char *)args.text, strlen(args.text));
MD5Final(MD5Res, &md5ctx);
MD5Hex[0] = '\0';
/* Convert to HEX and look if there is any matching file in the cache
directory */
for (i = 0; i < 16; i++) {
snprintf(koko, sizeof(koko), "%X", MD5Res[i]);
strncat(MD5Hex, koko, sizeof(MD5Hex) - strlen(MD5Hex) - 1);
}
readcache = 0;
writecache = 0;
if (strlen(cachedir) + strlen(MD5Hex) + 1 <= MAXFESTLEN && (usecache == -1)) {
snprintf(cachefile, sizeof(cachefile), "%s/%s", cachedir, MD5Hex);
fdesc = open(cachefile, O_RDWR);
if (fdesc == -1) {
fdesc = open(cachefile, O_CREAT | O_RDWR, AST_FILE_MODE);
if (fdesc != -1) {
writecache = 1;
strln = strlen(args.text);
ast_debug(1, "line length : %d\n", strln);
write(fdesc, &strln, sizeof(strln));
write(fdesc, args.text, strln);
seekpos = lseek(fdesc, 0, SEEK_CUR);
ast_debug(1, "Seek position : %d\n", seekpos);
}
} else {
read(fdesc, &strln, sizeof(strln));
ast_debug(1, "Cache file exists, strln=%d, strlen=%d\n", strln, (int)strlen(args.text));
if (strlen(args.text) == strln) {
ast_debug(1, "Size OK\n");
read(fdesc, &bigstring, strln);
bigstring[strln] = 0;
if (strcmp(bigstring, args.text) == 0) {
readcache = 1;
} else {
ast_log(LOG_WARNING, "Strings do not match\n");
}
} else {
ast_log(LOG_WARNING, "Size mismatch\n");
}
}
}
if (readcache == 1) {
close(fd);
fd = fdesc;
ast_debug(1, "Reading from cache...\n");
} else {
ast_debug(1, "Passing text to festival...\n");
fs = fdopen(dup(fd), "wb");
fprintf(fs, festivalcommand, args.text);
fflush(fs);
fclose(fs);
}
/* Write to cache and then pass it down */
if (writecache == 1) {
ast_debug(1, "Writing result to cache...\n");
while ((strln = read(fd, buffer, 16384)) != 0) {
write(fdesc, buffer, strln);
}
close(fd);
close(fdesc);
fd = open(cachefile, O_RDWR);
lseek(fd, seekpos, SEEK_SET);
}
ast_debug(1, "Passing data to channel...\n");
/* Read back info from server */
/* 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);
return -1;
}
n += read_data;
}
ack[3] = '\0';
if (strcmp(ack, "WV\n") == 0) { /* receive a waveform */
ast_debug(1, "Festival WV command\n");
if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
res = send_waveform_to_channel(chan, waveform, filesize, args.interrupt);
ast_free(waveform);
}
break;
} else if (strcmp(ack, "LP\n") == 0) { /* receive an s-expr */
ast_debug(1, "Festival LP command\n");
if ((waveform = socket_receive_file_to_buff(fd, &filesize))) {
waveform[filesize] = '\0';
ast_log(LOG_WARNING, "Festival returned LP : %s\n", waveform);
ast_free(waveform);
}
} else if (strcmp(ack, "ER\n") == 0) { /* server got an error */
ast_log(LOG_WARNING, "Festival returned ER\n");
res = -1;
break;
}
} while (strcmp(ack, "OK\n") != 0);
close(fd);
ast_config_destroy(cfg);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
struct ast_flags config_flags = { 0 };
struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG, config_flags);
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");

112
trunk/apps/app_flash.c Normal file
View File

@@ -0,0 +1,112 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief App to flash a zap trunk
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
/*** MODULEINFO
<depend>zaptel</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/zapata.h"
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/image.h"
static char *app = "Flash";
static char *synopsis = "Flashes a Zap Trunk";
static char *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";
static inline int zt_wait_event(int fd)
{
/* Avoid the silly zt_waitevent which ignores a bunch of events */
int i,j=0;
i = ZT_IOMUX_SIGEVENT;
if (ioctl(fd, ZT_IOMUX, &i) == -1) return -1;
if (ioctl(fd, ZT_GETEVENT, &j) == -1) return -1;
return j;
}
static int flash_exec(struct ast_channel *chan, void *data)
{
int res = -1;
int x;
struct zt_params ztp;
if (strcasecmp(chan->tech->type, "Zap")) {
ast_log(LOG_WARNING, "%s is not a Zap channel\n", chan->name);
return -1;
}
memset(&ztp, 0, sizeof(ztp));
res = ioctl(chan->fds[0], ZT_GET_PARAMS, &ztp);
if (!res) {
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 */
zt_wait_event(chan->fds[0]);
}
res = ast_safe_sleep(chan, 1000);
ast_verb(3, "Flashed channel %s\n", chan->name);
} else
ast_log(LOG_WARNING, "Unable to flash channel %s: %s\n", chan->name, strerror(errno));
} else
ast_log(LOG_WARNING, "%s is not an FXO Channel\n", chan->name);
} else
ast_log(LOG_WARNING, "Unable to get parameters of %s: %s\n", chan->name, strerror(errno));
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, flash_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Flash channel application");

1059
trunk/apps/app_followme.c Normal file

File diff suppressed because it is too large Load Diff

99
trunk/apps/app_forkcdr.c Normal file
View File

@@ -0,0 +1,99 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Anthony Minessale anthmct@yahoo.com
* Development of this app Sponsered/Funded by TAAN Softworks Corp
*
* 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
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/cdr.h"
#include "asterisk/module.h"
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\n"
" Options:\n"
" v - If the option is passed all cdr variables will be passed along also.\n";
static void ast_cdr_fork(struct ast_channel *chan)
{
struct ast_cdr *cdr;
struct ast_cdr *newcdr;
struct ast_flags flags = { AST_CDR_FLAG_KEEP_VARS };
cdr = chan->cdr;
while (cdr->next)
cdr = cdr->next;
if (!(newcdr = ast_cdr_dup(cdr)))
return;
ast_cdr_append(cdr, newcdr);
ast_cdr_reset(newcdr, &flags);
if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS))
ast_cdr_free_vars(cdr, 0);
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;
if (!chan->cdr) {
ast_log(LOG_WARNING, "Channel does not have a CDR\n");
return 0;
}
if (!ast_strlen_zero(data))
ast_set2_flag(chan->cdr, strchr(data, 'v'), AST_CDR_FLAG_KEEP_VARS);
ast_cdr_fork(chan);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static 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");

130
trunk/apps/app_getcpeid.c Normal file
View File

@@ -0,0 +1,130 @@
/*
* 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 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/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/adsi.h"
static char *app = "GetCPEID";
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 zapata.conf for on-hook operations.\n";
static int cpeid_setstatus(struct ast_channel *chan, char *stuff[], int voice)
{
int justify[5] = { ADSI_JUST_CENT, ADSI_JUST_LEFT, ADSI_JUST_LEFT, ADSI_JUST_LEFT };
char *tmp[5];
int x;
for (x=0;x<4;x++)
tmp[x] = stuff[x];
tmp[4] = NULL;
return ast_adsi_print(chan, tmp, justify, voice);
}
static int cpeid_exec(struct ast_channel *chan, void *idata)
{
int res=0;
unsigned char cpeid[4];
int gotgeometry = 0;
int gotcpeid = 0;
int width, height, buttons;
char *data[4];
unsigned int x;
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);
if (res > 0) {
cpeid_setstatus(chan, data, 0);
res = ast_adsi_get_cpeid(chan, cpeid, 0);
if (res > 0) {
gotcpeid = 1;
ast_verb(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);
if (res > -1) {
ast_verb(3, "CPE has %d lines, %d columns, and %d buttons on '%s'\n", height, width, buttons, chan->name);
gotgeometry = 1;
}
}
if (res > -1) {
if (gotcpeid)
snprintf(data[1], 80, "CPEID: %02x:%02x:%02x:%02x", cpeid[0], cpeid[1], cpeid[2], cpeid[3]);
else
strcpy(data[1], "CPEID Unknown");
if (gotgeometry)
snprintf(data[2], 80, "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);
for(;;) {
res = ast_waitfordigit(chan, 1000);
if (res < 0)
break;
if (res == '#') {
res = 0;
break;
}
}
ast_adsi_unload_session(chan);
}
}
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, cpeid_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Get ADSI CPE ID");

205
trunk/apps/app_ices.c Normal file
View File

@@ -0,0 +1,205 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Stream to an icecast server via ICES (see contrib/asterisk-ices.xml)
*
* \author Mark Spencer <markster@digium.com>
*
* \extref ICES - http://www.icecast.org/ices.php
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <signal.h>
#include <fcntl.h>
#include <sys/time.h>
#include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/frame.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#define ICES "/usr/bin/ices"
#define LOCAL_ICES "/usr/local/bin/ices"
static char *app = "ICES";
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 examples/asterisk-ices.conf). \n";
static int icesencode(char *filename, int fd)
{
int res;
int x;
sigset_t fullset, oldset;
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;
}
/* Stop ignoring PIPE */
signal(SIGPIPE, SIG_DFL);
pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
if (ast_opt_high_priority)
ast_set_priority(0);
dup2(fd, STDIN_FILENO);
for (x=STDERR_FILENO + 1;x<1024;x++) {
if ((x != STDIN_FILENO) && (x != STDOUT_FILENO))
close(x);
}
/* 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\n");
_exit(0);
}
static int ices_exec(struct ast_channel *chan, void *data)
{
int res = 0;
int fds[2];
int ms = -1;
int pid = -1;
int flags;
int oreadformat;
struct timeval last;
struct ast_frame *f;
char filename[256]="";
char *c;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "ICES requires an argument (configfile.xml)\n");
return -1;
}
last = ast_tv(0, 0);
if (pipe(fds)) {
ast_log(LOG_WARNING, "Unable to create pipe\n");
return -1;
}
flags = fcntl(fds[1], F_GETFL);
fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
ast_stopstream(chan);
if (chan->_state != AST_STATE_UP)
res = ast_answer(chan);
if (res) {
close(fds[0]);
close(fds[1]);
ast_log(LOG_WARNING, "Answer failed!\n");
return -1;
}
oreadformat = chan->readformat;
res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
if (res < 0) {
close(fds[0]);
close(fds[1]);
ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
return -1;
}
if (((char *)data)[0] == '/')
ast_copy_string(filename, (char *) data, sizeof(filename));
else
snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_CONFIG_DIR, (char *)data);
/* Placeholder for options */
c = strchr(filename, '|');
if (c)
*c = '\0';
res = icesencode(filename, fds[0]);
close(fds[0]);
if (res >= 0) {
pid = res;
for (;;) {
/* Wait for audio, and stream */
ms = ast_waitfor(chan, -1);
if (ms < 0) {
ast_debug(1, "Hangup detected\n");
res = -1;
break;
}
f = ast_read(chan);
if (!f) {
ast_debug(1, "Null frame == hangup() detected\n");
res = -1;
break;
}
if (f->frametype == AST_FRAME_VOICE) {
res = write(fds[1], f->data, f->datalen);
if (res < 0) {
if (errno != EAGAIN) {
ast_log(LOG_WARNING, "Write failed to pipe: %s\n", strerror(errno));
res = -1;
ast_frfree(f);
break;
}
}
}
ast_frfree(f);
}
}
close(fds[1]);
if (pid > -1)
kill(pid, SIGKILL);
if (!res && oreadformat)
ast_set_read_format(chan, oreadformat);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static 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");

80
trunk/apps/app_image.c Normal file
View File

@@ -0,0 +1,80 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief App to transmit an image
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/image.h"
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 fails, the channel\n"
"will be hung up. Otherwise, the dialplan continues execution. This\n"
"application sets the following channel variable upon completion:\n"
" SENDIMAGESTATUS The status is the result of the attempt, one of:\n"
" OK | NOSUPPORT \n";
static int sendimage_exec(struct ast_channel *chan, void *data)
{
int res = 0;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "SendImage requires an argument (filename)\n");
return -1;
}
if (!ast_supports_images(chan)) {
/* Does not support transport */
pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "NOSUPPORT");
return 0;
}
if (!(res = ast_send_image(chan, data)))
pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "OK");
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, sendimage_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Image Transmission Application");

115
trunk/apps/app_ivrdemo.c Normal file
View File

@@ -0,0 +1,115 @@
/*
* 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 "asterisk/file.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;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "skel requires an argument (filename)\n");
return -1;
}
/* 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);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, skel_exec, tdesc, synopsis);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "IVR Demo Application");

971
trunk/apps/app_jack.c Normal file
View File

@@ -0,0 +1,971 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007 - 2008, Russell Bryant
*
* Russell Bryant <russell@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 Jack Application
*
* \author Russell Bryant <russell@digium.com>
*
* This is an application to connect an Asterisk channel to an input
* and output jack port so that the audio can be processed through
* another application, or to play audio from another application.
*
* \arg http://www.jackaudio.org/
*
* \ingroup applications
*/
/*** MODULEINFO
<depend>jack</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <limits.h>
#include <jack/jack.h>
#include <jack/ringbuffer.h>
#include "asterisk/module.h"
#include "asterisk/channel.h"
#include "asterisk/strings.h"
#include "asterisk/lock.h"
#include "asterisk/libresample.h"
#include "asterisk/app.h"
#include "asterisk/pbx.h"
#include "asterisk/audiohook.h"
#define RESAMPLE_QUALITY 0
#define RINGBUFFER_SIZE 16384
/*! \brief Common options between the Jack() app and JACK_HOOK() function */
#define COMMON_OPTIONS \
" s(<name>) - Connect to the specified jack server name.\n" \
" i(<name>) - Connect the output port that gets created to the specified\n" \
" jack input port.\n" \
" o(<name>) - Connect the input port that gets created to the specified\n" \
" jack output port.\n" \
" n - Do not automatically start the JACK server if it is not already\n" \
" running.\n"
static char *jack_app = "JACK";
static char *jack_synopsis =
"JACK (Jack Audio Connection Kit) Application";
static char *jack_desc =
"JACK([options])\n"
" When this application is executed, two jack ports will be created; one input\n"
"and one output. Other applications can be hooked up to these ports to access\n"
"the audio coming from, or being sent to the channel.\n"
" Valid options:\n"
COMMON_OPTIONS
"";
struct jack_data {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(server_name);
AST_STRING_FIELD(connect_input_port);
AST_STRING_FIELD(connect_output_port);
);
jack_client_t *client;
jack_port_t *input_port;
jack_port_t *output_port;
jack_ringbuffer_t *input_rb;
jack_ringbuffer_t *output_rb;
void *output_resampler;
double output_resample_factor;
void *input_resampler;
double input_resample_factor;
unsigned int stop:1;
unsigned int has_audiohook:1;
unsigned int no_start_server:1;
/*! Only used with JACK_HOOK */
struct ast_audiohook audiohook;
};
static const struct {
jack_status_t status;
const char *str;
} jack_status_table[] = {
{ JackFailure, "Failure" },
{ JackInvalidOption, "Invalid Option" },
{ JackNameNotUnique, "Name Not Unique" },
{ JackServerStarted, "Server Started" },
{ JackServerFailed, "Server Failed" },
{ JackServerError, "Server Error" },
{ JackNoSuchClient, "No Such Client" },
{ JackLoadFailure, "Load Failure" },
{ JackInitFailure, "Init Failure" },
{ JackShmFailure, "Shared Memory Access Failure" },
{ JackVersionError, "Version Mismatch" },
};
static const char *jack_status_to_str(jack_status_t status)
{
int i;
for (i = 0; i < ARRAY_LEN(jack_status_table); i++) {
if (jack_status_table[i].status == status)
return jack_status_table[i].str;
}
return "Unknown Error";
}
static void log_jack_status(const char *prefix, jack_status_t status)
{
struct ast_str *str = ast_str_alloca(512);
int i, first = 0;
for (i = 0; i < (sizeof(status) * 8); i++) {
if (!(status & (1 << i)))
continue;
if (!first) {
ast_str_set(&str, 0, "%s", jack_status_to_str((1 << i)));
first = 1;
} else
ast_str_append(&str, 0, ", %s", jack_status_to_str((1 << i)));
}
ast_log(LOG_NOTICE, "%s: %s\n", prefix, str->str);
}
static int alloc_resampler(struct jack_data *jack_data, int input)
{
double from_srate, to_srate, jack_srate;
void **resampler;
double *resample_factor;
if (input && jack_data->input_resampler)
return 0;
if (!input && jack_data->output_resampler)
return 0;
jack_srate = jack_get_sample_rate(jack_data->client);
/* XXX Hard coded 8 kHz */
to_srate = input ? 8000.0 : jack_srate;
from_srate = input ? jack_srate : 8000.0;
resample_factor = input ? &jack_data->input_resample_factor :
&jack_data->output_resample_factor;
if (from_srate == to_srate) {
/* Awesome! The jack sample rate is the same as ours.
* Resampling isn't needed. */
*resample_factor = 1.0;
return 0;
}
*resample_factor = to_srate / from_srate;
resampler = input ? &jack_data->input_resampler :
&jack_data->output_resampler;
if (!(*resampler = resample_open(RESAMPLE_QUALITY,
*resample_factor, *resample_factor))) {
ast_log(LOG_ERROR, "Failed to open %s resampler\n",
input ? "input" : "output");
return -1;
}
return 0;
}
/*!
* \brief Handle jack input port
*
* Read nframes number of samples from the input buffer, resample it
* if necessary, and write it into the appropriate ringbuffer.
*/
static void handle_input(void *buf, jack_nframes_t nframes,
struct jack_data *jack_data)
{
short s_buf[nframes];
float *in_buf = buf;
size_t res;
int i;
size_t write_len = sizeof(s_buf);
if (jack_data->input_resampler) {
int total_in_buf_used = 0;
int total_out_buf_used = 0;
float f_buf[nframes + 1];
memset(f_buf, 0, sizeof(f_buf));
while (total_in_buf_used < nframes) {
int in_buf_used;
int out_buf_used;
out_buf_used = resample_process(jack_data->input_resampler,
jack_data->input_resample_factor,
&in_buf[total_in_buf_used], nframes - total_in_buf_used,
0, &in_buf_used,
&f_buf[total_out_buf_used], ARRAY_LEN(f_buf) - total_out_buf_used);
if (out_buf_used < 0)
break;
total_out_buf_used += out_buf_used;
total_in_buf_used += in_buf_used;
if (total_out_buf_used == ARRAY_LEN(f_buf)) {
ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size, "
"nframes '%d', total_out_buf_used '%d'\n", nframes, total_out_buf_used);
break;
}
}
for (i = 0; i < total_out_buf_used; i++)
s_buf[i] = f_buf[i] * (SHRT_MAX / 1.0);
write_len = total_out_buf_used * sizeof(int16_t);
} else {
/* No resampling needed */
for (i = 0; i < nframes; i++)
s_buf[i] = in_buf[i] * (SHRT_MAX / 1.0);
}
res = jack_ringbuffer_write(jack_data->input_rb, (const char *) s_buf, write_len);
if (res != write_len) {
ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n",
(int) sizeof(s_buf), (int) res);
}
}
/*!
* \brief Handle jack output port
*
* Read nframes number of samples from the ringbuffer and write it out to the
* output port buffer.
*/
static void handle_output(void *buf, jack_nframes_t nframes,
struct jack_data *jack_data)
{
size_t res, len;
len = nframes * sizeof(float);
res = jack_ringbuffer_read(jack_data->output_rb, buf, len);
if (len != res) {
ast_debug(2, "Wanted %d bytes to send to the output port, "
"but only got %d\n", (int) len, (int) res);
}
}
static int jack_process(jack_nframes_t nframes, void *arg)
{
struct jack_data *jack_data = arg;
void *input_port_buf, *output_port_buf;
if (!jack_data->input_resample_factor)
alloc_resampler(jack_data, 1);
input_port_buf = jack_port_get_buffer(jack_data->input_port, nframes);
handle_input(input_port_buf, nframes, jack_data);
output_port_buf = jack_port_get_buffer(jack_data->output_port, nframes);
handle_output(output_port_buf, nframes, jack_data);
return 0;
}
static void jack_shutdown(void *arg)
{
struct jack_data *jack_data = arg;
jack_data->stop = 1;
}
static struct jack_data *destroy_jack_data(struct jack_data *jack_data)
{
if (jack_data->input_port) {
jack_port_unregister(jack_data->client, jack_data->input_port);
jack_data->input_port = NULL;
}
if (jack_data->output_port) {
jack_port_unregister(jack_data->client, jack_data->output_port);
jack_data->output_port = NULL;
}
if (jack_data->client) {
jack_client_close(jack_data->client);
jack_data->client = NULL;
}
if (jack_data->input_rb) {
jack_ringbuffer_free(jack_data->input_rb);
jack_data->input_rb = NULL;
}
if (jack_data->output_rb) {
jack_ringbuffer_free(jack_data->output_rb);
jack_data->output_rb = NULL;
}
if (jack_data->output_resampler) {
resample_close(jack_data->output_resampler);
jack_data->output_resampler = NULL;
}
if (jack_data->input_resampler) {
resample_close(jack_data->input_resampler);
jack_data->input_resampler = NULL;
}
if (jack_data->has_audiohook)
ast_audiohook_destroy(&jack_data->audiohook);
ast_string_field_free_memory(jack_data);
ast_free(jack_data);
return NULL;
}
static int init_jack_data(struct ast_channel *chan, struct jack_data *jack_data)
{
const char *chan_name;
jack_status_t status = 0;
jack_options_t jack_options = JackNullOption;
ast_channel_lock(chan);
chan_name = ast_strdupa(chan->name);
ast_channel_unlock(chan);
if (!(jack_data->output_rb = jack_ringbuffer_create(RINGBUFFER_SIZE)))
return -1;
if (!(jack_data->input_rb = jack_ringbuffer_create(RINGBUFFER_SIZE)))
return -1;
if (jack_data->no_start_server)
jack_options |= JackNoStartServer;
if (!ast_strlen_zero(jack_data->server_name)) {
jack_options |= JackServerName;
jack_data->client = jack_client_open(chan_name, jack_options, &status,
jack_data->server_name);
} else {
jack_data->client = jack_client_open(chan_name, jack_options, &status);
}
if (status)
log_jack_status("Client Open Status", status);
if (!jack_data->client)
return -1;
jack_data->input_port = jack_port_register(jack_data->client, "input",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0);
if (!jack_data->input_port) {
ast_log(LOG_ERROR, "Failed to create input port for jack port\n");
return -1;
}
jack_data->output_port = jack_port_register(jack_data->client, "output",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0);
if (!jack_data->output_port) {
ast_log(LOG_ERROR, "Failed to create output port for jack port\n");
return -1;
}
if (jack_set_process_callback(jack_data->client, jack_process, jack_data)) {
ast_log(LOG_ERROR, "Failed to register process callback with jack client\n");
return -1;
}
jack_on_shutdown(jack_data->client, jack_shutdown, jack_data);
if (jack_activate(jack_data->client)) {
ast_log(LOG_ERROR, "Unable to activate jack client\n");
return -1;
}
while (!ast_strlen_zero(jack_data->connect_input_port)) {
const char **ports;
int i;
ports = jack_get_ports(jack_data->client, jack_data->connect_input_port,
NULL, JackPortIsInput);
if (!ports) {
ast_log(LOG_ERROR, "No input port matching '%s' was found\n",
jack_data->connect_input_port);
break;
}
for (i = 0; ports[i]; i++) {
ast_debug(1, "Found port '%s' that matched specified input port '%s'\n",
ports[i], jack_data->connect_input_port);
}
if (jack_connect(jack_data->client, jack_port_name(jack_data->output_port), ports[0])) {
ast_log(LOG_ERROR, "Failed to connect '%s' to '%s'\n", ports[0],
jack_port_name(jack_data->output_port));
} else {
ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
jack_port_name(jack_data->output_port));
}
free((void *) ports);
break;
}
while (!ast_strlen_zero(jack_data->connect_output_port)) {
const char **ports;
int i;
ports = jack_get_ports(jack_data->client, jack_data->connect_output_port,
NULL, JackPortIsOutput);
if (!ports) {
ast_log(LOG_ERROR, "No output port matching '%s' was found\n",
jack_data->connect_output_port);
break;
}
for (i = 0; ports[i]; i++) {
ast_debug(1, "Found port '%s' that matched specified output port '%s'\n",
ports[i], jack_data->connect_output_port);
}
if (jack_connect(jack_data->client, ports[0], jack_port_name(jack_data->input_port))) {
ast_log(LOG_ERROR, "Failed to connect '%s' to '%s'\n", ports[0],
jack_port_name(jack_data->input_port));
} else {
ast_debug(1, "Connected '%s' to '%s'\n", ports[0],
jack_port_name(jack_data->input_port));
}
free((void *) ports);
break;
}
return 0;
}
static int queue_voice_frame(struct jack_data *jack_data, struct ast_frame *f)
{
float f_buf[f->samples * 8];
size_t f_buf_used = 0;
int i;
int16_t *s_buf = f->data;
size_t res;
memset(f_buf, 0, sizeof(f_buf));
if (!jack_data->output_resample_factor)
alloc_resampler(jack_data, 0);
if (jack_data->output_resampler) {
float in_buf[f->samples];
int total_in_buf_used = 0;
int total_out_buf_used = 0;
memset(in_buf, 0, sizeof(in_buf));
for (i = 0; i < f->samples; i++)
in_buf[i] = s_buf[i] * (1.0 / SHRT_MAX);
while (total_in_buf_used < ARRAY_LEN(in_buf)) {
int in_buf_used;
int out_buf_used;
out_buf_used = resample_process(jack_data->output_resampler,
jack_data->output_resample_factor,
&in_buf[total_in_buf_used], ARRAY_LEN(in_buf) - total_in_buf_used,
0, &in_buf_used,
&f_buf[total_out_buf_used], ARRAY_LEN(f_buf) - total_out_buf_used);
if (out_buf_used < 0)
break;
total_out_buf_used += out_buf_used;
total_in_buf_used += in_buf_used;
if (total_out_buf_used == ARRAY_LEN(f_buf)) {
ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size\n");
break;
}
}
f_buf_used = total_out_buf_used;
if (f_buf_used > ARRAY_LEN(f_buf))
f_buf_used = ARRAY_LEN(f_buf);
} else {
/* No resampling needed */
for (i = 0; i < f->samples; i++)
f_buf[i] = s_buf[i] * (1.0 / SHRT_MAX);
f_buf_used = f->samples;
}
res = jack_ringbuffer_write(jack_data->output_rb, (const char *) f_buf, f_buf_used * sizeof(float));
if (res != (f_buf_used * sizeof(float))) {
ast_debug(2, "Tried to write %d bytes to the ringbuffer, but only wrote %d\n",
(int) (f_buf_used * sizeof(float)), (int) res);
}
return 0;
}
/*!
* \brief handle jack audio
*
* \param[in] chan The Asterisk channel to write the frames to if no output frame
* is provided.
* \param[in] jack_data This is the jack_data struct that contains the input
* ringbuffer that audio will be read from.
* \param[out] out_frame If this argument is non-NULL, then assuming there is
* enough data avilable in the ringbuffer, the audio in this frame
* will get replaced with audio from the input buffer. If there is
* not enough data available to read at this time, then the frame
* data gets zeroed out.
*
* Read data from the input ringbuffer, which is the properly resampled audio
* that was read from the jack input port. Write it to the channel in 20 ms frames,
* or fill up an output frame instead if one is provided.
*
* \return Nothing.
*/
static void handle_jack_audio(struct ast_channel *chan, struct jack_data *jack_data,
struct ast_frame *out_frame)
{
short buf[160];
struct ast_frame f = {
.frametype = AST_FRAME_VOICE,
.subclass = AST_FORMAT_SLINEAR,
.src = "JACK",
.data = buf,
.datalen = sizeof(buf),
.samples = ARRAY_LEN(buf),
};
for (;;) {
size_t res, read_len;
char *read_buf;
read_len = out_frame ? out_frame->datalen : sizeof(buf);
read_buf = out_frame ? out_frame->data : buf;
res = jack_ringbuffer_read_space(jack_data->input_rb);
if (res < read_len) {
/* Not enough data ready for another frame, move on ... */
if (out_frame) {
ast_debug(1, "Sending an empty frame for the JACK_HOOK\n");
memset(out_frame->data, 0, out_frame->datalen);
}
break;
}
res = jack_ringbuffer_read(jack_data->input_rb, (char *) read_buf, read_len);
if (res < read_len) {
ast_log(LOG_ERROR, "Error reading from ringbuffer, even though it said there was enough data\n");
break;
}
if (out_frame) {
/* If an output frame was provided, then we just want to fill up the
* buffer in that frame and return. */
break;
}
ast_write(chan, &f);
}
}
enum {
OPT_SERVER_NAME = (1 << 0),
OPT_INPUT_PORT = (1 << 1),
OPT_OUTPUT_PORT = (1 << 2),
OPT_NOSTART_SERVER = (1 << 3),
};
enum {
OPT_ARG_SERVER_NAME,
OPT_ARG_INPUT_PORT,
OPT_ARG_OUTPUT_PORT,
/* Must be the last element */
OPT_ARG_ARRAY_SIZE,
};
AST_APP_OPTIONS(jack_exec_options, BEGIN_OPTIONS
AST_APP_OPTION_ARG('s', OPT_SERVER_NAME, OPT_ARG_SERVER_NAME),
AST_APP_OPTION_ARG('i', OPT_INPUT_PORT, OPT_ARG_INPUT_PORT),
AST_APP_OPTION_ARG('o', OPT_OUTPUT_PORT, OPT_ARG_OUTPUT_PORT),
AST_APP_OPTION('n', OPT_NOSTART_SERVER),
END_OPTIONS );
static struct jack_data *jack_data_alloc(void)
{
struct jack_data *jack_data;
if (!(jack_data = ast_calloc(1, sizeof(*jack_data))))
return NULL;
if (ast_string_field_init(jack_data, 32)) {
ast_free(jack_data);
return NULL;
}
return jack_data;
}
/*!
* \note This must be done before calling init_jack_data().
*/
static int handle_options(struct jack_data *jack_data, const char *__options_str)
{
struct ast_flags options = { 0, };
char *option_args[OPT_ARG_ARRAY_SIZE];
char *options_str;
options_str = ast_strdupa(__options_str);
ast_app_parse_options(jack_exec_options, &options, option_args, options_str);
if (ast_test_flag(&options, OPT_SERVER_NAME)) {
if (!ast_strlen_zero(option_args[OPT_ARG_SERVER_NAME]))
ast_string_field_set(jack_data, server_name, option_args[OPT_ARG_SERVER_NAME]);
else {
ast_log(LOG_ERROR, "A server name must be provided with the s() option\n");
return -1;
}
}
if (ast_test_flag(&options, OPT_INPUT_PORT)) {
if (!ast_strlen_zero(option_args[OPT_ARG_INPUT_PORT]))
ast_string_field_set(jack_data, connect_input_port, option_args[OPT_ARG_INPUT_PORT]);
else {
ast_log(LOG_ERROR, "A name must be provided with the i() option\n");
return -1;
}
}
if (ast_test_flag(&options, OPT_OUTPUT_PORT)) {
if (!ast_strlen_zero(option_args[OPT_ARG_OUTPUT_PORT]))
ast_string_field_set(jack_data, connect_output_port, option_args[OPT_ARG_OUTPUT_PORT]);
else {
ast_log(LOG_ERROR, "A name must be provided with the o() option\n");
return -1;
}
}
jack_data->no_start_server = ast_test_flag(&options, OPT_NOSTART_SERVER) ? 1 : 0;
return 0;
}
static int jack_exec(struct ast_channel *chan, void *data)
{
struct jack_data *jack_data;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(options);
);
if (!(jack_data = jack_data_alloc()))
return -1;
args.options = data;
if (!ast_strlen_zero(args.options) && handle_options(jack_data, args.options)) {
destroy_jack_data(jack_data);
return -1;
}
if (init_jack_data(chan, jack_data)) {
destroy_jack_data(jack_data);
return -1;
}
if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
destroy_jack_data(jack_data);
return -1;
}
if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
destroy_jack_data(jack_data);
return -1;
}
while (!jack_data->stop) {
struct ast_frame *f;
ast_waitfor(chan, -1);
f = ast_read(chan);
if (!f) {
jack_data->stop = 1;
continue;
}
switch (f->frametype) {
case AST_FRAME_CONTROL:
if (f->subclass == AST_CONTROL_HANGUP)
jack_data->stop = 1;
break;
case AST_FRAME_VOICE:
queue_voice_frame(jack_data, f);
default:
break;
}
ast_frfree(f);
handle_jack_audio(chan, jack_data, NULL);
}
jack_data = destroy_jack_data(jack_data);
return 0;
}
static void jack_hook_ds_destroy(void *data)
{
struct jack_data *jack_data = data;
destroy_jack_data(jack_data);
}
static const struct ast_datastore_info jack_hook_ds_info = {
.type = "JACK_HOOK",
.destroy = jack_hook_ds_destroy,
};
static int jack_hook_callback(struct ast_audiohook *audiohook, struct ast_channel *chan,
struct ast_frame *frame, enum ast_audiohook_direction direction)
{
struct ast_datastore *datastore;
struct jack_data *jack_data;
if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
return 0;
if (direction != AST_AUDIOHOOK_DIRECTION_READ)
return 0;
if (frame->frametype != AST_FRAME_VOICE)
return 0;
if (frame->subclass != AST_FORMAT_SLINEAR) {
ast_log(LOG_WARNING, "Expected frame in SLINEAR for the audiohook, but got format %d\n",
frame->subclass);
return 0;
}
ast_channel_lock(chan);
if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
ast_log(LOG_ERROR, "JACK_HOOK datastore not found for '%s'\n", chan->name);
ast_channel_unlock(chan);
return -1;
}
jack_data = datastore->data;
queue_voice_frame(jack_data, frame);
handle_jack_audio(chan, jack_data, frame);
ast_channel_unlock(chan);
return 0;
}
static int enable_jack_hook(struct ast_channel *chan, char *data)
{
struct ast_datastore *datastore;
struct jack_data *jack_data = NULL;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(mode);
AST_APP_ARG(options);
);
AST_STANDARD_APP_ARGS(args, data);
ast_channel_lock(chan);
if ((datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
ast_log(LOG_ERROR, "JACK_HOOK already enabled for '%s'\n", chan->name);
goto return_error;
}
if (ast_strlen_zero(args.mode) || strcasecmp(args.mode, "manipulate")) {
ast_log(LOG_ERROR, "'%s' is not a supported mode. Only manipulate is supported.\n",
S_OR(args.mode, "<none>"));
goto return_error;
}
if (!(jack_data = jack_data_alloc()))
goto return_error;
if (!ast_strlen_zero(args.options) && handle_options(jack_data, args.options))
goto return_error;
if (init_jack_data(chan, jack_data))
goto return_error;
if (!(datastore = ast_channel_datastore_alloc(&jack_hook_ds_info, NULL)))
goto return_error;
jack_data->has_audiohook = 1;
ast_audiohook_init(&jack_data->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "JACK_HOOK");
jack_data->audiohook.manipulate_callback = jack_hook_callback;
datastore->data = jack_data;
if (ast_audiohook_attach(chan, &jack_data->audiohook))
goto return_error;
if (ast_channel_datastore_add(chan, datastore))
goto return_error;
ast_channel_unlock(chan);
return 0;
return_error:
ast_channel_unlock(chan);
if (jack_data)
destroy_jack_data(jack_data);
return -1;
}
static int disable_jack_hook(struct ast_channel *chan)
{
struct ast_datastore *datastore;
struct jack_data *jack_data;
ast_channel_lock(chan);
if (!(datastore = ast_channel_datastore_find(chan, &jack_hook_ds_info, NULL))) {
ast_channel_unlock(chan);
ast_log(LOG_WARNING, "No JACK_HOOK found to disable\n");
return -1;
}
ast_channel_datastore_remove(chan, datastore);
jack_data = datastore->data;
ast_audiohook_detach(&jack_data->audiohook);
/* Keep the channel locked while we destroy the datastore, so that we can
* ensure that all of the jack stuff is stopped just in case another frame
* tries to come through the audiohook callback. */
ast_channel_datastore_free(datastore);
ast_channel_unlock(chan);
return 0;
}
static int jack_hook_write(struct ast_channel *chan, const char *cmd, char *data,
const char *value)
{
int res;
if (!strcasecmp(value, "on"))
res = enable_jack_hook(chan, data);
else if (!strcasecmp(value, "off"))
res = disable_jack_hook(chan);
else {
ast_log(LOG_ERROR, "'%s' is not a valid value for JACK_HOOK()\n", value);
res = -1;
}
return res;
}
static struct ast_custom_function jack_hook_function = {
.name = "JACK_HOOK",
.synopsis = "Enable a jack hook on a channel",
.syntax = "JACK_HOOK(<mode>,[options])",
.desc =
" The JACK_HOOK allows turning on or off jack connectivity to this channel.\n"
"When the JACK_HOOK is turned on, jack ports will get created that allow\n"
"access to the audio stream for this channel. The mode specifies which mode\n"
"this hook should run in. A mode must be specified when turning the JACK_HOOK.\n"
"on. However, all arguments are optional when turning it off.\n"
"\n"
" Valid modes are:\n"
#if 0
/* XXX TODO */
" spy - Create a read-only audio hook. Only an output jack port will\n"
" get created.\n"
" whisper - Create a write-only audio hook. Only an input jack port will\n"
" get created.\n"
#endif
" manipulate - Create a read/write audio hook. Both an input and an output\n"
" jack port will get created. Audio from the channel will be\n"
" sent out the output port and will be replaced by the audio\n"
" coming in on the input port as it gets passed on.\n"
"\n"
" Valid options are:\n"
COMMON_OPTIONS
"\n"
" Examples:\n"
" To turn on the JACK_HOOK,\n"
" Set(JACK_HOOK(manipulate,i(pure_data_0:input0)o(pure_data_0:output0))=on)\n"
" To turn off the JACK_HOOK,\n"
" Set(JACK_HOOK()=off)\n"
"",
.write = jack_hook_write,
};
static int unload_module(void)
{
int res;
res = ast_unregister_application(jack_app);
res |= ast_custom_function_unregister(&jack_hook_function);
return res;
}
static int load_module(void)
{
if (ast_register_application(jack_app, jack_exec, jack_synopsis, jack_desc))
return AST_MODULE_LOAD_DECLINE;
if (ast_custom_function_register(&jack_hook_function)) {
ast_unregister_application(jack_app);
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "JACK Interface");

527
trunk/apps/app_macro.c Normal file
View File

@@ -0,0 +1,527 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Dial plan macro Implementation
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/config.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#define MAX_ARGS 80
/* special result value used to force macro exit */
#define MACRO_EXIT_RESULT 1024
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"
"${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"
"Extensions: While a macro is being executed, it becomes the current context.\n"
" This means that if a hangup occurs, for instance, that the macro\n"
" will be searched for an 'h' extension, NOT the context from which\n"
" the macro was called. So, make sure to define all appropriate\n"
" extensions in your macro! (Note: AEL does not use macros)\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 macros\n"
" could cause asterisk to crash earlier than this limit. It is advised that\n"
" if you need to deeply nest macro calls, that you use the Gosub application\n"
" (now allows arguments like a Macro) with explict Return() calls instead.\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";
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 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, dead = 0, inhangup = 0;
char *save_macro_exten;
char *save_macro_context;
char *save_macro_priority;
char *save_macro_offset;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
return -1;
}
/* does the user want a deeper rabbit hole? */
s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
if (s)
sscanf(s, "%d", &maxdepth);
/* Count how many levels deep the rabbit hole goes */
s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
if (s)
sscanf(s, "%d", &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, "%d", &inhangup);
if (depth >= maxdepth) {
ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
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");
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);
return 0;
}
/* If we are to run the macro exclusively, take the mutex */
if (exclusive) {
ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
ast_autoservice_start(chan);
if (ast_context_lockmacro(fullmacro)) {
ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
ast_autoservice_stop(chan);
return 0;
}
ast_autoservice_stop(chan);
}
/* 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);
save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
/* 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;
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;
int foundx;
runningapp[0] = '\0';
runningdata[0] = '\0';
/* What application will execute? */
if (ast_rdlock_contexts()) {
ast_log(LOG_WARNING, "Failed to lock contexts list\n");
} else {
for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
if (!strcmp(ast_get_context_name(c), chan->context)) {
if (ast_rdlock_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();
/* 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, &foundx,1))) {
/* Something bad happened, or a hangup has been requested. */
if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
(res == '*') || (res == '#')) {
/* Just return result as to the previous application as if it had been dialed */
ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
break;
}
switch(res) {
case MACRO_EXIT_RESULT:
res = 0;
goto out;
case AST_PBX_KEEPALIVE:
ast_debug(2, "Spawn extension (%s,%s,%d) exited KEEPALIVE in macro %s on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
ast_verb(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:
ast_debug(2, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
dead = 1;
goto out;
}
}
ast_debug(1, "Executed application: %s\n", runningapp);
if (!strcasecmp(runningapp, "GOSUB")) {
gosub_level++;
ast_debug(1, "Incrementing gosub_level\n");
} else if (!strcasecmp(runningapp, "GOSUBIF")) {
char 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_debug(1, "Incrementing gosub_level\n");
}
} else {
if (!ast_strlen_zero(app2)) {
gosub_level++;
ast_debug(1, "Incrementing gosub_level\n");
}
}
} else if (!strcasecmp(runningapp, "RETURN")) {
gosub_level--;
ast_debug(1, "Decrementing gosub_level\n");
} else if (!strcasecmp(runningapp, "STACKPOP")) {
gosub_level--;
ast_debug(1, "Decrementing gosub_level\n");
} else if (!strncasecmp(runningapp, "EXEC", 4)) {
/* Must evaluate args to find actual app */
char tmp2[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_debug(1, "Last app: %s\n", tmp3);
if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
gosub_level++;
ast_debug(1, "Incrementing gosub_level\n");
} else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
gosub_level--;
ast_debug(1, "Decrementing gosub_level\n");
} else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
gosub_level--;
ast_debug(1, "Decrementing gosub_level\n");
}
}
if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
break;
}
/* don't stop executing extensions when we're in "h" */
if (ast_check_hangup(chan) && !inhangup) {
ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", chan->exten, chan->macroexten, chan->priority);
goto out;
}
chan->priority++;
}
out:
/* 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);
if (!dead) {
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]) {
if (!dead)
pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
ast_free(oldargs[x]);
} else if (!dead) {
pbx_builtin_setvar_helper(chan, varname, NULL);
}
}
/* Restore macro variables */
if (!dead) {
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)
ast_free(save_macro_exten);
if (save_macro_context)
ast_free(save_macro_context);
if (save_macro_priority)
ast_free(save_macro_priority);
if (!dead && setmacrocontext) {
chan->macrocontext[0] = '\0';
chan->macroexten[0] = '\0';
chan->macropriority = 0;
}
if (!dead && !strcasecmp(chan->context, fullmacro)) {
/* If we're leaving the macro normally, restore original information */
chan->priority = oldpriority;
ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
if (!(ast_check_hangup(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
/* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
const char *offsets;
ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
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, "%d", &offset) == 1) {
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
chan->priority += offset;
}
}
}
}
}
if (!dead)
pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
if (save_macro_offset)
ast_free(save_macro_offset);
/* Unlock the macro */
if (exclusive) {
ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
if (ast_context_unlockmacro(fullmacro)) {
ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
res = 0;
}
}
return res;
}
static int macro_exec(struct ast_channel *chan, void *data)
{
return _macro_exec(chan, data, 0);
}
static int macroexclusive_exec(struct ast_channel *chan, void *data)
{
return _macro_exec(chan, data, 1);
}
static int macroif_exec(struct ast_channel *chan, void *data)
{
char *expr = NULL, *label_a = NULL, *label_b = NULL;
int res = 0;
if (!(expr = ast_strdupa(data)))
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");
return res;
}
static int macro_exit_exec(struct ast_channel *chan, void *data)
{
return MACRO_EXIT_RESULT;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(if_app);
res |= ast_unregister_application(exit_app);
res |= ast_unregister_application(app);
res |= ast_unregister_application(exclusive_app);
return res;
}
static int load_module(void)
{
int res;
res = ast_register_application(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;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");

5592
trunk/apps/app_meetme.c Normal file

File diff suppressed because it is too large Load Diff

134
trunk/apps/app_milliwatt.c Normal file
View File

@@ -0,0 +1,134 @@
/*
* 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 Digital Milliwatt Test
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "asterisk/channel.h"
static char *app = "Milliwatt";
static char *synopsis = "Generate a Constant 1000Hz tone at 0dbm (mu-law)";
static char *descrip =
"Milliwatt(): Generate a Constant 1000Hz tone at 0dbm (mu-law)\n";
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));
}
static void milliwatt_release(struct ast_channel *chan, void *data)
{
ast_free(data);
return;
}
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__,
};
/* 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;
}
len = samples * sizeof (buf[0]);
wf.datalen = len;
wf.samples = samples;
/* create a buffer containing the digital milliwatt pattern */
for (i = 0; i < len; i++) {
buf[AST_FRIENDLY_OFFSET + i] = digital_milliwatt[(*indexp)++];
*indexp &= 7;
}
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 =
{
alloc: milliwatt_alloc,
release: milliwatt_release,
generate: milliwatt_generate,
};
static int milliwatt_exec(struct ast_channel *chan, void *data)
{
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 unload_module(void)
{
return ast_unregister_application(app);
}
static 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");

3109
trunk/apps/app_minivm.c Normal file

File diff suppressed because it is too large Load Diff

424
trunk/apps/app_mixmonitor.c Normal file
View File

@@ -0,0 +1,424 @@
/*
* 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 "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
#include "asterisk/file.h"
#include "asterisk/audiohook.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/channel.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"
"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.\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"
" 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"
"Stops the audio recording that was started with a call to MixMonitor()\n"
"on the current channel.\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 ast_channel *chan;
};
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),
});
static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
{
struct ast_channel *peer = NULL;
int res = 0;
if (!chan)
return -1;
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_thread(void *obj)
{
struct mixmonitor *mixmonitor = obj;
struct ast_filestream *fs = NULL;
unsigned int oflags;
char *ext;
int errflag = 0;
ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
ast_audiohook_lock(&mixmonitor->audiohook);
while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
struct ast_frame *fr = NULL;
ast_audiohook_trigger_wait(&mixmonitor->audiohook);
if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
break;
if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR)))
continue;
if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || ast_bridged_channel(mixmonitor->chan)) {
/* Initialize the file if not already done so */
if (!fs && !errflag) {
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 frame */
if (fs)
ast_writestream(fs, fr);
}
/* All done! free it. */
ast_frame_free(fr, 0);
}
ast_audiohook_detach(&mixmonitor->audiohook);
ast_audiohook_unlock(&mixmonitor->audiohook);
ast_audiohook_destroy(&mixmonitor->audiohook);
ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
if (fs)
ast_closestream(fs);
if (mixmonitor->post_process) {
ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
ast_safe_system(mixmonitor->post_process);
}
ast_free(mixmonitor);
return NULL;
}
static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
int readvol, int writevol, const char *post_process)
{
pthread_t thread;
struct mixmonitor *mixmonitor;
char postprocess2[1024] = "";
size_t len;
len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
postprocess2[0] = 0;
/* 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 = ast_calloc(1, len))) {
return;
}
/* Copy over flags and channel name */
mixmonitor->flags = flags;
mixmonitor->chan = chan;
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);
/* Setup the actual spy before creating our thread */
if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
ast_free(mixmonitor);
return;
}
ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_WRITE);
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);
ast_audiohook_destroy(&mixmonitor->audiohook);
ast_free(mixmonitor);
return;
}
ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
}
static int mixmonitor_exec(struct ast_channel *chan, void *data)
{
int x, readvol = 0, writevol = 0;
struct ast_flags flags = {0};
char *parse, *tmp, *slash;
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;
}
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");
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], "%d", &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], "%d", &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], "%d", &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;
}
tmp = ast_strdupa(args.filename);
if ((slash = strrchr(tmp, '/')))
*slash = '\0';
ast_mkdir(tmp, 0777);
pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);
return 0;
}
static int stop_mixmonitor_exec(struct ast_channel *chan, void *data)
{
ast_audiohook_detach_source(chan, mixmonitor_spy_type);
return 0;
}
static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ast_channel *chan;
switch (cmd) {
case CLI_INIT:
e->command = "mixmonitor [start|stop]";
e->usage =
"Usage: mixmonitor <start|stop> <chan_name> [args]\n"
" The optional arguments are passed to the MixMonitor\n"
" application when the 'start' command is used.\n";
return NULL;
case CLI_GENERATE:
return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
}
if (a->argc < 3)
return CLI_SHOWUSAGE;
if (!(chan = ast_get_channel_by_name_prefix_locked(a->argv[2], strlen(a->argv[2])))) {
ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
/* Technically this is a failure, but we don't want 2 errors printing out */
return CLI_SUCCESS;
}
if (!strcasecmp(a->argv[1], "start")) {
mixmonitor_exec(chan, a->argv[3]);
ast_channel_unlock(chan);
} else {
ast_channel_unlock(chan);
ast_audiohook_detach_source(chan, mixmonitor_spy_type);
}
return CLI_SUCCESS;
}
static struct ast_cli_entry cli_mixmonitor[] = {
AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
};
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);
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");

161
trunk/apps/app_morsecode.c Normal file
View File

@@ -0,0 +1,161 @@
/*
* 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 "asterisk/file.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 =
" 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;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Syntax: Morsecode(<string>) - no argument found\n");
return 0;
}
/* Use variable MORESEDITLEN, if set (else 80) */
ditlenc = pbx_builtin_getvar_helper(chan, "MORSEDITLEN");
if (ast_strlen_zero(ditlenc) || (sscanf(ditlenc, "%d", &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, "%d", &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);
}
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app_morsecode);
}
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");

235
trunk/apps/app_mp3.c Normal file
View File

@@ -0,0 +1,235 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Silly application to play an MP3 file -- uses mpg123
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/time.h>
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/frame.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#define LOCAL_MPG_123 "/usr/local/bin/mpg123"
#define MPG_123 "/usr/bin/mpg123"
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.";
static int mp3play(char *filename, int fd)
{
int res;
int x;
sigset_t fullset, oldset;
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;
}
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++) {
if (x != STDOUT_FILENO)
close(x);
}
/* Execute mpg123, but buffer if it's a net connection */
if (!strncasecmp(filename, "http://", 7)) {
/* Most commonly installed in /usr/local/bin */
execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
/* But many places has it in /usr/bin */
execl(MPG_123, "mpg123", "-q", "-s", "-b", "1024","-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
/* As a last-ditch effort, try to use PATH */
execlp("mpg123", "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
}
else {
/* Most commonly installed in /usr/local/bin */
execl(MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
/* But many places has it in /usr/bin */
execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
/* As a last-ditch effort, try to use PATH */
execlp("mpg123", "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
}
ast_log(LOG_WARNING, "Execute of mpg123 failed\n");
_exit(0);
}
static int timed_read(int fd, void *data, int datalen, int timeout)
{
int res;
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;
res = poll(fds, 1, timeout);
if (res < 1) {
ast_log(LOG_NOTICE, "Poll timed out/errored out with %d\n", res);
return -1;
}
return read(fd, data, datalen);
}
static int mp3_exec(struct ast_channel *chan, void *data)
{
int res=0;
int fds[2];
int ms = -1;
int pid = -1;
int owriteformat;
int timeout = 2000;
struct timeval next;
struct ast_frame *f;
struct myframe {
struct ast_frame f;
char offset[AST_FRIENDLY_OFFSET];
short frdata[160];
} myf;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "MP3 Playback requires an argument (filename)\n");
return -1;
}
if (pipe(fds)) {
ast_log(LOG_WARNING, "Unable to create pipe\n");
return -1;
}
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");
return -1;
}
res = mp3play((char *)data, fds[1]);
if (!strncasecmp((char *)data, "http://", 7)) {
timeout = 10000;
}
/* Wait 1000 ms first */
next = ast_tvnow();
next.tv_sec += 1;
if (res >= 0) {
pid = res;
/* Order is important -- there's almost always going to be mp3... we want to prioritize the
user */
for (;;) {
ms = ast_tvdiff_ms(next, ast_tvnow());
if (ms <= 0) {
res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata), timeout);
if (res > 0) {
myf.f.frametype = AST_FRAME_VOICE;
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.delivery.tv_sec = 0;
myf.f.delivery.tv_usec = 0;
myf.f.data = myf.frdata;
if (ast_write(chan, &myf.f) < 0) {
res = -1;
break;
}
} else {
ast_debug(1, "No more mp3\n");
res = 0;
break;
}
next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
} else {
ms = ast_waitfor(chan, ms);
if (ms < 0) {
ast_debug(1, "Hangup detected\n");
res = -1;
break;
}
if (ms) {
f = ast_read(chan);
if (!f) {
ast_debug(1, "Null frame == hangup() detected\n");
res = -1;
break;
}
if (f->frametype == AST_FRAME_DTMF) {
ast_debug(1, "User pressed a key\n");
ast_frfree(f);
res = 0;
break;
}
ast_frfree(f);
}
}
}
}
close(fds[0]);
close(fds[1]);
if (pid > -1)
kill(pid, SIGKILL);
if (!res && owriteformat)
ast_set_write_format(chan, owriteformat);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, mp3_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly MP3 Application");

218
trunk/apps/app_nbscat.c Normal file
View File

@@ -0,0 +1,218 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Silly application to play an NBScat file -- uses nbscat8k
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <fcntl.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/frame.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#define LOCAL_NBSCAT "/usr/local/bin/nbscat8k"
#define NBSCAT "/usr/bin/nbscat8k"
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif
static char *app = "NBScat";
static 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";
static int NBScatplay(int fd)
{
int res;
int x;
sigset_t fullset, oldset;
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;
}
signal(SIGPIPE, SIG_DFL);
pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
if (ast_opt_high_priority)
ast_set_priority(0);
dup2(fd, STDOUT_FILENO);
for (x = STDERR_FILENO + 1; x < 1024; x++) {
if (x != STDOUT_FILENO)
close(x);
}
/* Most commonly installed in /usr/local/bin */
execl(NBSCAT, "nbscat8k", "-d", (char *)NULL);
execl(LOCAL_NBSCAT, "nbscat8k", "-d", (char *)NULL);
ast_log(LOG_WARNING, "Execute of nbscat8k failed\n");
_exit(0);
}
static int timed_read(int fd, void *data, int datalen)
{
int res;
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;
res = poll(fds, 1, 2000);
if (res < 1) {
ast_log(LOG_NOTICE, "Selected timed out/errored out with %d\n", res);
return -1;
}
return read(fd, data, datalen);
}
static int NBScat_exec(struct ast_channel *chan, void *data)
{
int res=0;
int fds[2];
int ms = -1;
int pid = -1;
int owriteformat;
struct timeval next;
struct ast_frame *f;
struct myframe {
struct ast_frame f;
char offset[AST_FRIENDLY_OFFSET];
short frdata[160];
} myf;
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds)) {
ast_log(LOG_WARNING, "Unable to create socketpair\n");
return -1;
}
ast_stopstream(chan);
owriteformat = 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");
return -1;
}
res = NBScatplay(fds[1]);
/* Wait 1000 ms first */
next = ast_tvnow();
next.tv_sec += 1;
if (res >= 0) {
pid = res;
/* Order is important -- there's almost always going to be mp3... we want to prioritize the
user */
for (;;) {
ms = ast_tvdiff_ms(next, ast_tvnow());
if (ms <= 0) {
res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata));
if (res > 0) {
myf.f.frametype = AST_FRAME_VOICE;
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.delivery.tv_sec = 0;
myf.f.delivery.tv_usec = 0;
myf.f.data = myf.frdata;
if (ast_write(chan, &myf.f) < 0) {
res = -1;
break;
}
} else {
ast_debug(1, "No more mp3\n");
res = 0;
break;
}
next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
} else {
ms = ast_waitfor(chan, ms);
if (ms < 0) {
ast_debug(1, "Hangup detected\n");
res = -1;
break;
}
if (ms) {
f = ast_read(chan);
if (!f) {
ast_debug(1, "Null frame == hangup() detected\n");
res = -1;
break;
}
if (f->frametype == AST_FRAME_DTMF) {
ast_debug(1, "User pressed a key\n");
ast_frfree(f);
res = 0;
break;
}
ast_frfree(f);
}
}
}
}
close(fds[0]);
close(fds[1]);
if (pid > -1)
kill(pid, SIGKILL);
if (!res && owriteformat)
ast_set_write_format(chan, owriteformat);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, NBScat_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly NBS Stream Application");

2041
trunk/apps/app_osplookup.c Normal file

File diff suppressed because it is too large Load Diff

193
trunk/apps/app_page.c Normal file
View File

@@ -0,0 +1,193 @@
/*
* 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>zaptel</depend>
<depend>app_meetme</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#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/devicestate.h"
#include "asterisk/dial.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"
" s - only dial channel if devicestate says it is not in use\n";
enum {
PAGE_DUPLEX = (1 << 0),
PAGE_QUIET = (1 << 1),
PAGE_RECORD = (1 << 2),
PAGE_SKIP = (1 << 3),
} 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),
AST_APP_OPTION('s', PAGE_SKIP),
});
#define MAX_DIALS 128
static int page_exec(struct ast_channel *chan, void *data)
{
char *options, *tech, *resource, *tmp;
char meetmeopts[88], originator[AST_CHANNEL_NAME], *opts[0];
struct ast_flags flags = { 0 };
unsigned int confid = ast_random();
struct ast_app *app;
int res = 0, pos = 0, i = 0;
struct ast_dial *dials[MAX_DIALS];
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "This application requires at least one argument (destination(s) to page)\n");
return -1;
}
if (!(app = pbx_findapp("MeetMe"))) {
ast_log(LOG_WARNING, "There is no MeetMe application available!\n");
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, opts, 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" : "") );
/* Go through parsing/calling each device */
while ((tech = strsep(&tmp, "&"))) {
int state = 0;
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;
}
/* Ensure device is not in use if skip option is enabled */
if (ast_test_flag(&flags, PAGE_SKIP) && (state = ast_device_state(tech)) != AST_DEVICE_NOT_INUSE) {
ast_log(LOG_WARNING, "Destination '%s' has device state '%s'.\n", tech, devstate2str(state));
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 */
dials[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 = dials[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);
}
return -1;
}
static int unload_module(void)
{
return ast_unregister_application(app_page);
}
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");

View File

@@ -0,0 +1,183 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* Author: Ben Miller <bgmiller@dccinc.com>
* With TONS of help from Mark!
*
* 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 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 "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/features.h"
#include "asterisk/say.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/app.h"
static char *app = "ParkAndAnnounce";
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";
static int parkandannounce_exec(struct ast_channel *chan, void *data)
{
int res = -1;
int lot, timeout = 0, dres;
char *dialtech, *tmp[100], buf[13];
int looptemp, i;
char *s;
struct ast_channel *dchan;
struct outgoing_helper oh = { 0, };
int outstate;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(template);
AST_APP_ARG(timeout);
AST_APP_ARG(dial);
AST_APP_ARG(return_context);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "ParkAndAnnounce requires arguments: (announce:template|timeout|dial|[return_context])\n");
return -1;
}
s = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, s);
if (args.timeout)
timeout = atoi(args.timeout) * 1000;
if (ast_strlen_zero(args.dial)) {
ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or Zap/g1/5551212\n");
return -1;
}
dialtech = strsep(&args.dial, "/");
ast_verb(3, "Dial Tech,String: (%s,%s)\n", dialtech, args.dial);
if (!ast_strlen_zero(args.return_context))
ast_parseable_goto(chan, args.return_context);
ast_verb(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_verb(3, "Warning: Return Context Invalid, call will return to default|s\n");
}
/* 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. */
ast_masq_park_call(chan, NULL, timeout, &lot);
ast_verb(3, "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, args.return_context);
/* Now place the call to the extension */
snprintf(buf, sizeof(buf), "%d", lot);
oh.parent_channel = chan;
oh.vars = ast_variable_new("_PARKEDAT", buf, "");
dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, args.dial, 30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh);
if (dchan) {
if (dchan->_state == AST_STATE_UP) {
ast_verb(4, "Channel %s was answered.\n", dchan->name);
} else {
ast_verb(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);
return -1;
}
} else {
ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
return -1;
}
ast_stopstream(dchan);
/* now we have the call placed and are ready to play stuff to it */
ast_verb(4, "Announce Template:%s\n", args.template);
for (looptemp = 0, tmp[looptemp++] = strsep(&args.template, ":");
looptemp < sizeof(tmp) / sizeof(tmp[0]);
tmp[looptemp++] = strsep(&args.template, ":"));
for (i = 0; i < looptemp; i++) {
ast_verb(4, "Announce:%s\n", tmp[i]);
if (!strcmp(tmp[i], "PARKED")) {
ast_say_digits(dchan, lot, "", dchan->language);
} else {
dres = ast_streamfile(dchan, tmp[i], dchan->language);
if (!dres) {
dres = ast_waitstream(dchan, "");
} else {
ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], dchan->name);
dres = 0;
}
}
}
ast_stopstream(dchan);
ast_hangup(dchan);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static 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");

174
trunk/apps/app_pickupchan.c Normal file
View File

@@ -0,0 +1,174 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2008, Gary Cook
*
* 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 Pickup a ringing channel
*
* \author Gary Cook
*
* \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"
static const char *app = "PickupChan";
static const char *synopsis = "Pickup a ringing channel";
static const char *descrip =
" PickupChan(channel[&channel...]): This application can pickup any ringing channel\n";
/*! \todo This application should return a result code, like PICKUPRESULT */
/*! \brief Helper function that determines whether a channel is capable of being picked up */
static int can_pickup(struct ast_channel *chan)
{
ast_debug(3, "Checking Pickup '%s' state '%s ( %d )'\n", chan->name, ast_state2str(chan->_state), chan->_state);
if (!chan->pbx && (chan->_state == AST_STATE_RINGING || chan->_state == AST_STATE_RING)) {
return 1;
} else {
return 0;
}
}
/*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
static struct ast_channel *my_ast_get_channel_by_name_locked(char *channame)
{
struct ast_channel *chan;
char *chkchan = alloca(strlen(channame) + 2);
/* need to append a '-' for the comparison so we check full channel name,
* i.e SIP/hgc- , use a temporary variable so original stays the same for
* debugging.
*/
strcpy(chkchan, channame);
strcat(chkchan, "-");
for (chan = ast_walk_channel_by_name_prefix_locked(NULL, channame, strlen(channame));
chan;
chan = ast_walk_channel_by_name_prefix_locked(chan, channame, strlen(channame))) {
if (!strncasecmp(chan->name, chkchan, strlen(chkchan)) && can_pickup(chan))
return chan;
ast_channel_unlock(chan);
}
return NULL;
}
/*! \brief Perform actual pickup between two channels */
static int pickup_do(struct ast_channel *chan, struct ast_channel *target)
{
int res = 0;
ast_debug(3, "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;
}
/*! \brief Attempt to pick up specified channel named , does not use context */
static int pickup_by_channel(struct ast_channel *chan, char *pickup)
{
int res = 0;
struct ast_channel *target;
if (!(target = my_ast_get_channel_by_name_locked(pickup)))
return -1;
/* Just check that we are not picking up the SAME as target */
if (chan->name != target->name && chan != target) {
res = pickup_do(chan, target);
ast_channel_unlock(target);
}
return res;
}
/*! \brief Main application entry point */
static int pickupchan_exec(struct ast_channel *chan, void *data)
{
int res = 0;
struct ast_module_user *u = NULL;
char *tmp = ast_strdupa(data);
char *pickup = NULL, *context = NULL;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Pickup requires an argument (channel)!\n");
return -1;
}
u = ast_module_user_add(chan);
/* Parse channel (and ignore context if there) */
while (!ast_strlen_zero(tmp) && (pickup = strsep(&tmp, "&"))) {
if ((context = strchr(pickup , '@'))) {
*context++ = '\0';
}
if (!strncasecmp(chan->name, pickup , strlen(pickup))) {
ast_log(LOG_NOTICE, "Cannot pickup your own channel %s.\n", pickup);
} else {
if (!pickup_by_channel(chan, pickup)) {
break;
}
ast_log(LOG_NOTICE, "No target channel found for %s.\n", pickup);
}
}
ast_module_user_remove(u);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, pickupchan_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel Pickup Application");

524
trunk/apps/app_playback.c Normal file
View File

@@ -0,0 +1,524 @@
/*
* 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 Trivial application to playback a sound file
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
/* This file provides config-file based 'say' functions, and implenents
* some CLI commands.
*/
#include "asterisk/say.h" /* provides config-file based 'say' functions */
#include "asterisk/cli.h"
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 comma.\n"
"The 'skip' 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"
"be answered before the sound is played. Not all channels support playing\n"
"messages while still on hook.\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"
;
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;
ast_debug(2, "string <%s> depth <%d>\n", s, depth);
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 */
ast_debug(2, "try <%s> in <%s>\n", s, a->language);
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;
ast_debug(2, "value is <%s>\n", s);
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 */
pbx_substitute_variables_varshead(&head, x, fn, sizeof(fn));
ast_debug(2, "doing [%s]\n", fn);
/* locate prefix and data, if any */
fmt = index(fn, ':');
if (!fmt || fmt == fn) { /* regular filename */
ret = s_streamwait3(a, fn);
continue;
}
fmt++;
data = index(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 = index(fn2, '\'');
if (!y) {
p = data; /* invalid. prepare to end */
break;
}
*y = '\0';
ast_trim_blanks(fn2);
p = index(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 ast_tm tm;
struct timeval tv = { t, 0 };
say_args_t a = { chan, ints, lang, -1, -1 };
if (format == NULL)
format = "";
ast_localtime(&tv, &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_mode(const char *mode) {
if (!strcmp(mode, say_new)) {
if (say_cfg == NULL) {
ast_log(LOG_ERROR, "There is no say.conf file to use new mode\n");
return -1;
}
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 if (strcmp(mode, say_old)) {
ast_log(LOG_WARNING, "unrecognized mode %s\n", mode);
return -1;
}
return 0;
}
static char *__say_cli_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
const char *old_mode = say_api_buf[0] ? say_new : say_old;
char *mode;
switch (cmd) {
case CLI_INIT:
e->command = "say load [new|old]";
e->usage =
"Usage: say load [new|old]\n"
" say load\n"
" Report status of current say mode\n"
" say load new\n"
" Set say method, configured in say.conf\n"
" say load old\n"
" Set old say method, coded in asterisk core\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc == 2) {
ast_cli(a->fd, "say mode is [%s]\n", old_mode);
return CLI_SUCCESS;
} else if (a->argc != 3)
return CLI_SHOWUSAGE;
mode = a->argv[2];
if (!strcmp(mode, old_mode))
ast_cli(a->fd, "say mode is %s already\n", mode);
else
if (say_init_mode(mode) == 0)
ast_cli(a->fd, "setting say mode from %s to %s\n", old_mode, mode);
return CLI_SUCCESS;
}
static struct ast_cli_entry cli_playback[] = {
AST_CLI_DEFINE(__say_cli_init, "Set or show the say mode"),
};
static int playback_exec(struct ast_channel *chan, void *data)
{
int res = 0;
int mres = 0;
char *tmp;
int option_skip=0;
int option_say=0;
int option_noanswer = 0;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(filenames);
AST_APP_ARG(options);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Playback requires an argument (filename)\n");
return -1;
}
tmp = ast_strdupa(data);
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 (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)
/* 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);
res = 0;
mres = 1;
}
}
}
done:
pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS");
return res;
}
static int reload(void)
{
struct ast_variable *v;
struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
struct ast_config *newcfg;
if ((newcfg = ast_config_load("say.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
return 0;
if (say_cfg) {
ast_config_destroy(say_cfg);
ast_log(LOG_NOTICE, "Reloading say.conf\n");
say_cfg = newcfg;
}
if (say_cfg) {
for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
if (ast_extension_match(v->name, "mode")) {
say_init_mode(v->value);
break;
}
}
}
/*
* 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;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(app);
ast_cli_unregister_multiple(cli_playback, sizeof(cli_playback) / sizeof(struct ast_cli_entry));
if (say_cfg)
ast_config_destroy(say_cfg);
return res;
}
static int load_module(void)
{
struct ast_variable *v;
struct ast_flags config_flags = { 0 };
say_cfg = ast_config_load("say.conf", config_flags);
if (say_cfg) {
for (v = ast_variable_browse(say_cfg, "general"); v ; v = v->next) {
if (ast_extension_match(v->name, "mode")) {
say_init_mode(v->value);
break;
}
}
}
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,
);

173
trunk/apps/app_privacy.c Normal file
View File

@@ -0,0 +1,173 @@
/*
* 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 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 "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/utils.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"
static char *app = "PrivacyManager";
static char *synopsis = "Require phone number to be entered, if no CallerID sent";
static char *descrip =
" PrivacyManager([maxretries][,minlength]): 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 'maxretries' attempts to do so.\n"
"The application does nothing if Caller*ID was received on the channel.\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"
"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"
;
static int privacy_exec (struct ast_channel *chan, void *data)
{
int res=0;
int retries;
int maxretries = 3;
int minlength = 10;
int x = 0;
char phone[30];
char *parse = NULL;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(maxretries);
AST_APP_ARG(minlength);
AST_APP_ARG(options);
);
if (!ast_strlen_zero(chan->cid.cid_num)) {
if (option_verbose > 2)
ast_verbose (VERBOSE_PREFIX_3 "CallerID Present: Skipping\n");
} else {
/*Answer the channel if it is not already*/
if (chan->_state != AST_STATE_UP) {
if ((res = ast_answer(chan)))
return -1;
}
if (!ast_strlen_zero(data)) {
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (args.maxretries) {
if (sscanf(args.maxretries, "%d", &x) == 1)
maxretries = x;
else
ast_log(LOG_WARNING, "Invalid max retries argument\n");
}
if (args.minlength) {
if (sscanf(args.minlength, "%d", &x) == 1)
minlength = x;
else
ast_log(LOG_WARNING, "Invalid min length argument\n");
}
}
/* Play unidentified call */
res = ast_safe_sleep(chan, 1000);
if (!res)
res = ast_streamfile(chan, "privacy-unident", chan->language);
if (!res)
res = ast_waitstream(chan, "");
/* 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, "#");
if (res < 0)
break;
/* Make sure we get at least digits */
if (strlen(phone) >= minlength )
break;
else {
res = ast_streamfile(chan, "privacy-incorrect", chan->language);
if (!res)
res = ast_waitstream(chan, "");
}
}
/* Got a number, play sounds and send them on their way */
if ((retries < maxretries) && res >= 0 ) {
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");
} else {
pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "FAILED");
}
}
return 0;
}
static int unload_module(void)
{
return ast_unregister_application (app);
}
static int load_module(void)
{
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");

6153
trunk/apps/app_queue.c Normal file

File diff suppressed because it is too large Load Diff

231
trunk/apps/app_read.c Normal file
View File

@@ -0,0 +1,231 @@
/*
* 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 Trivial application to read a variable
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/pbx.h"
#include "asterisk/channel.h"
#include "asterisk/app.h"
#include "asterisk/module.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 *app = "Read";
static char *synopsis = "Read a variable";
static char *descrip =
" Read(variable[,filename[&filename2...]][,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(s) to play before reading digits or tone with option i\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 -- The number of seconds to wait for a digit response. If greater\n"
" than 0, that value will override the default timeout. Can be floating point.\n"
"This application sets the following channel variable upon completion:\n"
" READSTATUS - This is the status of the read operation.\n"
" Possible values are:\n"
" OK | ERROR | HANGUP | INTERRUPTED | SKIPPED | TIMEOUT\n";
#define ast_next_data(instr,ptr,delim) if((ptr=strchr(instr,delim))) { *(ptr) = '\0' ; ptr++;}
static int read_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char tmp[256] = "";
int maxdigits = 255;
int tries = 1, to = 0, x = 0;
double tosec;
char *argcopy = NULL;
struct ind_tone_zone_sound *ts = NULL;
struct ast_flags flags = {0};
const char *status = "ERROR";
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);
);
pbx_builtin_setvar_helper(chan, "READSTATUS", status);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "Read requires an argument (variable)\n");
return 0;
}
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)) {
tosec = atof(arglist.timeout);
if (tosec <= 0)
to = 0;
else
to = tosec * 1000.0;
}
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
ast_verb(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");
return 0;
}
if (ast_test_flag(&flags, OPT_INDICATION)) {
if (! ast_strlen_zero(arglist.filename)) {
ts = ast_get_indication_tone(chan->zone, arglist.filename);
}
}
if (chan->_state != AST_STATE_UP) {
if (ast_test_flag(&flags, OPT_SKIP)) {
/* At the user's option, skip if the line is not up */
pbx_builtin_setvar_helper(chan, arglist.variable, "");
pbx_builtin_setvar_helper(chan, "READSTATUS", "SKIPPED");
return 0;
} else if (!ast_test_flag(&flags, OPT_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) {
if (res == 0)
status = "TIMEOUT";
tmp[x]='\0';
break;
}
tmp[x++] = res;
if (tmp[x-1] == '#') {
tmp[x-1] = '\0';
status = "OK";
break;
}
if (x >= maxdigits) {
status = "OK";
}
}
} else {
res = ast_app_getdata(chan, arglist.filename, tmp, maxdigits, to);
if (res == 0)
status = "OK";
else if (res == 1)
status = "TIMEOUT";
else if (res == 2)
status = "INTERRUPTED";
}
if (res > -1) {
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
if (!ast_strlen_zero(tmp)) {
ast_verb(3, "User entered '%s'\n", tmp);
tries = 0;
} else {
tries--;
if (tries)
ast_verb(3, "User entered nothing, %d chance%s left\n", tries, (tries != 1) ? "s" : "");
else
ast_verb(3, "User entered nothing.\n");
}
res = 0;
} else {
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
ast_verb(3, "User disconnected\n");
}
}
}
if (ast_check_hangup(chan))
status = "HANGUP";
pbx_builtin_setvar_helper(chan, "READSTATUS", status);
return 0;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, read_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read Variable Application");

258
trunk/apps/app_readexten.c Normal file
View File

@@ -0,0 +1,258 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007 Dave Chappell
*
* David Chappell <David.Chappell@trincoll.edu>
*
* 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 an extension into a variable
*
* \author David Chappell <David.Chappell@trincoll.edu>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/module.h"
#include "asterisk/indications.h"
#include "asterisk/channel.h"
enum {
OPT_SKIP = (1 << 0),
OPT_INDICATION = (1 << 1),
OPT_NOANSWER = (1 << 2),
} readexten_option_flags;
AST_APP_OPTIONS(readexten_app_options, {
AST_APP_OPTION('s', OPT_SKIP),
AST_APP_OPTION('i', OPT_INDICATION),
AST_APP_OPTION('n', OPT_NOANSWER),
});
static char *app = "ReadExten";
static char *synopsis = "Read an extension into a variable";
static char *descrip =
" ReadExten(<variable>[,[<filename>][,[<context>][,[<option>][,<timeout>]]]])\n\n"
"Reads a #-terminated string of digits from the user into the given variable.\n"
" filename file to play before reading digits or tone with option i\n"
" context context in which to match extensions\n"
" option options are:\n"
" s - Return immediately if the channel is not answered,\n"
" i - Play filename as an indication tone from your\n"
" indications.conf\n"
" n - Read digits even if the channel is not answered.\n"
" timeout An integer number of seconds to wait for a digit response. If\n"
" greater than 0, that value will override the default timeout.\n\n"
"ReadExten will set READEXTENSTATUS on exit with one of the following statuses:\n"
" OK A valid extension exists in ${variable}\n"
" TIMEOUT No extension was entered in the specified time\n"
" INVALID An invalid extension, ${INVALID_EXTEN}, was entered\n"
" SKIP Line was not up and the option 's' was specified\n"
" ERROR Invalid arguments were passed\n";
static int readexten_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char exten[256] = "";
int maxdigits = sizeof(exten) - 1;
int timeout = 0, digit_timeout = 0, x = 0;
char *argcopy = NULL, *status = "";
struct ind_tone_zone_sound *ts = NULL;
struct ast_flags flags = {0};
AST_DECLARE_APP_ARGS(arglist,
AST_APP_ARG(variable);
AST_APP_ARG(filename);
AST_APP_ARG(context);
AST_APP_ARG(options);
AST_APP_ARG(timeout);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "ReadExten requires at least one argument\n");
pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", "ERROR");
return 0;
}
argcopy = ast_strdupa(data);
AST_STANDARD_APP_ARGS(arglist, argcopy);
if (ast_strlen_zero(arglist.variable)) {
ast_log(LOG_WARNING, "Invalid! Usage: ReadExten(variable[|filename][|context][|options][|timeout])\n\n");
pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", "ERROR");
return 0;
}
if (ast_strlen_zero(arglist.filename))
arglist.filename = NULL;
if (ast_strlen_zero(arglist.context))
arglist.context = chan->context;
if (!ast_strlen_zero(arglist.options))
ast_app_parse_options(readexten_app_options, &flags, NULL, arglist.options);
if (!ast_strlen_zero(arglist.timeout)) {
timeout = atoi(arglist.timeout);
if (timeout > 0)
timeout *= 1000;
}
if (timeout <= 0)
timeout = chan->pbx ? chan->pbx->rtimeout * 1000 : 10000;
if (digit_timeout <= 0)
digit_timeout = chan->pbx ? chan->pbx->dtimeout * 1000 : 5000;
if (ast_test_flag(&flags, OPT_INDICATION) && !ast_strlen_zero(arglist.filename))
ts = ast_get_indication_tone(chan->zone, arglist.filename);
do {
if (chan->_state != AST_STATE_UP) {
if (ast_test_flag(&flags, OPT_SKIP)) {
/* At the user's option, skip if the line is not up */
pbx_builtin_setvar_helper(chan, arglist.variable, "");
status = "SKIP";
break;
} else if (!ast_test_flag(&flags, OPT_NOANSWER)) {
/* Otherwise answer unless we're supposed to read while on-hook */
res = ast_answer(chan);
}
}
if (res < 0) {
status = "HANGUP";
break;
}
ast_playtones_stop(chan);
ast_stopstream(chan);
if (ts && ts->data[0])
res = ast_playtones_start(chan, 0, ts->data, 0);
else if (arglist.filename)
res = ast_streamfile(chan, arglist.filename, chan->language);
for (x = 0; x < maxdigits; x++) {
ast_debug(3, "extension so far: '%s', timeout: %d\n", exten, timeout);
res = ast_waitfordigit(chan, timeout);
ast_playtones_stop(chan);
ast_stopstream(chan);
timeout = digit_timeout;
if (res < 1) { /* timeout expired or hangup */
if (ast_check_hangup(chan))
status = "HANGUP";
else
status = "TIMEOUT";
break;
} else if (res == '#') {
break;
}
exten[x] = res;
if (!ast_matchmore_extension(chan, arglist.context, exten, 1 /* priority */, chan->cid.cid_num)) {
break;
}
}
if (!ast_strlen_zero(status))
break;
if (ast_exists_extension(chan, arglist.context, exten, 1, chan->cid.cid_num)) {
ast_debug(3, "User entered valid extension '%s'\n", exten);
pbx_builtin_setvar_helper(chan, arglist.variable, exten);
status = "OK";
} else {
ast_debug(3, "User dialed invalid extension '%s' in context '%s' on %s\n", exten, arglist.context, chan->name);
pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
status = "INVALID";
}
} while (0);
pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", status);
return status[0] == 'H' ? -1 : 0;
}
static int acf_isexten_exec(struct ast_channel *chan, const char *cmd, char *parse, char *buffer, size_t buflen)
{
int priority_int;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(context);
AST_APP_ARG(extension);
AST_APP_ARG(priority);
);
AST_STANDARD_APP_ARGS(args, parse);
if (ast_strlen_zero(args.context))
args.context = chan->context;
if (ast_strlen_zero(args.extension)) {
ast_log(LOG_WARNING, "Syntax: VALID_EXTEN([<context>],<extension>[,<priority>]) - missing argument <extension>!\n");
return -1;
}
if (ast_strlen_zero(args.priority))
priority_int = 1;
else
priority_int = atoi(args.priority);
if (ast_exists_extension(chan, args.context, args.extension, priority_int, chan->cid.cid_num))
ast_copy_string(buffer, "1", buflen);
else
ast_copy_string(buffer, "0", buflen);
return 0;
}
static struct ast_custom_function acf_isexten = {
.name = "VALID_EXTEN",
.synopsis = "Determine whether an extension exists or not",
.syntax = "VALID_EXTEN([<context>],<extension>[,<priority>])",
.desc =
"Returns a true value if the indicated context, extension, and priority exist.\n"
"Context defaults to the current context, priority defaults to 1.\n",
.read = acf_isexten_exec,
};
static int unload_module(void)
{
int res = ast_unregister_application(app);
res |= ast_custom_function_unregister(&acf_isexten);
return res;
}
static int load_module(void)
{
int res = ast_register_application(app, readexten_exec, synopsis, descrip);
res |= ast_custom_function_register(&acf_isexten);
return res;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read and evaluate extension validity");

107
trunk/apps/app_readfile.c Normal file
View File

@@ -0,0 +1,107 @@
/*
* 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 "asterisk/file.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 = "Read the contents of a text file into a channel variable";
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;
char *s, *varname=NULL, *file=NULL, *length=NULL, *returnvar=NULL;
int len=0;
static int deprecation_warning = 0;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "ReadFile require an argument!\n");
return -1;
}
s = ast_strdupa(data);
varname = strsep(&s, "=");
file = strsep(&s, ",");
length = s;
if (deprecation_warning++ % 10 == 0)
ast_log(LOG_WARNING, "ReadFile has been deprecated in favor of Set(%s=${FILE(%s,0,%s)})\n", varname, file, length);
if (!varname || !file) {
ast_log(LOG_ERROR, "No file or variable specified!\n");
return -1;
}
if (length) {
if ((sscanf(length, "%d", &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);
ast_free(returnvar);
}
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app_readfile);
}
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");

365
trunk/apps/app_record.c Normal file
View File

@@ -0,0 +1,365 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, 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.
*
* 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
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
#include "asterisk/dsp.h" /* use dsp routines for silence detection */
static char *app = "Record";
static char *synopsis = "Record to a file";
static char *descrip =
" Record(filename.format,silence[,maxduration][,options])\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"
"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"
"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";
enum {
OPTION_APPEND = (1 << 0),
OPTION_NOANSWER = (1 << 1),
OPTION_QUIET = (1 << 2),
OPTION_SKIP = (1 << 3),
OPTION_STAR_TERMINATE = (1 << 4),
OPTION_IGNORE_TERMINATE = (1 << 5),
FLAG_HAS_PERCENT = (1 << 6),
};
AST_APP_OPTIONS(app_opts,{
AST_APP_OPTION('a', OPTION_APPEND),
AST_APP_OPTION('n', OPTION_NOANSWER),
AST_APP_OPTION('q', OPTION_QUIET),
AST_APP_OPTION('s', OPTION_SKIP),
AST_APP_OPTION('t', OPTION_STAR_TERMINATE),
AST_APP_OPTION('x', OPTION_IGNORE_TERMINATE),
});
static int record_exec(struct ast_channel *chan, void *data)
{
int res = 0;
int count = 0;
char *ext = NULL, *opts[0];
char *parse, *dir, *file;
int i = 0;
char tmp[256];
struct ast_filestream *s = NULL;
struct ast_frame *f = NULL;
struct ast_dsp *sildet = NULL; /* silence detector dsp */
int totalsilence = 0;
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 */
int gottimeout = 0; /* did we timeout for maxduration exceeded? */
int terminator = '#';
int rfmt = 0;
int ioflags;
int waitres;
struct ast_silence_generator *silgen = NULL;
struct ast_flags flags = { 0, };
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(filename);
AST_APP_ARG(silence);
AST_APP_ARG(maxduration);
AST_APP_ARG(options);
);
/* 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 */
ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
return -1;
}
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (args.argc == 4)
ast_app_parse_options(app_opts, &flags, opts, args.options);
if (!ast_strlen_zero(args.filename)) {
if (strstr(args.filename, "%d"))
ast_set_flag(&flags, FLAG_HAS_PERCENT);
ext = strrchr(args.filename, '.'); /* to support filename with a . in the filename, not format */
if (!ext)
ext = strchr(args.filename, ':');
if (ext) {
*ext = '\0';
ext++;
}
}
if (!ext) {
ast_log(LOG_WARNING, "No extension specified to filename!\n");
return -1;
}
if (args.silence) {
if ((sscanf(args.silence, "%d", &i) == 1) && (i > -1)) {
silence = i * 1000;
} else if (!ast_strlen_zero(args.silence)) {
ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", args.silence);
}
}
if (args.maxduration) {
if ((sscanf(args.maxduration, "%d", &i) == 1) && (i > -1))
/* Convert duration to milliseconds */
maxduration = i * 1000;
else if (!ast_strlen_zero(args.maxduration))
ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", args.maxduration);
}
if (ast_test_flag(&flags, OPTION_STAR_TERMINATE))
terminator = '*';
if (ast_test_flag(&flags, OPTION_IGNORE_TERMINATE))
terminator = '\0';
/* 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 (ast_test_flag(&flags, FLAG_HAS_PERCENT)) {
AST_DECLARE_APP_ARGS(fname,
AST_APP_ARG(piece)[100];
);
char *tmp2 = ast_strdupa(args.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);
}
count++;
} while (ast_fileexists(tmp, ext, chan->language) > 0);
pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
} else
ast_copy_string(tmp, args.filename, sizeof(tmp));
/* end of routine mentioned */
if (chan->_state != AST_STATE_UP) {
if (ast_test_flag(&flags, OPTION_SKIP)) {
/* At the user's option, skip if the line is not up */
return 0;
} else if (!ast_test_flag(&flags, OPTION_NOANSWER)) {
/* Otherwise answer unless we're supposed to record while on-hook */
res = ast_answer(chan);
}
}
if (res) {
ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
goto out;
}
if (!ast_test_flag(&flags, OPTION_QUIET)) {
/* 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) {
res = ast_waitstream(chan, "");
} else {
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");
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);
}
/* Create the directory if it does not exist. */
dir = ast_strdupa(tmp);
if ((file = strrchr(dir, '/')))
*file++ = '\0';
ast_mkdir (dir, 0777);
ioflags = ast_test_flag(&flags, OPTION_APPEND) ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
s = ast_writefile(tmp, ext, NULL, ioflags, 0, AST_FILE_MODE);
if (!s) {
ast_log(LOG_WARNING, "Could not create file %s\n", args.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;
}
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;
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_debug(1, "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:
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);
if (sildet)
ast_dsp_free(sildet);
}
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, record_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Trivial Record Application");

7482
trunk/apps/app_rpt.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,112 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (c) 2003, 2006 Tilghman Lesher. All rights reserved.
* Copyright (c) 2006 Digium, Inc.
*
* 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 "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/say.h"
#include "asterisk/app.h"
static char *app_sayunixtime = "SayUnixTime";
static char *app_datetime = "DateTime";
static char *sayunixtime_synopsis = "Says a specified time in a custom format";
static char *sayunixtime_descrip =
"SayUnixTime([unixtime][,[timezone][,format]])\n"
" unixtime - time, in seconds since Jan 1, 1970. May be negative.\n"
" defaults to now.\n"
" 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";
static char *datetime_descrip =
"DateTime([unixtime][,[timezone][,format]])\n"
" unixtime - time, in seconds since Jan 1, 1970. May be negative.\n"
" defaults to now.\n"
" 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";
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;
time_t unixtime;
if (!data)
return 0;
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
ast_get_time_t(args.timeval, &unixtime, time(NULL), NULL);
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);
return res;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(app_sayunixtime);
res |= ast_unregister_application(app_datetime);
return res;
}
static 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);
return res;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Say time");

127
trunk/apps/app_senddtmf.c Normal file
View File

@@ -0,0 +1,127 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief App to send DTMF digits
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/manager.h"
#include "asterisk/channel.h"
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";
static int senddtmf_exec(struct ast_channel *chan, void *vdata)
{
int res = 0;
char *data;
int timeout = 0, duration = 0;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(digits);
AST_APP_ARG(timeout);
AST_APP_ARG(duration);
);
if (ast_strlen_zero(vdata)) {
ast_log(LOG_WARNING, "SendDTMF requires an argument (digits or *#aAbBcCdD)\n");
return 0;
}
data = ast_strdupa(vdata);
AST_STANDARD_APP_ARGS(args, data);
if (!ast_strlen_zero(args.timeout))
timeout = atoi(args.timeout);
if (!ast_strlen_zero(args.duration))
duration = atoi(args.duration);
res = ast_dtmf_stream(chan, NULL, args.digits, timeout <= 0 ? 250 : timeout, duration);
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)
{
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 (!digit) {
astman_send_error(s, m, "No digit specified");
ast_channel_unlock(chan);
return 0;
}
ast_senddigit(chan, *digit, 0);
ast_channel_unlock(chan);
astman_send_ack(s, m, "DTMF successfully queued");
return 0;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(app);
res |= ast_manager_unregister("PlayDTMF");
return res;
}
static int load_module(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);
return res;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send DTMF digits Application");

100
trunk/apps/app_sendtext.c Normal file
View File

@@ -0,0 +1,100 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief App to transmit a text message
*
* \author Mark Spencer <markster@digium.com>
*
* \note Requires support of sending text messages from channel driver
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
static const char *app = "SendText";
static const 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";
static int sendtext_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char *status = "UNSUPPORTED";
char *parse = NULL;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(text);
AST_APP_ARG(options);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "SendText requires an argument (text[,options])\n");
return -1;
} else
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (args.options) {
}
ast_channel_lock(chan);
if (!chan->tech->send_text) {
ast_channel_unlock(chan);
/* Does not support transport */
return 0;
}
status = "FAILURE";
ast_channel_unlock(chan);
res = ast_sendtext(chan, args.text);
if (!res)
status = "SUCCESS";
pbx_builtin_setvar_helper(chan, "SENDTEXTSTATUS", status);
return 0;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, sendtext_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send Text Applications");

View File

@@ -0,0 +1,93 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief App to set callerid presentation
*
* \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/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/image.h"
#include "asterisk/callerid.h"
static char *app2 = "SetCallerPres";
static char *synopsis2 = "Set CallerID Presentation";
static char *descrip2 =
" SetCallerPres(presentation): Set Caller*ID presentation on a call.\n"
" Valid presentations are:\n"
"\n"
" allowed_not_screened : Presentation Allowed, Not Screened\n"
" allowed_passed_screen : Presentation Allowed, Passed Screen\n"
" allowed_failed_screen : Presentation Allowed, Failed Screen\n"
" allowed : Presentation Allowed, Network Number\n"
" prohib_not_screened : Presentation Prohibited, Not Screened\n"
" prohib_passed_screen : Presentation Prohibited, Passed Screen\n"
" prohib_failed_screen : Presentation Prohibited, Failed Screen\n"
" prohib : Presentation Prohibited, Network Number\n"
" unavailable : Number Unavailable\n"
"\n"
;
static int setcallerid_pres_exec(struct ast_channel *chan, void *data)
{
int pres = -1;
static int deprecated = 0;
if (!deprecated) {
deprecated = 1;
ast_log(LOG_WARNING, "SetCallerPres is deprecated. Please use Set(CALLERPRES()=%s) instead.\n", (char *)data);
}
pres = ast_parse_caller_presentation(data);
if (pres < 0) {
ast_log(LOG_WARNING, "'%s' is not a valid presentation (see 'show application SetCallerPres')\n",
(char *) data);
return 0;
}
chan->cid.cid_pres = pres;
return 0;
}
static int unload_module(void)
{
return ast_unregister_application(app2);
}
static int load_module(void)
{
return ast_register_application(app2, setcallerid_pres_exec, synopsis2, descrip2);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Set CallerID Presentation Application");

125
trunk/apps/app_skel.c Normal file
View File

@@ -0,0 +1,125 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) <Year>, <Your Name Here>
*
* <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.
*
* 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 Skeleton application
*
* \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim
*
* 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 "asterisk/file.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 *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";
enum {
OPTION_A = (1 << 0),
OPTION_B = (1 << 1),
OPTION_C = (1 << 2),
} option_flags;
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;
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)
{
int res = 0;
struct ast_flags flags;
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);
return -1;
}
/* 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>");
return res;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(app);
return res;
}
static int load_module(void)
{
if (ast_register_application(app, app_exec, synopsis, descrip))
return AST_MODULE_LOAD_DECLINE;
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Skeleton (sample) Application");

1932
trunk/apps/app_sms.c Normal file

File diff suppressed because it is too large Load Diff

120
trunk/apps/app_softhangup.c Normal file
View File

@@ -0,0 +1,120 @@
/*
* 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 SoftHangup application
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
static char *synopsis = "Soft Hangup Application";
static char *desc = " SoftHangup(Technology/resource[,options]):\n"
"Hangs up the requested channel. If there are no channels to hangup,\n"
"the application will report it.\n"
" Options:\n"
" 'a' - hang up all channels on a specified device instead of a single resource\n";
static char *app = "SoftHangup";
enum {
OPTION_ALL = (1 << 0),
};
AST_APP_OPTIONS(app_opts,{
AST_APP_OPTION('a', OPTION_ALL),
});
static int softhangup_exec(struct ast_channel *chan, void *data)
{
struct ast_channel *c = NULL;
char *cut, *opts[0];
char name[AST_CHANNEL_NAME] = "", *parse;
struct ast_flags flags;
int lenmatch;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(channel);
AST_APP_ARG(options);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "SoftHangup requires an argument (Technology/resource)\n");
return 0;
}
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (args.argc == 2)
ast_app_parse_options(app_opts, &flags, opts, args.options);
lenmatch = strlen(args.channel);
for (c = ast_walk_channel_by_name_prefix_locked(NULL, args.channel, lenmatch);
c;
c = ast_walk_channel_by_name_prefix_locked(c, args.channel, lenmatch)) {
ast_copy_string(name, c->name, sizeof(name));
if (ast_test_flag(&flags, OPTION_ALL)) {
/* CAPI is set up like CAPI[foo/bar]/clcnt */
if (!strcmp(c->tech->type, "CAPI"))
cut = strrchr(name, '/');
/* Basically everything else is Foo/Bar-Z */
else
cut = strchr(name, '-');
/* Get rid of what we've cut */
if (cut)
*cut = 0;
}
if (!strcasecmp(name, args.channel)) {
ast_log(LOG_WARNING, "Soft hanging %s up.\n", c->name);
ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
if (!ast_test_flag(&flags, OPTION_ALL)) {
ast_channel_unlock(c);
break;
}
}
ast_channel_unlock(c);
}
return 0;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, softhangup_exec, synopsis, desc);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hangs up the requested channel");

View File

@@ -0,0 +1,796 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2006, Digium, Inc.
*
* 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 Speech Recognition Utility Applications
*
* \author Joshua Colp <jcolp@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/speech.h"
/* Descriptions for each application */
static char *speechcreate_descrip =
" SpeechCreate(engine name):\n"
"This application creates information to be used by all the other applications.\n"
"It must be called before doing any speech recognition activities such as activating a grammar.\n"
"It takes the engine name to use as the argument, if not specified the default engine will be used.\n";
static char *speechactivategrammar_descrip =
" SpeechActivateGrammar(Grammar Name):\n"
"This activates the specified grammar to be recognized by the engine.\n"
"A grammar tells the speech recognition engine what to recognize, and how to portray it back to you \n"
"in the dialplan. The grammar name is the only argument to this application.\n";
static char *speechstart_descrip =
" SpeechStart():\n"
"Tell the speech recognition engine that it should start trying to get results from audio being \n"
"fed to it. This has no arguments.\n";
static char *speechbackground_descrip =
" SpeechBackground(Sound File,Timeout):\n"
"This application plays a sound file and waits for the person to speak. Once they start speaking playback\n"
"of the file stops, and silence is heard. Once they stop talking the processing sound is played to indicate\n"
"the speech recognition engine is working. Once results are available the application returns and results \n"
"(score and text) are available using dialplan functions.\n"
"The first text and score are ${SPEECH_TEXT(0)} AND ${SPEECH_SCORE(0)} while the second are ${SPEECH_TEXT(1)}\n"
"and ${SPEECH_SCORE(1)}.\n"
"The first argument is the sound file and the second is the timeout integer in seconds. Note the timeout will\n"
"only start once the sound file has stopped playing.\n";
static char *speechdeactivategrammar_descrip =
" SpeechDeactivateGrammar(Grammar Name):\n"
"This deactivates the specified grammar so that it is no longer recognized.\n"
"The only argument is the grammar name to deactivate.\n";
static char *speechprocessingsound_descrip =
" SpeechProcessingSound(Sound File):\n"
"This changes the processing sound that SpeechBackground plays back when the speech recognition engine is\n"
"processing and working to get results.\n"
"It takes the sound file as the only argument.\n";
static char *speechdestroy_descrip =
" SpeechDestroy():\n"
"This destroys the information used by all the other speech recognition applications.\n"
"If you call this application but end up wanting to recognize more speech, you must call SpeechCreate\n"
"again before calling any other application. It takes no arguments.\n";
static char *speechload_descrip =
" SpeechLoadGrammar(Grammar Name,Path):\n"
"Load a grammar only on the channel, not globally.\n"
"It takes the grammar name as first argument and path as second.\n";
static char *speechunload_descrip =
" SpeechUnloadGrammar(Grammar Name):\n"
"Unload a grammar. It takes the grammar name as the only argument.\n";
/*! \brief Helper function used by datastores to destroy the speech structure upon hangup */
static void destroy_callback(void *data)
{
struct ast_speech *speech = (struct ast_speech*)data;
if (speech == NULL) {
return;
}
/* Deallocate now */
ast_speech_destroy(speech);
return;
}
/*! \brief Static structure for datastore information */
static const struct ast_datastore_info speech_datastore = {
.type = "speech",
.destroy = destroy_callback
};
/*! \brief Helper function used to find the speech structure attached to a channel */
static struct ast_speech *find_speech(struct ast_channel *chan)
{
struct ast_speech *speech = NULL;
struct ast_datastore *datastore = NULL;
datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
if (datastore == NULL) {
return NULL;
}
speech = datastore->data;
return speech;
}
/* Helper function to find a specific speech recognition result by number and nbest alternative */
static struct ast_speech_result *find_result(struct ast_speech_result *results, char *result_num)
{
struct ast_speech_result *result = results;
char *tmp = NULL;
int nbest_num = 0, wanted_num = 0, i = 0;
if (!result)
return NULL;
if ((tmp = strchr(result_num, '/'))) {
*tmp++ = '\0';
nbest_num = atoi(result_num);
wanted_num = atoi(tmp);
} else {
wanted_num = atoi(result_num);
}
do {
if (result->nbest_num != nbest_num)
continue;
if (i == wanted_num)
break;
i++;
} while ((result = AST_LIST_NEXT(result, list)));
return result;
}
/*! \brief SPEECH_SCORE() Dialplan Function */
static int speech_score(struct ast_channel *chan, const char *cmd, char *data,
char *buf, size_t len)
{
struct ast_speech_result *result = NULL;
struct ast_speech *speech = find_speech(chan);
char tmp[128] = "";
if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
return -1;
snprintf(tmp, sizeof(tmp), "%d", result->score);
ast_copy_string(buf, tmp, len);
return 0;
}
static struct ast_custom_function speech_score_function = {
.name = "SPEECH_SCORE",
.synopsis = "Gets the confidence score of a result.",
.syntax = "SPEECH_SCORE([nbest number/]result number)",
.desc =
"Gets the confidence score of a result.\n",
.read = speech_score,
.write = NULL,
};
/*! \brief SPEECH_TEXT() Dialplan Function */
static int speech_text(struct ast_channel *chan, const char *cmd, char *data,
char *buf, size_t len)
{
struct ast_speech_result *result = NULL;
struct ast_speech *speech = find_speech(chan);
if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
return -1;
if (result->text != NULL)
ast_copy_string(buf, result->text, len);
return 0;
}
static struct ast_custom_function speech_text_function = {
.name = "SPEECH_TEXT",
.synopsis = "Gets the recognized text of a result.",
.syntax = "SPEECH_TEXT([nbest number/]result number)",
.desc =
"Gets the recognized text of a result.\n",
.read = speech_text,
.write = NULL,
};
/*! \brief SPEECH_GRAMMAR() Dialplan Function */
static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data,
char *buf, size_t len)
{
struct ast_speech_result *result = NULL;
struct ast_speech *speech = find_speech(chan);
if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
return -1;
if (result->grammar != NULL)
ast_copy_string(buf, result->grammar, len);
return 0;
}
static struct ast_custom_function speech_grammar_function = {
.name = "SPEECH_GRAMMAR",
.synopsis = "Gets the matched grammar of a result if available.",
.syntax = "SPEECH_GRAMMAR([nbest number/]result number)",
.desc =
"Gets the matched grammar of a result if available.\n",
.read = speech_grammar,
.write = NULL,
};
/*! \brief SPEECH_ENGINE() Dialplan Function */
static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
{
struct ast_speech *speech = find_speech(chan);
if (data == NULL || speech == NULL)
return -1;
ast_speech_change(speech, data, value);
return 0;
}
static struct ast_custom_function speech_engine_function = {
.name = "SPEECH_ENGINE",
.synopsis = "Change a speech engine specific attribute.",
.syntax = "SPEECH_ENGINE(name)=value",
.desc =
"Changes a speech engine specific attribute.\n",
.read = NULL,
.write = speech_engine_write,
};
/*! \brief SPEECH_RESULTS_TYPE() Dialplan Function */
static int speech_results_type_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
{
struct ast_speech *speech = find_speech(chan);
if (data == NULL || speech == NULL)
return -1;
if (!strcasecmp(value, "normal"))
ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NORMAL);
else if (!strcasecmp(value, "nbest"))
ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NBEST);
return 0;
}
static struct ast_custom_function speech_results_type_function = {
.name = "SPEECH_RESULTS_TYPE",
.synopsis = "Sets the type of results that will be returned.",
.syntax = "SPEECH_RESULTS_TYPE()=results type",
.desc =
"Sets the type of results that will be returned. Valid options are normal or nbest.",
.read = NULL,
.write = speech_results_type_write,
};
/*! \brief SPEECH() Dialplan Function */
static int speech_read(struct ast_channel *chan, const char *cmd, char *data,
char *buf, size_t len)
{
int results = 0;
struct ast_speech_result *result = NULL;
struct ast_speech *speech = find_speech(chan);
char tmp[128] = "";
/* Now go for the various options */
if (!strcasecmp(data, "status")) {
if (speech != NULL)
ast_copy_string(buf, "1", len);
else
ast_copy_string(buf, "0", len);
return 0;
}
/* Make sure we have a speech structure for everything else */
if (speech == NULL) {
return -1;
}
/* Check to see if they are checking for silence */
if (!strcasecmp(data, "spoke")) {
if (ast_test_flag(speech, AST_SPEECH_SPOKE))
ast_copy_string(buf, "1", len);
else
ast_copy_string(buf, "0", len);
} else if (!strcasecmp(data, "results")) {
/* Count number of results */
for (result = speech->results; result; result = AST_LIST_NEXT(result, list))
results++;
snprintf(tmp, sizeof(tmp), "%d", results);
ast_copy_string(buf, tmp, len);
}
return 0;
}
static struct ast_custom_function speech_function = {
.name = "SPEECH",
.synopsis = "Gets information about speech recognition results.",
.syntax = "SPEECH(argument)",
.desc =
"Gets information about speech recognition results.\n"
"status: Returns 1 upon speech object existing, or 0 if not\n"
"spoke: Returns 1 if spoker spoke, or 0 if not\n"
"results: Returns number of results that were recognized\n",
.read = speech_read,
.write = NULL,
};
/*! \brief SpeechCreate() Dialplan Application */
static int speech_create(struct ast_channel *chan, void *data)
{
struct ast_speech *speech = NULL;
struct ast_datastore *datastore = NULL;
/* Request a speech object */
speech = ast_speech_new(data, chan->nativeformats);
if (speech == NULL) {
/* Not available */
pbx_builtin_setvar_helper(chan, "ERROR", "1");
return 0;
}
datastore = ast_channel_datastore_alloc(&speech_datastore, NULL);
if (datastore == NULL) {
ast_speech_destroy(speech);
pbx_builtin_setvar_helper(chan, "ERROR", "1");
return 0;
}
datastore->data = speech;
ast_channel_datastore_add(chan, datastore);
return 0;
}
/*! \brief SpeechLoadGrammar(Grammar Name,Path) Dialplan Application */
static int speech_load(struct ast_channel *chan, void *vdata)
{
int res = 0;
struct ast_speech *speech = find_speech(chan);
char *data;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(grammar);
AST_APP_ARG(path);
);
data = ast_strdupa(vdata);
AST_STANDARD_APP_ARGS(args, data);
if (speech == NULL)
return -1;
if (args.argc != 2)
return -1;
/* Load the grammar locally on the object */
res = ast_speech_grammar_load(speech, args.grammar, args.path);
return res;
}
/*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
static int speech_unload(struct ast_channel *chan, void *data)
{
int res = 0;
struct ast_speech *speech = find_speech(chan);
if (speech == NULL)
return -1;
/* Unload the grammar */
res = ast_speech_grammar_unload(speech, data);
return res;
}
/*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
static int speech_deactivate(struct ast_channel *chan, void *data)
{
int res = 0;
struct ast_speech *speech = find_speech(chan);
if (speech == NULL)
return -1;
/* Deactivate the grammar on the speech object */
res = ast_speech_grammar_deactivate(speech, data);
return res;
}
/*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
static int speech_activate(struct ast_channel *chan, void *data)
{
int res = 0;
struct ast_speech *speech = find_speech(chan);
if (speech == NULL)
return -1;
/* Activate the grammar on the speech object */
res = ast_speech_grammar_activate(speech, data);
return res;
}
/*! \brief SpeechStart() Dialplan Application */
static int speech_start(struct ast_channel *chan, void *data)
{
int res = 0;
struct ast_speech *speech = find_speech(chan);
if (speech == NULL)
return -1;
ast_speech_start(speech);
return res;
}
/*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
static int speech_processing_sound(struct ast_channel *chan, void *data)
{
int res = 0;
struct ast_speech *speech = find_speech(chan);
if (speech == NULL)
return -1;
if (speech->processing_sound != NULL) {
ast_free(speech->processing_sound);
speech->processing_sound = NULL;
}
speech->processing_sound = ast_strdup(data);
return res;
}
/*! \brief Helper function used by speech_background to playback a soundfile */
static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
{
struct ast_filestream *fs = NULL;
if (!(fs = ast_openstream(chan, filename, preflang)))
return -1;
if (ast_applystream(chan, fs))
return -1;
ast_playstream(fs);
return 0;
}
/*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
static int speech_background(struct ast_channel *chan, void *data)
{
unsigned int timeout = 0;
int res = 0, done = 0, started = 0, quieted = 0, max_dtmf_len = 0;
struct ast_speech *speech = find_speech(chan);
struct ast_frame *f = NULL;
int oldreadformat = AST_FORMAT_SLINEAR;
char dtmf[AST_MAX_EXTENSION] = "";
time_t start, current;
struct ast_datastore *datastore = NULL;
char *parse, *filename_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#';
const char *tmp2 = NULL;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(soundfile);
AST_APP_ARG(timeout);
);
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (speech == NULL)
return -1;
/* If channel is not already answered, then answer it */
if (chan->_state != AST_STATE_UP && ast_answer(chan))
return -1;
/* Record old read format */
oldreadformat = chan->readformat;
/* Change read format to be signed linear */
if (ast_set_read_format(chan, speech->format))
return -1;
if (!ast_strlen_zero(args.soundfile)) {
/* Yay sound file */
filename_tmp = ast_strdupa(args.soundfile);
if (!ast_strlen_zero(args.timeout)) {
if ((timeout = atoi(args.timeout)) == 0)
timeout = -1;
} else
timeout = 0;
}
/* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */
if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2))
max_dtmf_len = atoi(tmp2);
/* See if a terminator is specified */
if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) {
if (ast_strlen_zero(tmp2))
dtmf_terminator = '\0';
else
dtmf_terminator = tmp2[0];
}
/* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
ast_speech_start(speech);
}
/* Ensure no streams are currently running */
ast_stopstream(chan);
/* Okay it's streaming so go into a loop grabbing frames! */
while (done == 0) {
/* If the filename is null and stream is not running, start up a new sound file */
if (!quieted && (chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) {
/* Discard old stream information */
ast_stopstream(chan);
/* Start new stream */
speech_streamfile(chan, filename, chan->language);
}
/* Run scheduled stuff */
ast_sched_runq(chan->sched);
/* Yay scheduling */
res = ast_sched_wait(chan->sched);
if (res < 0)
res = 1000;
/* If there is a frame waiting, get it - if not - oh well */
if (ast_waitfor(chan, res) > 0) {
f = ast_read(chan);
if (f == NULL) {
/* The channel has hung up most likely */
done = 3;
break;
}
}
/* Do timeout check (shared between audio/dtmf) */
if ((!quieted || strlen(dtmf)) && started == 1) {
time(&current);
if ((current-start) >= timeout) {
done = 1;
if (f)
ast_frfree(f);
break;
}
}
/* Do checks on speech structure to see if it's changed */
ast_mutex_lock(&speech->lock);
if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
if (chan->stream)
ast_stopstream(chan);
ast_clear_flag(speech, AST_SPEECH_QUIET);
quieted = 1;
}
/* Check state so we can see what to do */
switch (speech->state) {
case AST_SPEECH_STATE_READY:
/* If audio playback has stopped do a check for timeout purposes */
if (chan->streamid == -1 && chan->timingfunc == NULL)
ast_stopstream(chan);
if (!quieted && chan->stream == NULL && timeout && started == 0 && !filename_tmp) {
if (timeout == -1) {
done = 1;
if (f)
ast_frfree(f);
break;
}
time(&start);
started = 1;
}
/* Write audio frame out to speech engine if no DTMF has been received */
if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
ast_speech_write(speech, f->data, f->datalen);
}
break;
case AST_SPEECH_STATE_WAIT:
/* Cue up waiting sound if not already playing */
if (!strlen(dtmf)) {
if (chan->stream == NULL) {
if (speech->processing_sound != NULL) {
if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
speech_streamfile(chan, speech->processing_sound, chan->language);
}
}
} else if (chan->streamid == -1 && chan->timingfunc == NULL) {
ast_stopstream(chan);
if (speech->processing_sound != NULL) {
if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
speech_streamfile(chan, speech->processing_sound, chan->language);
}
}
}
}
break;
case AST_SPEECH_STATE_DONE:
/* Now that we are done... let's switch back to not ready state */
ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
if (!strlen(dtmf)) {
/* Copy to speech structure the results, if available */
speech->results = ast_speech_results_get(speech);
/* Break out of our background too */
done = 1;
/* Stop audio playback */
if (chan->stream != NULL) {
ast_stopstream(chan);
}
}
break;
default:
break;
}
ast_mutex_unlock(&speech->lock);
/* Deal with other frame types */
if (f != NULL) {
/* Free the frame we received */
switch (f->frametype) {
case AST_FRAME_DTMF:
if (dtmf_terminator != '\0' && f->subclass == dtmf_terminator) {
done = 1;
} else {
if (chan->stream != NULL) {
ast_stopstream(chan);
}
if (!started) {
/* Change timeout to be 5 seconds for DTMF input */
timeout = (chan->pbx && chan->pbx->dtimeout) ? chan->pbx->dtimeout : 5;
started = 1;
}
time(&start);
snprintf(tmp, sizeof(tmp), "%c", f->subclass);
strncat(dtmf, tmp, sizeof(dtmf));
/* If the maximum length of the DTMF has been reached, stop now */
if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
done = 1;
}
break;
case AST_FRAME_CONTROL:
switch (f->subclass) {
case AST_CONTROL_HANGUP:
/* Since they hung up we should destroy the speech structure */
done = 3;
default:
break;
}
default:
break;
}
ast_frfree(f);
f = NULL;
}
}
if (!ast_strlen_zero(dtmf)) {
/* We sort of make a results entry */
speech->results = ast_calloc(1, sizeof(*speech->results));
if (speech->results != NULL) {
ast_speech_dtmf(speech, dtmf);
speech->results->score = 1000;
speech->results->text = ast_strdup(dtmf);
speech->results->grammar = ast_strdup("dtmf");
}
}
/* See if it was because they hung up */
if (done == 3) {
/* Destroy speech structure */
ast_speech_destroy(speech);
datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
if (datastore != NULL)
ast_channel_datastore_remove(chan, datastore);
} else {
/* Channel is okay so restore read format */
ast_set_read_format(chan, oldreadformat);
}
return 0;
}
/*! \brief SpeechDestroy() Dialplan Application */
static int speech_destroy(struct ast_channel *chan, void *data)
{
int res = 0;
struct ast_speech *speech = find_speech(chan);
struct ast_datastore *datastore = NULL;
if (speech == NULL)
return -1;
/* Destroy speech structure */
ast_speech_destroy(speech);
datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
if (datastore != NULL) {
ast_channel_datastore_remove(chan, datastore);
}
return res;
}
static int unload_module(void)
{
int res = 0;
res = ast_unregister_application("SpeechCreate");
res |= ast_unregister_application("SpeechLoadGrammar");
res |= ast_unregister_application("SpeechUnloadGrammar");
res |= ast_unregister_application("SpeechActivateGrammar");
res |= ast_unregister_application("SpeechDeactivateGrammar");
res |= ast_unregister_application("SpeechStart");
res |= ast_unregister_application("SpeechBackground");
res |= ast_unregister_application("SpeechDestroy");
res |= ast_unregister_application("SpeechProcessingSound");
res |= ast_custom_function_unregister(&speech_function);
res |= ast_custom_function_unregister(&speech_score_function);
res |= ast_custom_function_unregister(&speech_text_function);
res |= ast_custom_function_unregister(&speech_grammar_function);
res |= ast_custom_function_unregister(&speech_engine_function);
res |= ast_custom_function_unregister(&speech_results_type_function);
return res;
}
static int load_module(void)
{
int res = 0;
res = ast_register_application("SpeechCreate", speech_create, "Create a Speech Structure", speechcreate_descrip);
res |= ast_register_application("SpeechLoadGrammar", speech_load, "Load a Grammar", speechload_descrip);
res |= ast_register_application("SpeechUnloadGrammar", speech_unload, "Unload a Grammar", speechunload_descrip);
res |= ast_register_application("SpeechActivateGrammar", speech_activate, "Activate a Grammar", speechactivategrammar_descrip);
res |= ast_register_application("SpeechDeactivateGrammar", speech_deactivate, "Deactivate a Grammar", speechdeactivategrammar_descrip);
res |= ast_register_application("SpeechStart", speech_start, "Start recognizing voice in the audio stream", speechstart_descrip);
res |= ast_register_application("SpeechBackground", speech_background, "Play a sound file and wait for speech to be recognized", speechbackground_descrip);
res |= ast_register_application("SpeechDestroy", speech_destroy, "End speech recognition", speechdestroy_descrip);
res |= ast_register_application("SpeechProcessingSound", speech_processing_sound, "Change background processing sound", speechprocessingsound_descrip);
res |= ast_custom_function_register(&speech_function);
res |= ast_custom_function_register(&speech_score_function);
res |= ast_custom_function_register(&speech_text_function);
res |= ast_custom_function_register(&speech_grammar_function);
res |= ast_custom_function_register(&speech_engine_function);
res |= ast_custom_function_register(&speech_results_type_function);
return res;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan Speech Applications");

416
trunk/apps/app_stack.c Normal file
View File

@@ -0,0 +1,416 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (c) 2004-2006 Tilghman Lesher <app_stack_v003@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.
*
* 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 Stack applications Gosub, Return, etc.
*
* \author Tilghman Lesher <app_stack_v003@the-tilghman.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/manager.h"
#include "asterisk/channel.h"
static const char *app_gosub = "Gosub";
static const char *app_gosubif = "GosubIf";
static const char *app_return = "Return";
static const char *app_pop = "StackPop";
static const char *gosub_synopsis = "Jump to label, saving return address";
static const char *gosubif_synopsis = "Conditionally jump to label, saving return address";
static const char *return_synopsis = "Return from gosub routine";
static const char *pop_synopsis = "Remove one address from gosub stack";
static const char *gosub_descrip =
" Gosub([[context,]exten,]priority[(arg1[,...][,argN])]):\n"
"Jumps to the label specified, saving the return address.\n";
static const char *gosubif_descrip =
" GosubIf(condition?labeliftrue[(arg1[,...])][:labeliffalse[(arg1[,...])]]):\n"
"If the condition is true, then jump to labeliftrue. If false, jumps to\n"
"labeliffalse, if specified. In either case, a jump saves the return point\n"
"in the dialplan, to be returned to with a Return.\n";
static const char *return_descrip =
" Return([return-value]):\n"
"Jumps to the last label on the stack, removing it. The return value, if\n"
"any, is saved in the channel variable GOSUB_RETVAL.\n";
static const char *pop_descrip =
" StackPop():\n"
"Removes last label on the stack, discarding it.\n";
static void gosub_free(void *data);
static struct ast_datastore_info stack_info = {
.type = "GOSUB",
.destroy = gosub_free,
};
struct gosub_stack_frame {
AST_LIST_ENTRY(gosub_stack_frame) entries;
/* 100 arguments is all that we support anyway, but this will handle up to 255 */
unsigned char arguments;
struct varshead varshead;
int priority;
char *context;
char extension[0];
};
static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
{
struct ast_var_t *variables;
int found = 0;
/* Does this variable already exist? */
AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
if (!strcmp(var, ast_var_name(variables))) {
found = 1;
break;
}
}
if (!ast_strlen_zero(value)) {
if (!found) {
variables = ast_var_assign(var, "");
AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
pbx_builtin_pushvar_helper(chan, var, value);
} else
pbx_builtin_setvar_helper(chan, var, value);
manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
"Channel: %s\r\n"
"Variable: LOCAL(%s)\r\n"
"Value: %s\r\n"
"Uniqueid: %s\r\n",
chan->name, var, value, chan->uniqueid);
}
return 0;
}
static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
{
unsigned char i;
char argname[15];
struct ast_var_t *vardata;
/* If chan is not defined, then we're calling it as part of gosub_free,
* and the channel variables will be deallocated anyway. Otherwise, we're
* just releasing a single frame, so we need to clean up the arguments for
* that frame, so that we re-expose the variables from the previous frame
* that were hidden by this one.
*/
if (chan) {
for (i = 1; i <= frame->arguments && i != 0; i++) {
snprintf(argname, sizeof(argname), "ARG%hhd", i);
pbx_builtin_setvar_helper(chan, argname, NULL);
}
}
/* Delete local variables */
while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
if (chan)
pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
ast_var_delete(vardata);
}
ast_free(frame);
}
static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
{
struct gosub_stack_frame *new = NULL;
int len_extension = strlen(extension), len_context = strlen(context);
if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
strcpy(new->extension, extension);
new->context = new->extension + len_extension + 1;
strcpy(new->context, context);
new->priority = priority;
new->arguments = arguments;
}
return new;
}
static void gosub_free(void *data)
{
AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
struct gosub_stack_frame *oldframe;
AST_LIST_LOCK(oldlist);
while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
gosub_release_frame(NULL, oldframe);
}
AST_LIST_UNLOCK(oldlist);
AST_LIST_HEAD_DESTROY(oldlist);
ast_free(oldlist);
}
static int pop_exec(struct ast_channel *chan, void *data)
{
struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
struct gosub_stack_frame *oldframe;
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
if (!stack_store) {
ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
return 0;
}
oldlist = stack_store->data;
AST_LIST_LOCK(oldlist);
oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
AST_LIST_UNLOCK(oldlist);
if (oldframe) {
gosub_release_frame(chan, oldframe);
} else {
ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
}
return 0;
}
static int return_exec(struct ast_channel *chan, void *data)
{
struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
struct gosub_stack_frame *oldframe;
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
char *retval = data;
if (!stack_store) {
ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
return -1;
}
oldlist = stack_store->data;
AST_LIST_LOCK(oldlist);
oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
AST_LIST_UNLOCK(oldlist);
if (!oldframe) {
ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
return -1;
}
ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
gosub_release_frame(chan, oldframe);
/* Set a return value, if any */
pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
return 0;
}
static int gosub_exec(struct ast_channel *chan, void *data)
{
struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
struct gosub_stack_frame *newframe;
char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
int i;
AST_DECLARE_APP_ARGS(args2,
AST_APP_ARG(argval)[100];
);
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "%s requires an argument: %s([[context|]exten|]priority[(arg1[|...][|argN])])\n", app_gosub, app_gosub);
return -1;
}
if (!stack_store) {
ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", chan->name);
stack_store = ast_channel_datastore_alloc(&stack_info, NULL);
if (!stack_store) {
ast_log(LOG_ERROR, "Unable to allocate new datastore. Gosub will fail.\n");
return -1;
}
oldlist = ast_calloc(1, sizeof(*oldlist));
if (!oldlist) {
ast_log(LOG_ERROR, "Unable to allocate datastore list head. Gosub will fail.\n");
ast_channel_datastore_free(stack_store);
return -1;
}
stack_store->data = oldlist;
AST_LIST_HEAD_INIT(oldlist);
ast_channel_datastore_add(chan, stack_store);
}
/* Separate the arguments from the label */
/* NOTE: you cannot use ast_app_separate_args for this, because '(' cannot be used as a delimiter. */
label = strsep(&tmp, "(");
if (tmp) {
endparen = strrchr(tmp, ')');
if (endparen)
*endparen = '\0';
else
ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", (char *)data);
AST_STANDARD_APP_ARGS(args2, tmp);
} else
args2.argc = 0;
/* Create the return address, but don't save it until we know that the Gosub destination exists */
newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, args2.argc);
if (!newframe)
return -1;
if (ast_parseable_goto(chan, label)) {
ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
ast_free(newframe);
return -1;
}
/* Now that we know for certain that we're going to a new location, set our arguments */
for (i = 0; i < args2.argc; i++) {
snprintf(argname, sizeof(argname), "ARG%d", i + 1);
frame_set_var(chan, newframe, argname, args2.argval[i]);
ast_debug(1, "Setting '%s' to '%s'\n", argname, args2.argval[i]);
}
/* And finally, save our return address */
oldlist = stack_store->data;
AST_LIST_LOCK(oldlist);
AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
AST_LIST_UNLOCK(oldlist);
return 0;
}
static int gosubif_exec(struct ast_channel *chan, void *data)
{
char *args;
int res=0;
AST_DECLARE_APP_ARGS(cond,
AST_APP_ARG(ition);
AST_APP_ARG(labels);
);
AST_DECLARE_APP_ARGS(label,
AST_APP_ARG(iftrue);
AST_APP_ARG(iffalse);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
return 0;
}
args = ast_strdupa(data);
AST_NONSTANDARD_APP_ARGS(cond, args, '?');
if (cond.argc != 2) {
ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
return 0;
}
AST_NONSTANDARD_APP_ARGS(label, cond.labels, ':');
if (pbx_checkcondition(cond.ition)) {
if (!ast_strlen_zero(label.iftrue))
res = gosub_exec(chan, label.iftrue);
} else if (!ast_strlen_zero(label.iffalse)) {
res = gosub_exec(chan, label.iffalse);
}
return res;
}
static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
struct gosub_stack_frame *frame;
struct ast_var_t *variables;
if (!stack_store)
return -1;
oldlist = stack_store->data;
AST_LIST_LOCK(oldlist);
frame = AST_LIST_FIRST(oldlist);
AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
if (!strcmp(data, ast_var_name(variables))) {
const char *tmp = pbx_builtin_getvar_helper(chan, data);
ast_copy_string(buf, S_OR(tmp, ""), len);
break;
}
}
AST_LIST_UNLOCK(oldlist);
return 0;
}
static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
{
struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
struct gosub_stack_frame *frame;
if (!stack_store) {
ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
return -1;
}
oldlist = stack_store->data;
AST_LIST_LOCK(oldlist);
frame = AST_LIST_FIRST(oldlist);
if (frame)
frame_set_var(chan, frame, var, value);
AST_LIST_UNLOCK(oldlist);
return 0;
}
static struct ast_custom_function local_function = {
.name = "LOCAL",
.synopsis = "Variables local to the gosub stack frame",
.syntax = "LOCAL(<varname>)",
.write = local_write,
.read = local_read,
};
static int unload_module(void)
{
ast_unregister_application(app_return);
ast_unregister_application(app_pop);
ast_unregister_application(app_gosubif);
ast_unregister_application(app_gosub);
ast_custom_function_unregister(&local_function);
return 0;
}
static int load_module(void)
{
ast_register_application(app_pop, pop_exec, pop_synopsis, pop_descrip);
ast_register_application(app_return, return_exec, return_synopsis, return_descrip);
ast_register_application(app_gosubif, gosubif_exec, gosubif_synopsis, gosubif_descrip);
ast_register_application(app_gosub, gosub_exec, gosub_synopsis, gosub_descrip);
ast_custom_function_register(&local_function);
return 0;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan subroutines (Gosub, Return, etc)");

129
trunk/apps/app_system.c Normal file
View File

@@ -0,0 +1,129 @@
/*
* 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 arbitrary system commands
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/channel.h" /* autoservice */
static char *app = "System";
static char *app2 = "TrySystem";
static char *synopsis = "Execute a system command";
static char *synopsis2 = "Try executing a system command";
static char *chanvar = "SYSTEMSTATUS";
static char *descrip =
" System(command): Executes a command by using system(). If the command\n"
"fails, the console should report a fallthrough. \n"
"Result of execution is returned in the SYSTEMSTATUS channel variable:\n"
" FAILURE Could not execute the specified command\n"
" SUCCESS Specified command successfully executed\n";
static char *descrip2 =
" TrySystem(command): Executes a command by using system().\n"
"on any situation.\n"
"Result of execution is returned in the SYSTEMSTATUS channel variable:\n"
" FAILURE Could not execute the specified command\n"
" SUCCESS Specified command successfully executed\n"
" APPERROR Specified command successfully executed, but returned error code\n";
static int system_exec_helper(struct ast_channel *chan, void *data, int failmode)
{
int res = 0;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "System requires an argument(command)\n");
pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
return failmode;
}
ast_autoservice_start(chan);
/* Do our thing here */
res = ast_safe_system((char *)data);
if ((res < 0) && (errno != ECHILD)) {
ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);
pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
res = failmode;
} else if (res == 127) {
ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);
pbx_builtin_setvar_helper(chan, chanvar, "FAILURE");
res = failmode;
} else {
if (res < 0)
res = 0;
if (res != 0)
pbx_builtin_setvar_helper(chan, chanvar, "APPERROR");
else
pbx_builtin_setvar_helper(chan, chanvar, "SUCCESS");
res = 0;
}
ast_autoservice_stop(chan);
return res;
}
static int system_exec(struct ast_channel *chan, void *data)
{
return system_exec_helper(chan, data, -1);
}
static int trysystem_exec(struct ast_channel *chan, void *data)
{
return system_exec_helper(chan, data, 0);
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(app);
res |= ast_unregister_application(app2);
return res;
}
static int load_module(void)
{
int res;
res = ast_register_application(app2, trysystem_exec, synopsis2, descrip2);
res |= ast_register_application(app, system_exec, synopsis, descrip);
return res;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Generic System() application");

211
trunk/apps/app_talkdetect.c Normal file
View File

@@ -0,0 +1,211 @@
/*
* 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 Playback a file with audio detect
*
* \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/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/utils.h"
#include "asterisk/dsp.h"
#include "asterisk/app.h"
static char *app = "BackgroundDetect";
static char *synopsis = "Background a file with talk detect";
static char *descrip =
" BackgroundDetect(filename[,sil[,min,[max]]]): Plays back a given\n"
"filename, waiting for interruption from a given digit (the digit must\n"
"start the beginning of a valid extension, or it will be ignored).\n"
"During the playback of the file, audio is monitored in the receive\n"
"direction, and if a period of non-silence which is greater than 'min' ms\n"
"yet less than 'max' ms is followed by silence for at least 'sil' ms then\n"
"the audio playback is aborted and processing jumps to the 'talk' extension\n"
"if available. If unspecified, sil, min, and max default to 1000, 100, and\n"
"infinity respectively.\n";
static int background_detect_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char *tmp;
struct ast_frame *fr;
int notsilent = 0;
struct timeval start = { 0, 0};
int sil = 1000;
int min = 100;
int max = -1;
int x;
int origrformat=0;
struct ast_dsp *dsp = NULL;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(filename);
AST_APP_ARG(silence);
AST_APP_ARG(min);
AST_APP_ARG(max);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "BackgroundDetect requires an argument (filename)\n");
return -1;
}
tmp = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, tmp);
if ((sscanf(args.silence, "%d", &x) == 1) && (x > 0))
sil = x;
if ((sscanf(args.min, "%d", &x) == 1) && (x > 0))
min = x;
if ((sscanf(args.max, "%d", &x) == 1) && (x > 0))
max = x;
ast_debug(1, "Preparing detect of '%s', sil=%d, min=%d, max=%d\n", args.filename, sil, min, max);
do {
if (chan->_state != AST_STATE_UP) {
if ((res = ast_answer(chan)))
break;
}
origrformat = chan->readformat;
if ((ast_set_read_format(chan, AST_FORMAT_SLINEAR))) {
ast_log(LOG_WARNING, "Unable to set read format to linear!\n");
res = -1;
break;
}
if (!(dsp = ast_dsp_new())) {
ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
res = -1;
break;
}
ast_stopstream(chan);
if (ast_streamfile(chan, tmp, chan->language)) {
ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
break;
}
while (chan->stream) {
res = ast_sched_wait(chan->sched);
if ((res < 0) && !chan->timingfunc) {
res = 0;
break;
}
if (res < 0)
res = 1000;
res = ast_waitfor(chan, res);
if (res < 0) {
ast_log(LOG_WARNING, "Waitfor failed on %s\n", chan->name);
break;
} else if (res > 0) {
fr = ast_read(chan);
if (!fr) {
res = -1;
break;
} else if (fr->frametype == AST_FRAME_DTMF) {
char t[2];
t[0] = fr->subclass;
t[1] = '\0';
if (ast_canmatch_extension(chan, chan->context, t, 1, chan->cid.cid_num)) {
/* They entered a valid extension, or might be anyhow */
res = fr->subclass;
ast_frfree(fr);
break;
}
} else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass == AST_FORMAT_SLINEAR)) {
int totalsilence;
int ms;
res = ast_dsp_silence(dsp, fr, &totalsilence);
if (res && (totalsilence > sil)) {
/* We've been quiet a little while */
if (notsilent) {
/* We had heard some talking */
ms = ast_tvdiff_ms(ast_tvnow(), start);
ms -= sil;
if (ms < 0)
ms = 0;
if ((ms > min) && ((max < 0) || (ms < max))) {
char ms_str[10];
ast_debug(1, "Found qualified token of %d ms\n", ms);
/* Save detected talk time (in milliseconds) */
sprintf(ms_str, "%d", ms );
pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str);
ast_goto_if_exists(chan, chan->context, "talk", 1);
res = 0;
ast_frfree(fr);
break;
} else {
ast_debug(1, "Found unqualified token of %d ms\n", ms);
}
notsilent = 0;
}
} else {
if (!notsilent) {
/* Heard some audio, mark the begining of the token */
start = ast_tvnow();
ast_debug(1, "Start of voice token!\n");
notsilent = 1;
}
}
}
ast_frfree(fr);
}
ast_sched_runq(chan->sched);
}
ast_stopstream(chan);
} while (0);
if (res > -1) {
if (origrformat && ast_set_read_format(chan, origrformat)) {
ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n",
chan->name, ast_getformatname(origrformat));
}
}
if (dsp)
ast_dsp_free(dsp);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, background_detect_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Playback with Talk Detection");

467
trunk/apps/app_test.c Normal file
View File

@@ -0,0 +1,467 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
* Russell Bryant <russelb@clemson.edu>
*
* 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 Applications to test connection and produce report in text file
*
* \author Mark Spencer <markster@digium.com>
* \author Russell Bryant <russelb@clemson.edu>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/stat.h>
#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
#include "asterisk/channel.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/pbx.h"
#include "asterisk/utils.h"
static char *tests_descrip =
" TestServer(): Perform test server function and write call report.\n"
"Results stored in /var/log/asterisk/testreports/<testid>-server.txt";
static char *tests_app = "TestServer";
static char *tests_synopsis = "Execute Interface Test Server";
static char *testc_descrip =
" TestClient(testid): Executes test client with given testid.\n"
"Results stored in /var/log/asterisk/testreports/<testid>-client.txt";
static char *testc_app = "TestClient";
static char *testc_synopsis = "Execute Interface Test Client";
static int measurenoise(struct ast_channel *chan, int ms, char *who)
{
int res=0;
int mssofar;
int noise=0;
int samples=0;
int x;
short *foo;
struct timeval start;
struct ast_frame *f;
int rformat;
rformat = chan->readformat;
if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
ast_log(LOG_NOTICE, "Unable to set to linear mode!\n");
return -1;
}
start = ast_tvnow();
for(;;) {
mssofar = ast_tvdiff_ms(ast_tvnow(), start);
if (mssofar > ms)
break;
res = ast_waitfor(chan, ms - mssofar);
if (res < 1)
break;
f = ast_read(chan);
if (!f) {
res = -1;
break;
}
if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
foo = (short *)f->data;
for (x=0;x<f->samples;x++) {
noise += abs(foo[x]);
samples++;
}
}
ast_frfree(f);
}
if (rformat) {
if (ast_set_read_format(chan, rformat)) {
ast_log(LOG_NOTICE, "Unable to restore original format!\n");
return -1;
}
}
if (res < 0)
return res;
if (!samples) {
ast_log(LOG_NOTICE, "No samples were received from the other side!\n");
return -1;
}
ast_debug(1, "%s: Noise: %d, samples: %d, avg: %d\n", who, noise, samples, noise / samples);
return (noise / samples);
}
static int sendnoise(struct ast_channel *chan, int ms)
{
int res;
res = ast_tonepair_start(chan, 1537, 2195, ms, 8192);
if (!res) {
res = ast_waitfordigit(chan, ms);
ast_tonepair_stop(chan);
}
return res;
}
static int testclient_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char *testid=data;
char fn[80];
char serverver[80];
FILE *f;
/* Check for test id */
if (ast_strlen_zero(testid)) {
ast_log(LOG_WARNING, "TestClient requires an argument - the test id\n");
return -1;
}
if (chan->_state != AST_STATE_UP)
res = ast_answer(chan);
/* Wait a few just to be sure things get started */
res = ast_safe_sleep(chan, 3000);
/* Transmit client version */
if (!res)
res = ast_dtmf_stream(chan, NULL, "8378*1#", 0, 0);
ast_debug(1, "Transmit client version\n");
/* Read server version */
ast_debug(1, "Read server version\n");
if (!res)
res = ast_app_getdata(chan, NULL, serverver, sizeof(serverver) - 1, 0);
if (res > 0)
res = 0;
ast_debug(1, "server version: %s\n", serverver);
if (res > 0)
res = 0;
if (!res)
res = ast_safe_sleep(chan, 1000);
/* Send test id */
if (!res)
res = ast_dtmf_stream(chan, NULL, testid, 0, 0);
if (!res)
res = ast_dtmf_stream(chan, NULL, "#", 0, 0);
ast_debug(1, "send test identifier: %s\n", testid);
if ((res >=0) && (!ast_strlen_zero(testid))) {
/* Make the directory to hold the test results in case it's not there */
snprintf(fn, sizeof(fn), "%s/testresults", ast_config_AST_LOG_DIR);
ast_mkdir(fn, 0777);
snprintf(fn, sizeof(fn), "%s/testresults/%s-client.txt", ast_config_AST_LOG_DIR, testid);
if ((f = fopen(fn, "w+"))) {
setlinebuf(f);
fprintf(f, "CLIENTCHAN: %s\n", chan->name);
fprintf(f, "CLIENTTEST ID: %s\n", testid);
fprintf(f, "ANSWER: PASS\n");
res = 0;
if (!res) {
/* Step 1: Wait for "1" */
ast_debug(1, "TestClient: 2. Wait DTMF 1\n");
res = ast_waitfordigit(chan, 3000);
fprintf(f, "WAIT DTMF 1: %s\n", (res != '1') ? "FAIL" : "PASS");
if (res == '1')
res = 0;
else
res = -1;
}
if (!res)
res = ast_safe_sleep(chan, 1000);
if (!res) {
/* Step 2: Send "2" */
ast_debug(1, "TestClient: 2. Send DTMF 2\n");
res = ast_dtmf_stream(chan, NULL, "2", 0, 0);
fprintf(f, "SEND DTMF 2: %s\n", (res < 0) ? "FAIL" : "PASS");
if (res > 0)
res = 0;
}
if (!res) {
/* Step 3: Wait one second */
ast_debug(1, "TestClient: 3. Wait one second\n");
res = ast_safe_sleep(chan, 1000);
fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
if (res > 0)
res = 0;
}
if (!res) {
/* Step 4: Measure noise */
ast_debug(1, "TestClient: 4. Measure noise\n");
res = measurenoise(chan, 5000, "TestClient");
fprintf(f, "MEASURENOISE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
if (res > 0)
res = 0;
}
if (!res) {
/* Step 5: Wait for "4" */
ast_debug(1, "TestClient: 5. Wait DTMF 4\n");
res = ast_waitfordigit(chan, 3000);
fprintf(f, "WAIT DTMF 4: %s\n", (res != '4') ? "FAIL" : "PASS");
if (res == '4')
res = 0;
else
res = -1;
}
if (!res) {
/* Step 6: Transmit tone noise */
ast_debug(1, "TestClient: 6. Transmit tone\n");
res = sendnoise(chan, 6000);
fprintf(f, "SENDTONE: %s\n", (res < 0) ? "FAIL" : "PASS");
}
if (!res || (res == '5')) {
/* Step 7: Wait for "5" */
ast_debug(1, "TestClient: 7. Wait DTMF 5\n");
if (!res)
res = ast_waitfordigit(chan, 3000);
fprintf(f, "WAIT DTMF 5: %s\n", (res != '5') ? "FAIL" : "PASS");
if (res == '5')
res = 0;
else
res = -1;
}
if (!res) {
/* Step 8: Wait one second */
ast_debug(1, "TestClient: 8. Wait one second\n");
res = ast_safe_sleep(chan, 1000);
fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
if (res > 0)
res = 0;
}
if (!res) {
/* Step 9: Measure noise */
ast_debug(1, "TestClient: 6. Measure tone\n");
res = measurenoise(chan, 4000, "TestClient");
fprintf(f, "MEASURETONE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
if (res > 0)
res = 0;
}
if (!res) {
/* Step 10: Send "7" */
ast_debug(1, "TestClient: 7. Send DTMF 7\n");
res = ast_dtmf_stream(chan, NULL, "7", 0, 0);
fprintf(f, "SEND DTMF 7: %s\n", (res < 0) ? "FAIL" : "PASS");
if (res > 0)
res =0;
}
if (!res) {
/* Step 11: Wait for "8" */
ast_debug(1, "TestClient: 11. Wait DTMF 8\n");
res = ast_waitfordigit(chan, 3000);
fprintf(f, "WAIT DTMF 8: %s\n", (res != '8') ? "FAIL" : "PASS");
if (res == '8')
res = 0;
else
res = -1;
}
if (!res) {
/* Step 12: Hangup! */
ast_debug(1, "TestClient: 12. Hangup\n");
}
ast_debug(1, "-- TEST COMPLETE--\n");
fprintf(f, "-- END TEST--\n");
fclose(f);
res = -1;
} else
res = -1;
} else {
ast_log(LOG_NOTICE, "Did not read a test ID on '%s'\n", chan->name);
res = -1;
}
return res;
}
static int testserver_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char testid[80]="";
char fn[80];
FILE *f;
if (chan->_state != AST_STATE_UP)
res = ast_answer(chan);
/* Read version */
ast_debug(1, "Read client version\n");
if (!res)
res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0);
if (res > 0)
res = 0;
ast_debug(1, "client version: %s\n", testid);
ast_debug(1, "Transmit server version\n");
res = ast_safe_sleep(chan, 1000);
if (!res)
res = ast_dtmf_stream(chan, NULL, "8378*1#", 0, 0);
if (res > 0)
res = 0;
if (!res)
res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0);
ast_debug(1, "read test identifier: %s\n", testid);
/* Check for sneakyness */
if (strchr(testid, '/'))
res = -1;
if ((res >=0) && (!ast_strlen_zero(testid))) {
/* Got a Test ID! Whoo hoo! */
/* Make the directory to hold the test results in case it's not there */
snprintf(fn, sizeof(fn), "%s/testresults", ast_config_AST_LOG_DIR);
ast_mkdir(fn, 0777);
snprintf(fn, sizeof(fn), "%s/testresults/%s-server.txt", ast_config_AST_LOG_DIR, testid);
if ((f = fopen(fn, "w+"))) {
setlinebuf(f);
fprintf(f, "SERVERCHAN: %s\n", chan->name);
fprintf(f, "SERVERTEST ID: %s\n", testid);
fprintf(f, "ANSWER: PASS\n");
ast_debug(1, "Processing Test ID '%s'\n", testid);
res = ast_safe_sleep(chan, 1000);
if (!res) {
/* Step 1: Send "1" */
ast_debug(1, "TestServer: 1. Send DTMF 1\n");
res = ast_dtmf_stream(chan, NULL, "1", 0,0 );
fprintf(f, "SEND DTMF 1: %s\n", (res < 0) ? "FAIL" : "PASS");
if (res > 0)
res = 0;
}
if (!res) {
/* Step 2: Wait for "2" */
ast_debug(1, "TestServer: 2. Wait DTMF 2\n");
res = ast_waitfordigit(chan, 3000);
fprintf(f, "WAIT DTMF 2: %s\n", (res != '2') ? "FAIL" : "PASS");
if (res == '2')
res = 0;
else
res = -1;
}
if (!res) {
/* Step 3: Measure noise */
ast_debug(1, "TestServer: 3. Measure noise\n");
res = measurenoise(chan, 6000, "TestServer");
fprintf(f, "MEASURENOISE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
if (res > 0)
res = 0;
}
if (!res) {
/* Step 4: Send "4" */
ast_debug(1, "TestServer: 4. Send DTMF 4\n");
res = ast_dtmf_stream(chan, NULL, "4", 0, 0);
fprintf(f, "SEND DTMF 4: %s\n", (res < 0) ? "FAIL" : "PASS");
if (res > 0)
res = 0;
}
if (!res) {
/* Step 5: Wait one second */
ast_debug(1, "TestServer: 5. Wait one second\n");
res = ast_safe_sleep(chan, 1000);
fprintf(f, "WAIT 1 SEC: %s\n", (res < 0) ? "FAIL" : "PASS");
if (res > 0)
res = 0;
}
if (!res) {
/* Step 6: Measure noise */
ast_debug(1, "TestServer: 6. Measure tone\n");
res = measurenoise(chan, 4000, "TestServer");
fprintf(f, "MEASURETONE: %s (%d)\n", (res < 0) ? "FAIL" : "PASS", res);
if (res > 0)
res = 0;
}
if (!res) {
/* Step 7: Send "5" */
ast_debug(1, "TestServer: 7. Send DTMF 5\n");
res = ast_dtmf_stream(chan, NULL, "5", 0, 0);
fprintf(f, "SEND DTMF 5: %s\n", (res < 0) ? "FAIL" : "PASS");
if (res > 0)
res = 0;
}
if (!res) {
/* Step 8: Transmit tone noise */
ast_debug(1, "TestServer: 8. Transmit tone\n");
res = sendnoise(chan, 6000);
fprintf(f, "SENDTONE: %s\n", (res < 0) ? "FAIL" : "PASS");
}
if (!res || (res == '7')) {
/* Step 9: Wait for "7" */
ast_debug(1, "TestServer: 9. Wait DTMF 7\n");
if (!res)
res = ast_waitfordigit(chan, 3000);
fprintf(f, "WAIT DTMF 7: %s\n", (res != '7') ? "FAIL" : "PASS");
if (res == '7')
res = 0;
else
res = -1;
}
if (!res)
res = ast_safe_sleep(chan, 1000);
if (!res) {
/* Step 10: Send "8" */
ast_debug(1, "TestServer: 10. Send DTMF 8\n");
res = ast_dtmf_stream(chan, NULL, "8", 0, 0);
fprintf(f, "SEND DTMF 8: %s\n", (res < 0) ? "FAIL" : "PASS");
if (res > 0)
res = 0;
}
if (!res) {
/* Step 11: Wait for hangup to arrive! */
ast_debug(1, "TestServer: 11. Waiting for hangup\n");
res = ast_safe_sleep(chan, 10000);
fprintf(f, "WAIT HANGUP: %s\n", (res < 0) ? "PASS" : "FAIL");
}
ast_log(LOG_NOTICE, "-- TEST COMPLETE--\n");
fprintf(f, "-- END TEST--\n");
fclose(f);
res = -1;
} else
res = -1;
} else {
ast_log(LOG_NOTICE, "Did not read a test ID on '%s'\n", chan->name);
res = -1;
}
return res;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(testc_app);
res |= ast_unregister_application(tests_app);
return res;
}
static int load_module(void)
{
int res;
res = ast_register_application(testc_app, testclient_exec, testc_synopsis, testc_descrip);
res |= ast_register_application(tests_app, testserver_exec, tests_synopsis, tests_descrip);
return res;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Interface Test Application");

125
trunk/apps/app_transfer.c Normal file
View File

@@ -0,0 +1,125 @@
/*
* 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 Transfer a caller
*
* \author Mark Spencer <markster@digium.com>
*
* Requires transfer support from channel driver
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
static const char *app = "Transfer";
static const char *synopsis = "Transfer caller to remote extension";
static const char *descrip =
" Transfer([Tech/]dest[,options]): Requests the remote caller be transferred\n"
"to a given destination. If TECH (SIP, IAX2, LOCAL etc) is used, only\n"
"an incoming call with the same channel technology will be transfered.\n"
"Note that for SIP, if you transfer before call is setup, a 302 redirect\n"
"SIP message will be returned to the caller.\n"
"\nThe result of the application will be reported in the TRANSFERSTATUS\n"
"channel variable:\n"
" SUCCESS Transfer succeeded\n"
" FAILURE Transfer failed\n"
" UNSUPPORTED Transfer unsupported by channel driver\n";
static int transfer_exec(struct ast_channel *chan, void *data)
{
int res;
int len;
char *slash;
char *tech = NULL;
char *dest = NULL;
char *status;
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(dest);
AST_APP_ARG(options);
);
if (ast_strlen_zero((char *)data)) {
ast_log(LOG_WARNING, "Transfer requires an argument ([Tech/]destination[,options])\n");
pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
return 0;
} else
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (args.options) {
}
dest = args.dest;
if ((slash = strchr(dest, '/')) && (len = (slash - dest))) {
tech = dest;
dest = slash + 1;
/* Allow execution only if the Tech/destination agrees with the type of the channel */
if (strncasecmp(chan->tech->type, tech, len)) {
pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "FAILURE");
return 0;
}
}
/* Check if the channel supports transfer before we try it */
if (!chan->tech->transfer) {
pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", "UNSUPPORTED");
return 0;
}
res = ast_transfer(chan, dest);
if (res < 0) {
status = "FAILURE";
res = 0;
} else {
status = "SUCCESS";
res = 0;
}
pbx_builtin_setvar_helper(chan, "TRANSFERSTATUS", status);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, transfer_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Transfers a caller to another extension");

149
trunk/apps/app_url.c Normal file
View File

@@ -0,0 +1,149 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief App to transmit a URL
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
static char *app = "SendURL";
static char *synopsis = "Send a URL";
static char *descrip =
" SendURL(URL[,option]): Requests client go to URL (IAX2) or sends the \n"
"URL to the client (other channels).\n"
"Result is returned in the SENDURLSTATUS channel variable:\n"
" SUCCESS URL successfully sent to client\n"
" FAILURE Failed to send URL\n"
" NOLOAD Client failed to load URL (wait enabled)\n"
" UNSUPPORTED Channel does not support URL transport\n"
"\n"
"If the option 'w' is specified, execution will wait for an\n"
"acknowledgement that the URL has been loaded before continuing\n"
"\n"
"SendURL continues normally if the URL was sent correctly or if the channel\n"
"does not support HTML transport. Otherwise, the channel is hung up.\n";
enum {
OPTION_WAIT = (1 << 0),
} option_flags;
AST_APP_OPTIONS(app_opts,{
AST_APP_OPTION('w', OPTION_WAIT),
});
static int sendurl_exec(struct ast_channel *chan, void *data)
{
int res = 0;
char *tmp;
struct ast_frame *f;
char *status = "FAILURE";
char *opts[0];
struct ast_flags flags;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(url);
AST_APP_ARG(options);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "SendURL requires an argument (URL)\n");
pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", status);
return -1;
}
tmp = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, tmp);
if (args.argc == 2)
ast_app_parse_options(app_opts, &flags, opts, args.options);
if (!ast_channel_supports_html(chan)) {
/* Does not support transport */
pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", "UNSUPPORTED");
return 0;
}
res = ast_channel_sendurl(chan, args.url);
if (res == -1) {
pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", "FAILURE");
return res;
}
status = "SUCCESS";
if (ast_test_flag(&flags, OPTION_WAIT)) {
for(;;) {
/* Wait for an event */
res = ast_waitfor(chan, -1);
if (res < 0)
break;
f = ast_read(chan);
if (!f) {
res = -1;
status = "FAILURE";
break;
}
if (f->frametype == AST_FRAME_HTML) {
switch(f->subclass) {
case AST_HTML_LDCOMPLETE:
res = 0;
ast_frfree(f);
status = "NOLOAD";
goto out;
break;
case AST_HTML_NOSUPPORT:
/* Does not support transport */
status = "UNSUPPORTED";
res = 0;
ast_frfree(f);
goto out;
break;
default:
ast_log(LOG_WARNING, "Don't know what to do with HTML subclass %d\n", f->subclass);
};
}
ast_frfree(f);
}
}
out:
pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", status);
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, sendurl_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send URL Applications");

View File

@@ -0,0 +1,89 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief UserEvent application -- send manager event
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/manager.h"
#include "asterisk/app.h"
static char *app = "UserEvent";
static char *synopsis = "Send an arbitrary event to the manager interface";
static char *descrip =
" UserEvent(eventname[,body]): Sends an arbitrary event to the manager\n"
"interface, with an optional body representing additional arguments. The\n"
"body may be specified as a | delimeted list of headers. Each additional\n"
"argument will be placed on a new line in the event. The format of the\n"
"event will be:\n"
" Event: UserEvent\n"
" UserEvent: <specified event name>\n"
" [body]\n"
"If no body is specified, only Event and UserEvent headers will be present.\n";
static int userevent_exec(struct ast_channel *chan, void *data)
{
char *parse, buf[2048] = "";
int x, buflen = 0;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(eventname);
AST_APP_ARG(extra)[100];
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "UserEvent requires an argument (eventname,optional event body)\n");
return -1;
}
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
for (x = 0; x < args.argc - 1; x++) {
ast_copy_string(buf + buflen, args.extra[x], sizeof(buf) - buflen - 2);
buflen += strlen(args.extra[x]);
ast_copy_string(buf + buflen, "\r\n", 3);
buflen += 2;
}
manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", args.eventname, buf);
return 0;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, userevent_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Custom User Event Application");

158
trunk/apps/app_verbose.c Normal file
View File

@@ -0,0 +1,158 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (c) 2004 - 2005 Tilghman Lesher. All rights reserved.
*
* Tilghman Lesher <app_verbose_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 Verbose logging application
*
* \author Tilghman Lesher <app_verbose_v001@the-tilghman.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
static char *app_verbose = "Verbose";
static char *verbose_synopsis = "Send arbitrary text to verbose output";
static char *verbose_descrip =
"Verbose([<level>,]<message>)\n"
" level must be an integer value. If not specified, defaults to 0.\n";
static char *app_log = "Log";
static char *log_synopsis = "Send arbitrary text to a selected log level";
static char *log_descrip =
"Log(<level>,<message>)\n"
" level must be one of ERROR, WARNING, NOTICE, DEBUG, VERBOSE, DTMF\n";
static int verbose_exec(struct ast_channel *chan, void *data)
{
int vsize;
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(level);
AST_APP_ARG(msg);
);
if (ast_strlen_zero(data)) {
return 0;
}
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (args.argc == 1) {
args.msg = args.level;
args.level = "0";
}
if (sscanf(args.level, "%d", &vsize) != 1) {
vsize = 0;
ast_log(LOG_WARNING, "'%s' is not a verboser number\n", args.level);
}
if (option_verbose >= vsize) {
switch (vsize) {
case 0:
ast_verbose("%s\n", args.msg);
break;
case 1:
ast_verbose(VERBOSE_PREFIX_1 "%s\n", args.msg);
break;
case 2:
ast_verbose(VERBOSE_PREFIX_2 "%s\n", args.msg);
break;
case 3:
ast_verbose(VERBOSE_PREFIX_3 "%s\n", args.msg);
break;
default:
ast_verbose(VERBOSE_PREFIX_4 "%s\n", args.msg);
}
}
return 0;
}
static int log_exec(struct ast_channel *chan, void *data)
{
char *parse;
int lnum = -1;
char extension[AST_MAX_EXTENSION + 5], context[AST_MAX_EXTENSION + 2];
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(level);
AST_APP_ARG(msg);
);
if (ast_strlen_zero(data))
return 0;
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (!strcasecmp(args.level, "ERROR")) {
lnum = __LOG_ERROR;
} else if (!strcasecmp(args.level, "WARNING")) {
lnum = __LOG_WARNING;
} else if (!strcasecmp(args.level, "NOTICE")) {
lnum = __LOG_NOTICE;
} else if (!strcasecmp(args.level, "DEBUG")) {
lnum = __LOG_DEBUG;
} else if (!strcasecmp(args.level, "VERBOSE")) {
lnum = __LOG_VERBOSE;
} else if (!strcasecmp(args.level, "DTMF")) {
lnum = __LOG_DTMF;
} else if (!strcasecmp(args.level, "EVENT")) {
lnum = __LOG_EVENT;
} else {
ast_log(LOG_ERROR, "Unknown log level: '%s'\n", args.level);
}
if (lnum > -1) {
snprintf(context, sizeof(context), "@ %s", chan->context);
snprintf(extension, sizeof(extension), "Ext. %s", chan->exten);
ast_log(lnum, extension, chan->priority, context, "%s\n", args.msg);
}
return 0;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(app_verbose);
res |= ast_unregister_application(app_log);
return res;
}
static int load_module(void)
{
int res;
res = ast_register_application(app_log, log_exec, log_synopsis, log_descrip);
res |= ast_register_application(app_verbose, verbose_exec, verbose_synopsis, verbose_descrip);
return res;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send verbose output");

9831
trunk/apps/app_voicemail.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,117 @@
/*
* 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 Wait for Ring Application
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
static char *synopsis = "Wait for Ring Application";
static char *desc = " WaitForRing(timeout):\n"
"Returns 0 after waiting at least timeout seconds. and\n"
"only after the next ring has completed. Returns 0 on\n"
"success or -1 on hangup\n";
static char *app = "WaitForRing";
static int waitforring_exec(struct ast_channel *chan, void *data)
{
struct ast_frame *f;
int res = 0;
double s;
int ms;
if (!data || (sscanf(data, "%lg", &s) != 1)) {
ast_log(LOG_WARNING, "WaitForRing requires an argument (minimum seconds)\n");
return 0;
}
ms = s*1000.0;
while(ms > 0) {
ms = ast_waitfor(chan, ms);
if (ms < 0) {
res = ms;
break;
}
if (ms > 0) {
f = ast_read(chan);
if (!f) {
res = -1;
break;
}
if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) {
ast_verb(3, "Got a ring but still waiting for timeout\n");
}
ast_frfree(f);
}
}
/* Now we're really ready for the ring */
if (!res) {
ms = 99999999;
while(ms > 0) {
ms = ast_waitfor(chan, ms);
if (ms < 0) {
res = ms;
break;
}
if (ms > 0) {
f = ast_read(chan);
if (!f) {
res = -1;
break;
}
if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) {
ast_verb(3, "Got a ring after the timeout\n");
ast_frfree(f);
break;
}
ast_frfree(f);
}
}
}
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, waitforring_exec, synopsis, desc);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Waits until first ring after time");

View File

@@ -0,0 +1,189 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* WaitForSilence Application by David C. Troy <dave@popvox.com>
* Version 1.11 2006-06-29
*
* 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 Wait for Silence
* - Waits for up to 'x' milliseconds of silence, 'y' times \n
* - WaitForSilence(500,2) will wait for 1/2 second of silence, twice \n
* - WaitForSilence(1000,1) will wait for 1 second of silence, once \n
* - WaitForSilence(300,3,10) will wait for 300ms of silence, 3 times, and return after 10sec \n
*
* \author David C. Troy <dave@popvox.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/dsp.h"
#include "asterisk/module.h"
static char *app = "WaitForSilence";
static char *synopsis = "Waits for a specified amount of silence";
static char *descrip =
" WaitForSilence(silencerequired[,iterations][,timeout]):\n"
"Wait for Silence: Waits for up to 'silencerequired' \n"
"milliseconds of silence, 'iterations' times or once if omitted.\n"
"An optional timeout specified the number of seconds to return\n"
"after, even if we do not receive the specified amount of silence.\n"
"Use 'timeout' with caution, as it may defeat the purpose of this\n"
"application, which is to wait indefinitely until silence is detected\n"
"on the line. This is particularly useful for reverse-911-type\n"
"call broadcast applications where you need to wait for an answering\n"
"machine to complete its spiel before playing a message.\n"
"The timeout parameter is specified only to avoid an infinite loop in\n"
"cases where silence is never achieved. Typically you will want to\n"
"include two or more calls to WaitForSilence when dealing with an answering\n"
"machine; first waiting for the spiel to finish, then waiting for the beep, etc.\n\n"
"Examples:\n"
" - WaitForSilence(500,2) will wait for 1/2 second of silence, twice\n"
" - WaitForSilence(1000) will wait for 1 second of silence, once\n"
" - WaitForSilence(300,3,10) will wait for 300ms silence, 3 times,\n"
" and returns after 10 sec, even if silence is not detected\n\n"
"Sets the channel variable WAITSTATUS with to one of these values:\n"
"SILENCE - if exited with silence detected\n"
"TIMEOUT - if exited without silence detected after timeout\n";
static int do_waiting(struct ast_channel *chan, int silencereqd, time_t waitstart, int timeout) {
struct ast_frame *f;
int dspsilence = 0;
static int silencethreshold = 128;
int rfmt = 0;
int res = 0;
struct ast_dsp *sildet; /* silence detector dsp */
time_t now;
rfmt = chan->readformat; /* Set to linear mode */
res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to set channel to linear mode, giving up\n");
return -1;
}
sildet = ast_dsp_new(); /* Create the silence detector */
if (!sildet) {
ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
return -1;
}
ast_dsp_set_threshold(sildet, silencethreshold);
/* Await silence... */
f = NULL;
for(;;) {
/* Start with no silence received */
dspsilence = 0;
res = ast_waitfor(chan, silencereqd);
/* Must have gotten a hangup; let's exit */
if (res <= 0) {
f = NULL;
break;
}
/* We waited and got no frame; sounds like digital silence or a muted digital channel */
if (!res) {
dspsilence = silencereqd;
} else {
/* Looks like we did get a frame, so let's check it out */
f = ast_read(chan);
if (!f)
break;
if (f && f->frametype == AST_FRAME_VOICE) {
ast_dsp_silence(sildet, f, &dspsilence);
ast_frfree(f);
}
}
ast_verb(3, "Got %dms silence< %dms required\n", dspsilence, silencereqd);
if (dspsilence >= silencereqd) {
ast_verb(3, "Exiting with %dms silence >= %dms required\n", dspsilence, silencereqd);
/* Ended happily with silence */
res = 1;
pbx_builtin_setvar_helper(chan, "WAITSTATUS", "SILENCE");
ast_debug(1, "WAITSTATUS was set to SILENCE\n");
break;
}
if ( timeout && (difftime(time(&now),waitstart) >= timeout) ) {
pbx_builtin_setvar_helper(chan, "WAITSTATUS", "TIMEOUT");
ast_debug(1, "WAITSTATUS was set to TIMEOUT\n");
res = 0;
break;
}
}
if (rfmt && ast_set_read_format(chan, rfmt)) {
ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
}
ast_dsp_free(sildet);
return res;
}
static int waitforsilence_exec(struct ast_channel *chan, void *data)
{
int res = 1;
int silencereqd = 1000;
int timeout = 0;
int iterations = 1, i;
time_t waitstart;
res = ast_answer(chan); /* Answer the channel */
if (!data || ( (sscanf(data, "%d,%d,%d", &silencereqd, &iterations, &timeout) != 3) &&
(sscanf(data, "%d|%d", &silencereqd, &iterations) != 2) &&
(sscanf(data, "%d", &silencereqd) != 1) ) ) {
ast_log(LOG_WARNING, "Using default value of 1000ms, 1 iteration, no timeout\n");
}
ast_verb(3, "Waiting %d time(s) for %d ms silence with %d timeout\n", iterations, silencereqd, timeout);
time(&waitstart);
res = 1;
for (i=0; (i<iterations) && (res == 1); i++) {
res = do_waiting(chan, silencereqd, waitstart, timeout);
}
if (res > 0)
res = 0;
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, waitforsilence_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Wait For Silence");

View File

@@ -0,0 +1,93 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007, Redfish Solutions
*
* Philip Prindeville <philipp@redfish-solutions.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 Sleep until the given epoch
*
* \author Philip Prindeville <philipp@redfish-solutions.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
static char *app = "WaitUntil";
static char *synopsis = "Wait (sleep) until the current time is the given epoch";
static char *descrip =
" WaitUntil(<epoch>): Waits until the given time. Sets WAITUNTILSTATUS to\n"
"one of the following values:\n"
" OK Wait succeeded\n"
" FAILURE Invalid argument\n"
" HANGUP Channel hung up before time elapsed\n"
" PAST The time specified was already past\n";
static int waituntil_exec(struct ast_channel *chan, void *data)
{
int res;
double fraction;
struct timeval future = { 0, };
struct timeval tv = ast_tvnow();
int msec;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "WaitUntil requires an argument(epoch)\n");
pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "FAILURE");
return 0;
}
if (sscanf(data, "%ld%lf", (long *)&future.tv_sec, &fraction) == 0) {
ast_log(LOG_WARNING, "WaitUntil called with non-numeric argument\n");
pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "FAILURE");
return 0;
}
future.tv_usec = fraction * 1000000;
if ((msec = ast_tvdiff_ms(future, tv)) < 0) {
ast_log(LOG_NOTICE, "WaitUntil called in the past (now %ld, arg %ld)\n", (long)tv.tv_sec, (long)future.tv_sec);
pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "PAST");
return 0;
}
if ((res = ast_safe_sleep(chan, msec)))
pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "HANGUP");
else
pbx_builtin_setvar_helper(chan, "WAITUNTILSTATUS", "OK");
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ast_register_application(app, waituntil_exec, synopsis, descrip);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Wait until specified time");

307
trunk/apps/app_while.c Normal file
View File

@@ -0,0 +1,307 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright 2004 - 2005, Anthony Minessale <anthmct@yahoo.com>
*
* Anthony Minessale <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 While Loop Implementation
*
* \author Anthony Minessale <anthmct@yahoo.com>
*
* \ingroup applications
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/channel.h"
static char *start_app = "While";
static char *start_desc =
" While(<expr>): Start a While Loop. Execution will return to this\n"
"point when EndWhile() is called until expr is no longer true.\n";
static char *start_synopsis = "Start a while loop";
static char *stop_app = "EndWhile";
static char *stop_desc =
" EndWhile(): Return to the previous called While()\n";
static char *stop_synopsis = "End a while loop";
static char *exit_app = "ExitWhile";
static char *exit_desc =
" ExitWhile(): Exits a While() loop, whether or not the conditional has been satisfied.\n";
static char *exit_synopsis = "End a While loop";
static char *continue_app = "ContinueWhile";
static char *continue_desc =
" ContinueWhile(): Returns to the top of the while loop and re-evaluates the conditional.\n";
static char *continue_synopsis = "Restart a While loop";
#define VAR_SIZE 64
static const char *get_index(struct ast_channel *chan, const char *prefix, int index) {
char varname[VAR_SIZE];
snprintf(varname, VAR_SIZE, "%s_%d", prefix, index);
return pbx_builtin_getvar_helper(chan, varname);
}
static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
{
struct ast_exten *e;
struct ast_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 find_matching_endwhile(struct ast_channel *chan)
{
struct ast_context *c;
int res=-1;
if (ast_rdlock_contexts()) {
ast_log(LOG_ERROR, "Failed to lock contexts list\n");
return -1;
}
for (c=ast_walk_contexts(NULL); c; c=ast_walk_contexts(c)) {
struct ast_exten *e;
if (!ast_rdlock_context(c)) {
if (!strcmp(ast_get_context_name(c), chan->context)) {
/* This is the matching context we want */
int cur_priority = chan->priority + 1, level=1;
for (e = find_matching_priority(c, chan->exten, cur_priority, chan->cid.cid_num); e; e = find_matching_priority(c, chan->exten, ++cur_priority, chan->cid.cid_num)) {
if (!strcasecmp(ast_get_extension_app(e), "WHILE")) {
level++;
} else if (!strcasecmp(ast_get_extension_app(e), "ENDWHILE")) {
level--;
}
if (level == 0) {
res = cur_priority;
break;
}
}
}
ast_unlock_context(c);
if (res > 0) {
break;
}
}
}
ast_unlock_contexts();
return res;
}
static int _while_exec(struct ast_channel *chan, void *data, int end)
{
int res=0;
const char *while_pri = NULL;
char *my_name = NULL;
const char *condition = NULL, *label = NULL;
char varname[VAR_SIZE], end_varname[VAR_SIZE];
const char *prefix = "WHILE";
size_t size=0;
int used_index_i = -1, x=0;
char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
if (!chan) {
/* huh ? */
return -1;
}
/* dont want run away loops if the chan isn't even up
this is up for debate since it slows things down a tad ......
*/
if (ast_waitfordigit(chan,1) < 0)
return -1;
for (x=0;;x++) {
if (get_index(chan, prefix, x)) {
used_index_i = x;
} else
break;
}
snprintf(used_index, VAR_SIZE, "%d", used_index_i);
snprintf(new_index, VAR_SIZE, "%d", used_index_i + 1);
if (!end)
condition = ast_strdupa(data);
size = strlen(chan->context) + strlen(chan->exten) + 32;
my_name = alloca(size);
memset(my_name, 0, size);
snprintf(my_name, size, "%s_%s_%d", chan->context, chan->exten, chan->priority);
if (ast_strlen_zero(label)) {
if (end)
label = used_index;
else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
label = new_index;
pbx_builtin_setvar_helper(chan, my_name, label);
}
}
snprintf(varname, VAR_SIZE, "%s_%s", prefix, label);
while_pri = pbx_builtin_getvar_helper(chan, varname);
if ((while_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
snprintf(end_varname,VAR_SIZE,"END_%s",varname);
}
if ((!end && !pbx_checkcondition(condition)) || (end == 2)) {
/* Condition Met (clean up helper vars) */
const char *goto_str;
pbx_builtin_setvar_helper(chan, varname, NULL);
pbx_builtin_setvar_helper(chan, my_name, NULL);
snprintf(end_varname,VAR_SIZE,"END_%s",varname);
if ((goto_str=pbx_builtin_getvar_helper(chan, end_varname))) {
ast_parseable_goto(chan, goto_str);
pbx_builtin_setvar_helper(chan, end_varname, NULL);
} else {
int pri = find_matching_endwhile(chan);
if (pri > 0) {
ast_verb(3, "Jumping to priority %d\n", pri);
chan->priority = pri;
} else {
ast_log(LOG_WARNING, "Couldn't find matching EndWhile? (While at %s@%s priority %d)\n", chan->context, chan->exten, chan->priority);
}
}
return res;
}
if (!end && !while_pri) {
char *goto_str;
size = strlen(chan->context) + strlen(chan->exten) + 32;
goto_str = alloca(size);
memset(goto_str, 0, size);
snprintf(goto_str, size, "%s,%s,%d", chan->context, chan->exten, chan->priority);
pbx_builtin_setvar_helper(chan, varname, goto_str);
}
else if (end && while_pri) {
/* END of loop */
snprintf(end_varname, VAR_SIZE, "END_%s", varname);
if (! pbx_builtin_getvar_helper(chan, end_varname)) {
char *goto_str;
size = strlen(chan->context) + strlen(chan->exten) + 32;
goto_str = alloca(size);
memset(goto_str, 0, size);
snprintf(goto_str, size, "%s,%s,%d", chan->context, chan->exten, chan->priority+1);
pbx_builtin_setvar_helper(chan, end_varname, goto_str);
}
ast_parseable_goto(chan, while_pri);
}
return res;
}
static int while_start_exec(struct ast_channel *chan, void *data) {
return _while_exec(chan, data, 0);
}
static int while_end_exec(struct ast_channel *chan, void *data) {
return _while_exec(chan, data, 1);
}
static int while_exit_exec(struct ast_channel *chan, void *data) {
return _while_exec(chan, data, 2);
}
static int while_continue_exec(struct ast_channel *chan, void *data)
{
int x;
const char *prefix = "WHILE", *while_pri=NULL;
for (x = 0; ; x++) {
const char *tmp = get_index(chan, prefix, x);
if (tmp)
while_pri = tmp;
else
break;
}
if (while_pri)
ast_parseable_goto(chan, while_pri);
return 0;
}
static int unload_module(void)
{
int res;
res = ast_unregister_application(start_app);
res |= ast_unregister_application(stop_app);
res |= ast_unregister_application(exit_app);
res |= ast_unregister_application(continue_app);
return res;
}
static int load_module(void)
{
int res;
res = ast_register_application(start_app, while_start_exec, start_synopsis, start_desc);
res |= ast_register_application(stop_app, while_end_exec, stop_synopsis, stop_desc);
res |= ast_register_application(exit_app, while_exit_exec, exit_synopsis, exit_desc);
res |= ast_register_application(continue_app, while_continue_exec, continue_synopsis, continue_desc);
return res;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "While Loops and Conditional Execution");

113
trunk/apps/app_zapateller.c Normal file
View File

@@ -0,0 +1,113 @@
/*
* 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 Playback the special information tone to get rid of telemarketers
*
* \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/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/app.h"
static char *app = "Zapateller";
static char *synopsis = "Block telemarketers with SIT";
static char *descrip =
" Zapateller(options): Generates special information tone to block\n"
"telemarketers from calling you. Options is a pipe-delimited list of\n"
"options. The following options are available:\n"
" 'answer' - causes the line to be answered before playing the tone,\n"
" 'nocallerid' - causes Zapateller to only play the tone if there is no\n"
" callerid information available. Options should be\n"
" separated by , characters\n\n"
" This application will set the following channel variable upon completion:\n"
" ZAPATELLERSTATUS - This will contain the last action accomplished by the\n"
" Zapateller application. Possible values include:\n"
" NOTHING | ANSWERED | ZAPPED\n\n";
static int zapateller_exec(struct ast_channel *chan, void *data)
{
int res = 0;
int i, answer = 0, nocallerid = 0;
char *parse = ast_strdupa((char *)data);
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(options)[2];
);
AST_STANDARD_APP_ARGS(args, parse);
for (i = 0; i < args.argc; i++) {
if (!strcasecmp(args.options[i], "answer"))
answer = 1;
else if (!strcasecmp(args.options[i], "nocallerid"))
nocallerid = 1;
}
pbx_builtin_setvar_helper(chan, "ZAPATELLERSTATUS", "NOTHING");
ast_stopstream(chan);
if (chan->_state != AST_STATE_UP) {
if (answer) {
res = ast_answer(chan);
pbx_builtin_setvar_helper(chan, "ZAPATELLERSTATUS", "ANSWERED");
}
if (!res)
res = ast_safe_sleep(chan, 500);
}
if (!ast_strlen_zero(chan->cid.cid_num) && nocallerid)
return res;
if (!res)
res = ast_tonepair(chan, 950, 0, 330, 0);
if (!res)
res = ast_tonepair(chan, 1400, 0, 330, 0);
if (!res)
res = ast_tonepair(chan, 1800, 0, 330, 0);
if (!res)
res = ast_tonepair(chan, 0, 0, 1000, 0);
pbx_builtin_setvar_helper(chan, "ZAPATELLERSTATUS", "ZAPPED");
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ((ast_register_application(app, zapateller_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Block Telemarketers with Special Information Tone");

299
trunk/apps/app_zapbarge.c Normal file
View File

@@ -0,0 +1,299 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2005, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* 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 Barge support
*
* \author Mark Spencer <markster@digium.com>
*
* \note Special thanks to comphealth.com for sponsoring this
* GPL application.
*
* \ingroup applications
*/
/*** MODULEINFO
<depend>zaptel</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/zapata.h"
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/config.h"
#include "asterisk/app.h"
#include "asterisk/cli.h"
#include "asterisk/say.h"
#include "asterisk/utils.h"
static char *app = "ZapBarge";
static char *synopsis = "Barge in (monitor) Zap channel";
static char *descrip =
" ZapBarge([channel]): Barges in on a specified zap\n"
"channel or prompts if one is not specified. Returns\n"
"-1 when caller user hangs up and is independent of the\n"
"state of the channel being monitored.";
#define CONF_SIZE 160
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 zt_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;
ZT_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("/dev/zap/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 = ZT_POLICY_IMMEDIATE;
bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
bi.numbufs = 4;
if (ioctl(fd, ZT_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, ZT_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_debug(1, "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 = ZT_CONF_MONITORBOTH;
if (ioctl(fd, ZT_SETCONF, &ztc)) {
ast_log(LOG_WARNING, "Error setting conference\n");
close(fd);
goto outrun;
}
ast_debug(1, "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_debug(1, "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) && (f->subclass == '#')) {
ret = 0;
ast_frfree(f);
break;
} else 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 (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, ZT_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;
int retrycnt = 0;
int confflags = 0;
int confno = 0;
char confstr[80] = "";
if (!ast_strlen_zero(data)) {
if ((sscanf(data, "Zap/%d", &confno) != 1) &&
(sscanf(data, "%d", &confno) != 1)) {
ast_log(LOG_WARNING, "ZapBarge Argument (if specified) must be a channel number, not '%s'\n", (char *)data);
return 0;
}
}
if (chan->_state != AST_STATE_UP)
ast_answer(chan);
while(!confno && (++retrycnt < 4)) {
/* Prompt user for conference number */
confstr[0] = '\0';
res = ast_app_getdata(chan, "conf-getchannel",confstr, sizeof(confstr) - 1, 0);
if (res <0) goto out;
if (sscanf(confstr, "%d", &confno) != 1)
confno = 0;
}
if (confno) {
/* XXX Should prompt user for pin if pin is required XXX */
/* Run the conference */
res = conf_run(chan, confno, confflags);
}
out:
/* Do the conference */
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ((ast_register_application(app, conf_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Barge in on Zap channel application");

238
trunk/apps/app_zapras.c Normal file
View File

@@ -0,0 +1,238 @@
/*
* 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>zaptel</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 <fcntl.h>
#include "asterisk/zapata.h"
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
static char *app = "ZapRAS";
static char *synopsis = "Executes Zaptel ISDN RAS application";
static char *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 be patched to be zaptel aware. 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;
sigfillset(&fullset);
pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
/* Start by forking */
pid = fork();
if (pid) {
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
return pid;
}
/* 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 zaptel 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";
argv[argc++] = "zaptel.so";
argv[argc++] = "stdin";
/* Finally launch PPP */
execv(PPP_EXEC, argv);
fprintf(stderr, "Failed to exec PPPD!\n");
exit(1);
}
static void run_ras(struct ast_channel *chan, char *args)
{
pid_t pid;
int status;
int res;
int signalled = 0;
struct zt_bufferinfo savebi;
int x;
res = ioctl(chan->fds[0], ZT_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 (ast_check_hangup(chan) && !signalled) {
ast_debug(1, "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], ZT_AUDIOMODE, &x);
/* Restore saved values */
res = ioctl(chan->fds[0], ZT_SET_BUFINFO, &savebi);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to set buffer policy on channel %s\n", chan->name);
}
break;
}
}
}
static int zapras_exec(struct ast_channel *chan, void *data)
{
int res=-1;
char *args;
ZT_PARAMS ztp;
if (!data)
data = "";
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, "Zap")) {
/* If it's not a zap channel, we're done. Wait a couple of
seconds and then hangup... */
ast_verb(2, "Channel %s is not a Zap channel\n", chan->name);
sleep(2);
} else {
memset(&ztp, 0, sizeof(ztp));
if (ioctl(chan->fds[0], ZT_GET_PARAMS, &ztp)) {
ast_log(LOG_WARNING, "Unable to get zaptel parameters\n");
} else if (ztp.sigtype != ZT_SIG_CLEAR) {
ast_verb(2, "Channel %s is not a clear channel\n", chan->name);
} else {
/* Everything should be okay. Run PPP. */
ast_verb(3, "Starting RAS on %s\n", chan->name);
/* Execute RAS */
run_ras(chan, args);
}
}
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ((ast_register_application(app, zapras_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Zaptel ISDN Remote Access Server");

363
trunk/apps/app_zapscan.c Normal file
View File

@@ -0,0 +1,363 @@
/*
* 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>zaptel</depend>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/zapata.h"
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/config.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "asterisk/say.h"
static char *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),"Zap/%d-1",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 zt_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;
ZT_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("/dev/zap/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 = ZT_POLICY_IMMEDIATE;
bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
bi.numbufs = 4;
if (ioctl(fd, ZT_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, ZT_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_debug(1, "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 = ZT_CONF_MONITORBOTH;
if (ioctl(fd, ZT_SETCONF, &ztc)) {
ast_log(LOG_WARNING, "Error setting conference\n");
close(fd);
goto outrun;
}
ast_debug(1, "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_debug(1, "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_verb(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, ZT_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;
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;
if (chan->_state != AST_STATE_UP)
ast_answer(chan);
desired_group = ast_strdupa(data);
if(!ast_strlen_zero(desired_group)) {
ast_verb(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_verb(3, "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
} else {
ast_channel_unlock(tempchan);
lastchan = tempchan;
continue;
}
}
if (tempchan && (!strcmp(tempchan->tech->type, "Zap")) && (tempchan != chan) ) {
ast_verb(3, "Zap channel %s is in-use, monitoring...\n", tempchan->name);
ast_copy_string(confstr, tempchan->name, sizeof(confstr));
ast_channel_unlock(tempchan);
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_channel_unlock(tempchan);
lastchan = tempchan;
}
return res;
}
static int unload_module(void)
{
return ast_unregister_application(app);
}
static int load_module(void)
{
return ((ast_register_application(app, conf_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan Zap channels application");

287
trunk/apps/enter.h Normal file
View File

@@ -0,0 +1,287 @@
/*
* U-law 8-bit audio data
*
* Source: enter.raw
*
* Copyright (C) 1999, Mark Spencer and Linux Support Services
*
* Distributed under the terms of the GNU General Public License
*
*/
static unsigned char enter[] = {
0xba, 0xba, 0xb0, 0xa6, 0xa9, 0xb8, 0xfe, 0x46, 0x42, 0x46,
0x4a, 0xfe, 0xac, 0xa2, 0x9f, 0x9f, 0xa8, 0xb8, 0x3b, 0x29,
0x35, 0x4a, 0xfe, 0xc1, 0xad, 0xa2, 0xad, 0xc5, 0x4e, 0x68,
0x68, 0xe7, 0xb8, 0xb0, 0xb2, 0xc1, 0xc1, 0xb0, 0xae, 0xcd,
0xfe, 0xfe, 0xcd, 0xcd, 0xfe, 0x68, 0xd3, 0xb2, 0xae, 0xab,
0xb2, 0xfe, 0x35, 0x31, 0xdb, 0xac, 0xab, 0xaf, 0xab, 0xaa,
0xb4, 0x68, 0x3b, 0x39, 0x3f, 0x68, 0xb4, 0xa8, 0xa8, 0xb0,
0xbc, 0xbc, 0xc5, 0x3f, 0x31, 0x37, 0xfe, 0xc1, 0xbc, 0xb0,
0xa5, 0xa2, 0xa8, 0xaf, 0xbe, 0x3b, 0x28, 0x26, 0x3d, 0xbc,
0xb0, 0xae, 0xa2, 0x9f, 0xa2, 0xfe, 0x29, 0x24, 0x29, 0x4a,
0xc5, 0xaa, 0xa8, 0xa9, 0xa8, 0xa5, 0xa7, 0xdb, 0x2c, 0x27,
0x2d, 0x4a, 0xfe, 0xdb, 0xb2, 0xa2, 0x9f, 0x9f, 0xae, 0xe7,
0x2c, 0x22, 0x2b, 0xfe, 0xba, 0xb0, 0xaa, 0x9f, 0xa3, 0xb0,
0x5c, 0x33, 0x33, 0x39, 0x5c, 0xdb, 0xc1, 0xb4, 0xb0, 0xaa,
0xad, 0xba, 0x54, 0x46, 0xfe, 0xe7, 0xfe, 0x54, 0xe7, 0xaf,
0xa6, 0xa7, 0xb0, 0xfe, 0x46, 0x39, 0x5c, 0xe7, 0xdb, 0xfe,
0xba, 0xac, 0xa8, 0xc5, 0x46, 0x33, 0x54, 0xc5, 0xae, 0xad,
0xb2, 0xc1, 0xcd, 0xc1, 0xbc, 0xfe, 0x3f, 0x37, 0xfe, 0xb4,
0xb6, 0xcd, 0xdb, 0xc1, 0xb0, 0xb6, 0xcd, 0x4e, 0x39, 0x37,
0xfe, 0xb0, 0xab, 0xa9, 0xa9, 0xa9, 0xb0, 0x5c, 0x29, 0x25,
0x31, 0xfe, 0xc1, 0xb4, 0xae, 0xab, 0xab, 0xb2, 0xcd, 0x3b,
0x2a, 0x2c, 0x54, 0xb4, 0xb4, 0xba, 0xb2, 0xa3, 0x9f, 0xa8,
0xfe, 0x33, 0x27, 0x2a, 0x39, 0xfe, 0xc1, 0xbe, 0xb0, 0xa2,
0x9f, 0xb0, 0x33, 0x22, 0x25, 0x46, 0xc1, 0xb8, 0xb0, 0xab,
0xa8, 0xa8, 0xb0, 0xbe, 0x42, 0x2c, 0x2e, 0x4a, 0xfe, 0x5c,
0xfe, 0xb4, 0xa8, 0xa8, 0xba, 0xfe, 0x4a, 0x39, 0x39, 0x46,
0xfe, 0xbc, 0xaf, 0xa5, 0xa5, 0xae, 0x68, 0x37, 0x4a, 0xfe,
0xfe, 0x4a, 0x4a, 0xd3, 0xb0, 0xb0, 0xc1, 0x5c, 0x46, 0x46,
0xd3, 0xb6, 0xbe, 0x54, 0x54, 0xc9, 0xab, 0xae, 0xc5, 0x46,
0x4a, 0xfe, 0xcd, 0xc9, 0xcd, 0xe7, 0xe7, 0xc9, 0xb4, 0xc5,
0x4a, 0x2c, 0x37, 0xc1, 0xb0, 0xb2, 0xb4, 0xb2, 0xb6, 0xdb,
0xfe, 0x4a, 0x46, 0x3f, 0x68, 0xba, 0xb2, 0xba, 0xc5, 0xb6,
0xb2, 0xcd, 0x33, 0x2e, 0x39, 0x68, 0xfe, 0xe7, 0xba, 0xaf,
0xa7, 0xa7, 0xad, 0xe7, 0x2d, 0x25, 0x2f, 0xd3, 0xbe, 0xcd,
0xc5, 0xac, 0xa6, 0xac, 0xfe, 0x3b, 0x2c, 0x2d, 0x3d, 0xc1,
0xb4, 0xbe, 0xcd, 0xaf, 0xa5, 0xa8, 0xe7, 0x31, 0x2f, 0x39,
0x46, 0x5c, 0xdb, 0xbc, 0xba, 0xaf, 0xa9, 0xad, 0xfe, 0x2f,
0x2d, 0xba, 0xad, 0xba, 0xfe, 0x3d, 0x42, 0x5c, 0xc9, 0xc1,
0xcd, 0xfe, 0xc1, 0xae, 0xa6, 0xcd, 0x33, 0x25, 0x3b, 0xdb,
0xb0, 0xb6, 0xb8, 0xb6, 0xb4, 0xb8, 0xba, 0xfe, 0x3d, 0x37,
0xfe, 0xba, 0xc1, 0x54, 0x54, 0xd3, 0xb0, 0xb4, 0xe7, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xd3, 0xb6, 0xa9, 0xa7, 0xba,
0x3d, 0x35, 0xfe, 0xc1, 0xcd, 0x4a, 0x54, 0xbe, 0xb2, 0xb8,
0xfe, 0x46, 0x3b, 0xfe, 0xba, 0xab, 0xc5, 0x46, 0x3b, 0xbc,
0xaa, 0xab, 0xd3, 0x68, 0xfe, 0xd3, 0xcd, 0xdb, 0x54, 0x3d,
0x4a, 0xbc, 0xac, 0xb4, 0x3f, 0x2e, 0x3d, 0xba, 0xb0, 0xb8,
0xba, 0xb6, 0xba, 0xcd, 0xfe, 0xfe, 0x5c, 0x54, 0xc9, 0xb4,
0xbe, 0x54, 0x54, 0xcd, 0xb6, 0xc9, 0x46, 0x54, 0xcd, 0xc5,
0xdb, 0xfe, 0xfe, 0xc1, 0xae, 0xa9, 0xac, 0xfe, 0x35, 0x2e,
0xfe, 0xba, 0xc1, 0x5c, 0xfe, 0xb6, 0xaa, 0xb0, 0xe7, 0x35,
0x2e, 0x39, 0xc1, 0xac, 0xb0, 0xfe, 0xfe, 0xbc, 0xa6, 0xac,
0xc1, 0x42, 0x46, 0x54, 0xfe, 0xfe, 0xfe, 0xfe, 0xc9, 0xae,
0xa9, 0xb0, 0x54, 0x35, 0x37, 0xfe, 0xd3, 0xd3, 0xb8, 0xae,
0xab, 0xb6, 0xe7, 0xfe, 0xfe, 0x68, 0xfe, 0xfe, 0xfe, 0x4e,
0xfe, 0xb0, 0xac, 0xb8, 0xfe, 0xfe, 0xc1, 0xb6, 0xc5, 0x46,
0x3d, 0xe7, 0xb4, 0xa7, 0xab, 0xbc, 0x3f, 0x37, 0x54, 0xba,
0xcd, 0x54, 0x42, 0xc5, 0xae, 0xac, 0xc9, 0x46, 0x3d, 0x54,
0xba, 0xb0, 0xb0, 0xfe, 0x5c, 0xcd, 0xb0, 0xb0, 0xc9, 0x54,
0x54, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xcd, 0xc1, 0xba, 0xc5,
0xfe, 0x42, 0x46, 0xfe, 0xc5, 0xba, 0xb2, 0xa7, 0xa7, 0xb0,
0xfe, 0x3d, 0x4a, 0x5c, 0xfe, 0xfe, 0xfe, 0xe7, 0xbc, 0xb0,
0xae, 0xc5, 0x4e, 0x39, 0xfe, 0xc5, 0xbe, 0xfe, 0x54, 0xc9,
0xa9, 0xa2, 0xa5, 0xbc, 0x3b, 0x2f, 0x35, 0xfe, 0xc9, 0xfe,
0xfe, 0xc5, 0xa9, 0xa6, 0xb0, 0x54, 0x31, 0x31, 0x3f, 0xd3,
0xbc, 0xc1, 0xcd, 0xb8, 0xae, 0xa8, 0xb4, 0xd3, 0x54, 0x4e,
0x5c, 0x54, 0xfe, 0xdb, 0xba, 0xb4, 0xb4, 0xba, 0xcd, 0x5c,
0x3d, 0x3f, 0x54, 0xfe, 0xcd, 0xaf, 0xa8, 0xac, 0xc5, 0xfe,
0xfe, 0xe7, 0xdb, 0xfe, 0xfe, 0xfe, 0xe7, 0xb8, 0xaf, 0xb0,
0xe7, 0x42, 0x4a, 0xcd, 0xbc, 0xdb, 0x46, 0x68, 0xcd, 0xb0,
0xab, 0xbc, 0xfe, 0x3d, 0x46, 0xfe, 0xb8, 0xbc, 0xd3, 0xd3,
0xb6, 0xb0, 0xb6, 0x5c, 0x3b, 0x35, 0x54, 0xdb, 0xba, 0xb4,
0xc1, 0xc9, 0xc1, 0xba, 0xc9, 0x5c, 0x3d, 0x46, 0xfe, 0xcd,
0xc5, 0xb8, 0xae, 0xaf, 0xb4, 0xd3, 0x54, 0x3d, 0x35, 0x46,
0xfe, 0xdb, 0xbc, 0xb2, 0xa9, 0xab, 0xba, 0x3f, 0x31, 0x39,
0xfe, 0xe7, 0xdb, 0xcd, 0xb8, 0xae, 0xab, 0xac, 0xe7, 0x3d,
0x2d, 0x3f, 0xfe, 0xdb, 0xfe, 0xfe, 0xbc, 0xaa, 0xa8, 0xb0,
0xfe, 0x31, 0x2d, 0x3d, 0xdb, 0xc5, 0xcd, 0xc9, 0xb4, 0xa8,
0xad, 0xc5, 0x46, 0x39, 0x3f, 0x5c, 0xfe, 0xd3, 0xc5, 0xc1,
0xb6, 0xb0, 0xbc, 0x68, 0x46, 0x4e, 0xe7, 0xfe, 0x5c, 0xfe,
0xc1, 0xaf, 0xb0, 0xb8, 0xe7, 0x5c, 0x5c, 0xfe, 0xe7, 0xfe,
0xfe, 0xe7, 0xb0, 0xab, 0xb2, 0x4a, 0x37, 0x3f, 0xcd, 0xbe,
0xc1, 0xe7, 0xe7, 0xd3, 0xb6, 0xb4, 0xc9, 0x3b, 0x33, 0x4a,
0xba, 0xb4, 0xc5, 0xfe, 0xc9, 0xb6, 0xb4, 0xcd, 0xfe, 0x3b,
0x3b, 0xfe, 0xc1, 0xb6, 0xc5, 0xc5, 0xb8, 0xb0, 0xba, 0x4a,
0x31, 0x35, 0x68, 0xcd, 0xc5, 0xba, 0xb4, 0xb0, 0xb0, 0xba,
0x5c, 0x35, 0x2f, 0x4e, 0xd3, 0xc1, 0xdb, 0xd3, 0xb4, 0xa9,
0xab, 0xcd, 0x3b, 0x2f, 0x35, 0xfe, 0xd3, 0xd3, 0xdb, 0xbc,
0xad, 0xa4, 0xb0, 0xfe, 0x2d, 0x2f, 0x3f, 0xe7, 0xe7, 0xe7,
0xcd, 0xb4, 0xaf, 0xad, 0xc5, 0x3d, 0x31, 0x3d, 0xe7, 0xd3,
0xe7, 0xe7, 0xc1, 0xaf, 0xad, 0xb6, 0xfe, 0x4a, 0x42, 0x54,
0xfe, 0x68, 0xfe, 0xd3, 0xb2, 0xae, 0xb4, 0xfe, 0x42, 0x4e,
0xcd, 0xc5, 0xcd, 0xdb, 0xc9, 0xb4, 0xb0, 0xb6, 0xfe, 0x3b,
0x42, 0xe7, 0xb0, 0xb8, 0xcd, 0xfe, 0xc9, 0xb6, 0xb8, 0xfe,
0x42, 0x3d, 0xfe, 0xc1, 0xb0, 0xba, 0xd3, 0xfe, 0xc1, 0xb0,
0xb6, 0xfe, 0x3b, 0x3f, 0xe7, 0xba, 0xb8, 0xbc, 0xc5, 0xc1,
0xc1, 0xcd, 0xfe, 0x3b, 0x37, 0xfe, 0xc1, 0xb4, 0xb6, 0xb8,
0xb6, 0xb8, 0xc5, 0x5c, 0x3f, 0x46, 0xfe, 0xcd, 0xc5, 0xcd,
0xcd, 0xc1, 0xb2, 0xb2, 0xfe, 0x3f, 0x35, 0x54, 0xdb, 0xc1,
0xcd, 0xcd, 0xbc, 0xaf, 0xac, 0xb6, 0x54, 0x35, 0x31, 0x68,
0xba, 0xb8, 0xcd, 0xdb, 0xc9, 0xb2, 0xb4, 0xc9, 0x46, 0x39,
0x42, 0xdb, 0xbc, 0xbc, 0xcd, 0xcd, 0xbe, 0xb2, 0xb8, 0xe7,
0x54, 0x46, 0xfe, 0xfe, 0xdb, 0xc9, 0xc5, 0xbe, 0xbe, 0xc9,
0xfe, 0x5c, 0x5c, 0xfe, 0xd3, 0xcd, 0xcd, 0xc5, 0xb6, 0xb2,
0xc5, 0x68, 0x4e, 0xfe, 0xc5, 0xc1, 0xcd, 0x68, 0x5c, 0xe7,
0xb8, 0xb6, 0xd3, 0x4a, 0x46, 0xfe, 0xbc, 0xb8, 0xc1, 0xe7,
0xe7, 0xc1, 0xb4, 0xbe, 0xfe, 0x3f, 0x3f, 0xfe, 0xba, 0xb2,
0xba, 0xe7, 0xfe, 0xcd, 0xcd, 0xfe, 0x4e, 0x46, 0xfe, 0xc5,
0xb8, 0xb2, 0xba, 0xc1, 0xcd, 0xd3, 0xe7, 0xfe, 0x5c, 0x5c,
0xfe, 0xe7, 0xc5, 0xbe, 0xb6, 0xba, 0xc5, 0xfe, 0x3f, 0x3f,
0x54, 0xfe, 0xd3, 0xc1, 0xbc, 0xb6, 0xb0, 0xb0, 0xd3, 0x54,
0x39, 0x46, 0xfe, 0xc1, 0xcd, 0xe7, 0xe7, 0xc5, 0xb8, 0xb4,
0xd3, 0x54, 0x37, 0x42, 0xdb, 0xbe, 0xc1, 0xd3, 0xcd, 0xb8,
0xb0, 0xb0, 0xcd, 0x4a, 0x3b, 0x42, 0xe7, 0xc5, 0xbe, 0xcd,
0xe7, 0xd3, 0xc5, 0xcd, 0xfe, 0x54, 0x54, 0x68, 0xe7, 0xc5,
0xc1, 0xc1, 0xcd, 0xcd, 0xc9, 0xc9, 0xcd, 0xe7, 0xfe, 0xfe,
0xfe, 0xe7, 0xc5, 0xbe, 0xc1, 0xfe, 0x5c, 0x5c, 0xfe, 0xcd,
0xcd, 0xcd, 0xdb, 0xd3, 0xc1, 0xbc, 0xbe, 0xfe, 0x4e, 0x54,
0xcd, 0xb6, 0xb8, 0xd3, 0x5c, 0x5c, 0xfe, 0xc5, 0xc9, 0xfe,
0x46, 0x4a, 0xe7, 0xb4, 0xb6, 0xc5, 0xfe, 0xe7, 0xcd, 0xc9,
0xdb, 0xfe, 0x4e, 0x68, 0xd3, 0xb6, 0xb2, 0xbc, 0xfe, 0x68,
0xfe, 0xfe, 0x68, 0x54, 0x68, 0xe7, 0xc5, 0xbc, 0xb8, 0xbe,
0xcd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xd3, 0xd3, 0xcd,
0xc1, 0xb8, 0xbc, 0xdb, 0x4e, 0x42, 0x4a, 0xfe, 0xc9, 0xc1,
0xcd, 0xd3, 0xcd, 0xba, 0xb8, 0xcd, 0x46, 0x3b, 0xfe, 0xc9,
0xba, 0xcd, 0xe7, 0xfe, 0xd3, 0xc1, 0xba, 0xdb, 0x54, 0x3d,
0x68, 0xd3, 0xbc, 0xcd, 0xfe, 0xfe, 0xc5, 0xbe, 0xc1, 0xe7,
0x54, 0x4a, 0xfe, 0xc9, 0xc1, 0xcd, 0xfe, 0xfe, 0xd3, 0xd3,
0xd3, 0xfe, 0xe7, 0xe7, 0xe7, 0xdb, 0xd3, 0xe7, 0xe7, 0xe7,
0xfe, 0xfe, 0xfe, 0xfe, 0xcd, 0xc9, 0xdb, 0xfe, 0xfe, 0xdb,
0xbe, 0xc9, 0xfe, 0x5c, 0xfe, 0xc9, 0xbc, 0xbe, 0xdb, 0x68,
0x5c, 0xdb, 0xc5, 0xd3, 0x54, 0x46, 0xfe, 0xbc, 0xb2, 0xb8,
0xdb, 0x68, 0x68, 0xe7, 0xcd, 0xdb, 0x5c, 0x54, 0xfe, 0xc1,
0xb8, 0xc1, 0xe7, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe,
0xd3, 0xc5, 0xc1, 0xc5, 0xcd, 0xd3, 0xe7, 0xfe, 0x54, 0x4e,
0xfe, 0xd3, 0xcd, 0xd3, 0xd3, 0xc5, 0xc1, 0xc1, 0xe7, 0x5c,
0x4e, 0x5c, 0xd3, 0xc1, 0xcd, 0xfe, 0xfe, 0xcd, 0xba, 0xba,
0xe7, 0x4a, 0x4a, 0x68, 0xcd, 0xc5, 0xcd, 0xfe, 0xfe, 0xcd,
0xb8, 0xc1, 0xe7, 0x4e, 0x5c, 0xe7, 0xc1, 0xc9, 0xdb, 0xfe,
0xe7, 0xc9, 0xc5, 0xd3, 0xfe, 0x68, 0xfe, 0xdb, 0xd3, 0xe7,
0xfe, 0xfe, 0xcd, 0xc9, 0xcd, 0xd3, 0xd3, 0xd3, 0xcd, 0xe7,
0xfe, 0xfe, 0xe7, 0xc5, 0xc5, 0xe7, 0x68, 0x68, 0xe7, 0xc1,
0xc5, 0xfe, 0x5c, 0xfe, 0xd3, 0xc1, 0xd3, 0xfe, 0x68, 0xe7,
0xc5, 0xb6, 0xc5, 0xe7, 0x68, 0xfe, 0xcd, 0xc5, 0xe7, 0xfe,
0x54, 0xfe, 0xc9, 0xc5, 0xdb, 0xfe, 0xfe, 0xfe, 0xd3, 0xd3,
0xfe, 0xfe, 0xfe, 0xcd, 0xc1, 0xc1, 0xc9, 0xd3, 0xd3, 0xe7,
0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xd3, 0xdb, 0xe7, 0xe7, 0xd3,
0xcd, 0xd3, 0xfe, 0xfe, 0xfe, 0xcd, 0xc5, 0xd3, 0xe7, 0xe7,
0xc9, 0xbc, 0xbe, 0xe7, 0x68, 0x4a, 0xfe, 0xdb, 0xcd, 0xfe,
0xfe, 0xfe, 0xcd, 0xc1, 0xc9, 0xfe, 0x54, 0x5c, 0xe7, 0xc9,
0xc5, 0xe7, 0xfe, 0xfe, 0xcd, 0xc5, 0xc5, 0xe7, 0xfe, 0xfe,
0xfe, 0xe7, 0xe7, 0xfe, 0xfe, 0xdb, 0xd3, 0xd3, 0xdb, 0xe7,
0xfe, 0xfe, 0xe7, 0xe7, 0xdb, 0xd3, 0xc9, 0xd3, 0xe7, 0xfe,
0xfe, 0xd3, 0xd3, 0xdb, 0xfe, 0xfe, 0xfe, 0xd3, 0xcd, 0xcd,
0xfe, 0xfe, 0xe7, 0xc9, 0xc5, 0xd3, 0xfe, 0xfe, 0xfe, 0xcd,
0xc9, 0xd3, 0xfe, 0xfe, 0xfe, 0xdb, 0xc9, 0xcd, 0xe7, 0xfe,
0xe7, 0xcd, 0xcd, 0xe7, 0xfe, 0xfe, 0xe7, 0xd3, 0xc5, 0xcd,
0xe7, 0xfe, 0xfe, 0xfe, 0xdb, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe,
0xe7, 0xcd, 0xcd, 0xd3, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe,
0xe7, 0xe7, 0xdb, 0xc9, 0xc1, 0xc5, 0xfe, 0x5c, 0x68, 0xfe,
0xd3, 0xdb, 0xe7, 0xe7, 0xe7, 0xd3, 0xc5, 0xcd, 0xe7, 0x68,
0xfe, 0xe7, 0xcd, 0xd3, 0xe7, 0xfe, 0xe7, 0xcd, 0xc1, 0xc1,
0xdb, 0xfe, 0x54, 0xfe, 0xe7, 0xcd, 0xe7, 0xfe, 0xe7, 0xd3,
0xcd, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xcd, 0xc5, 0xcd, 0xfe,
0xfe, 0xe7, 0xcd, 0xd3, 0xdb, 0xe7, 0xfe, 0xfe, 0xfe, 0xe7,
0xd3, 0xd3, 0xe7, 0xfe, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe,
0xfe, 0xdb, 0xc5, 0xc1, 0xd3, 0xfe, 0xfe, 0xfe, 0xd3, 0xc9,
0xcd, 0xe7, 0xfe, 0xfe, 0xd3, 0xcd, 0xdb, 0xfe, 0x5c, 0xfe,
0xcd, 0xc9, 0xd3, 0xfe, 0xfe, 0xfe, 0xd3, 0xc9, 0xcd, 0xfe,
0x68, 0xfe, 0xd3, 0xc1, 0xc1, 0xdb, 0xfe, 0xfe, 0xe7, 0xe7,
0xfe, 0xfe, 0x68, 0xfe, 0xe7, 0xc5, 0xc9, 0xdb, 0xfe, 0xfe,
0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xdb, 0xc5, 0xc5,
0xd3, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xfe,
0xc9, 0xc1, 0xc5, 0xfe, 0x54, 0x5c, 0xfe, 0xcd, 0xc5, 0xcd,
0xfe, 0xfe, 0xdb, 0xc5, 0xc9, 0xfe, 0x5c, 0x68, 0xfe, 0xcd,
0xcd, 0xfe, 0xfe, 0xfe, 0xe7, 0xc5, 0xc1, 0xd3, 0xfe, 0xfe,
0xdb, 0xc9, 0xc5, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe,
0xfe, 0xfe, 0xe7, 0xcd, 0xcd, 0xdb, 0xfe, 0xfe, 0xfe, 0xfe,
0xe7, 0xd3, 0xcd, 0xd3, 0xfe, 0xfe, 0xdb, 0xcd, 0xd3, 0xe7,
0xfe, 0xfe, 0xfe, 0xdb, 0xcd, 0xd3, 0xe7, 0xfe, 0xd3, 0xc5,
0xc9, 0xfe, 0x5c, 0x54, 0xfe, 0xcd, 0xc1, 0xcd, 0xe7, 0xfe,
0xfe, 0xd3, 0xcd, 0xfe, 0x54, 0x5c, 0xe7, 0xc1, 0xc1, 0xd3,
0xfe, 0xfe, 0xe7, 0xd3, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xcd,
0xc5, 0xcd, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xe7, 0xd3, 0xcd, 0xc9, 0xcd, 0xe7, 0xfe, 0xfe, 0xfe, 0xdb,
0xc9, 0xcd, 0xe7, 0xfe, 0xe7, 0xc9, 0xc5, 0xdb, 0xfe, 0x5c,
0xfe, 0xe7, 0xcd, 0xcd, 0xe7, 0xfe, 0xe7, 0xc5, 0xc1, 0xd3,
0xfe, 0x5c, 0xfe, 0xcd, 0xc5, 0xcd, 0xe7, 0xfe, 0xfe, 0xe7,
0xd3, 0xe7, 0xfe, 0xfe, 0xe7, 0xcd, 0xcd, 0xdb, 0xfe, 0xfe,
0xfe, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xe7, 0xdb, 0xcd, 0xd3,
0xd3, 0xdb, 0xfe, 0xfe, 0xfe, 0xfe, 0xdb, 0xd3, 0xdb, 0xe7,
0xe7, 0xdb, 0xd3, 0xe7, 0xfe, 0xfe, 0xfe, 0xe7, 0xc9, 0xc5,
0xcd, 0xe7, 0xfe, 0xdb, 0xd3, 0xe7, 0xfe, 0x68, 0xfe, 0xe7,
0xcd, 0xcd, 0xd3, 0xfe, 0xfe, 0xe7, 0xdb, 0xe7, 0xfe, 0x68,
0xfe, 0xdb, 0xfe, 0x68, 0xbe, 0xb2, 0xae, 0xab, 0xb2, 0xfe,
0x2f, 0x31, 0xdb, 0xac, 0xad, 0xaf, 0xab, 0xab, 0xb4, 0x68,
0x37, 0x39, 0x3f, 0xe7, 0xb4, 0xa8, 0xaa, 0xb0, 0xbc, 0xbc,
0xc5, 0x3f, 0x31, 0x3d, 0xfe, 0xc1, 0xb8, 0xb0, 0xa5, 0xa2,
0xa8, 0xaf, 0xdb, 0x3b, 0x28, 0x2a, 0x3d, 0xbc, 0xb0, 0xaa,
0xa2, 0x9f, 0xab, 0xfe, 0x29, 0x24, 0x29, 0x4a, 0xb4, 0xaa,
0xa8, 0xa9, 0xa8, 0xa5, 0xac, 0xdb, 0x2c, 0x27, 0x35, 0x4a,
0xfe, 0xcd, 0xb2, 0xa2, 0x9f, 0x9f, 0xae, 0x4e, 0x2c, 0x22,
0x33, 0xfe, 0xba, 0xb0, 0xa6, 0x9f, 0xa3, 0xbc, 0x5c, 0x33,
0x31, 0x39, 0x5c, 0xcd, 0xc1, 0xb4, 0xad, 0xaa, 0xad, 0xcd,
0x54, 0x46, 0xfe, 0xe7, 0xfe, 0x54, 0xc5, 0xaf, 0xa6, 0xa9,
0xb0, 0xfe, 0x3d, 0x39, 0x5c, 0xdb, 0xdb, 0xfe, 0xba, 0xac,
0xa8, 0xc5, 0x39, 0x33, 0x54, 0xb8, 0xae, 0xad, 0xb8, 0xc1,
0xcd, 0xbe, 0xbc, 0xfe, 0x39, 0x37, 0xfe, 0xb4, 0xba, 0xcd,
0xdb, 0xb8, 0xb0, 0xb6, 0xfe, 0x4e, 0x39, 0x3d, 0xfe, 0xb0,
0xaa, 0xa9, 0xa9, 0xaa, 0xb0, 0x5c, 0x29, 0x28, 0x31, 0xfe,
0xba, 0xb4, 0xae, 0xab, 0xab, 0xb2, 0xfe, 0x3b, 0x2a, 0x2f,
0x54, 0xb4, 0xb4, 0xba, 0xb2, 0xa3, 0x9f, 0xa8, 0xfe, 0x2c,
0x27, 0x2a, 0x46, 0xfe, 0xc1, 0xbc, 0xb0, 0xa2, 0xa2, 0xb0,
0x33, 0x22, 0x2b, 0x46, 0xc1, 0xb4, 0xb0, 0xab, 0xa8, 0xa8,
0xb0, 0xdb, 0x42, 0x2c, 0x33, 0x4a, 0xfe, 0x5c, 0xdb, 0xb4,
0xa8, 0xad, 0xba, 0xfe, 0x46, 0x39, 0x39, 0x4a, 0xfe, 0xbc,
0xab, 0xa5, 0xa5, 0xb8, 0x68, 0x37, 0x4a, 0xe7, 0xfe, 0x4a,
0x5c, 0xd3, 0xb0, 0xb2, 0xc1, 0x5c, 0x42, 0x46, 0xd3, 0xb4,
0xbe, 0x54, 0x54, 0xb6, 0xab, 0xae, 0xe7, 0x46, 0x4a, 0xfe,
0xcd, 0xc9, 0xd3, 0xe7, 0xe7, 0xbe, 0xb4, 0xc5, 0x37, 0x2c,
0x37, 0xc1, 0xb0, 0xb2, 0xb4, 0xb2, 0xb6, 0xdb, 0x54, 0x4a,
0x46, 0x42, 0x68, 0xba, 0xb2, 0xba, 0xc5, 0xb6, 0xb6, 0xcd,
0x33, 0x2f, 0x39, 0x68, 0xfe, 0xe7, 0xba, 0xac, 0xa7, 0xa7,
0xb2, 0xe7, 0x2d, 0x25, 0x2f, 0xd3, 0xbe, 0xd3, 0xc5, 0xac,
0xa6, 0xac, 0xfe, 0x33, 0x2c, 0x2d, 0x54, 0xc1, 0xb4, 0xcd,
0xcd, 0xaf, 0xa4, 0xa8, 0xe7, 0x31, 0x31, 0x39, 0x46, 0xfe,
0xdb, 0xbc, 0xb6, 0xaf, 0xa9, 0xb2, 0xfe, 0x2f, 0xfe, 0xba,
0xad, 0xba, 0x4e, 0x3d, 0x42, 0xfe, 0xc9, 0xc1, 0xe7, 0xfe,
0xc1, 0xa9, 0xa6, 0xcd, 0x2a, 0x25, 0x3b, 0xbc, 0xb0, 0xb6,
0xb8, 0xb4, 0xb4, 0xb8, 0xc1, 0xfe, 0x3d, 0x3d, 0xfe, 0xba,
0xd3, 0x54, 0x54, 0xbe, 0xb0, 0xb4, 0xe7, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xc5, 0xb6, 0xa9, 0xaa, 0xba, 0x3d, 0x39,
0xfe, 0xc1, 0xfe, 0x4a, 0x54, 0xbe, 0xb2, 0xb8, 0xfe, 0x3d,
0x3b, 0xfe, 0xb0, 0xab, 0xc5, 0x39, 0x3b, 0xbc, 0xa7, 0xab,
0xd3, 0x68, 0xfe, 0xd3, 0xcd, 0xfe, 0x54, 0x3d, 0xfe, 0xbc,
0xac, 0xc9, 0x3f, 0x2e, 0xfe, 0xba, 0xb0, 0xba, 0xba, 0xb6,
0xba, 0xd3, 0xfe, 0xfe, 0x5c, 0x54, 0xc9, 0xb4, 0xbe, 0x54,
0x68, 0xcd, 0xb6, 0xfe, 0x46, 0x54, 0xcd, 0xc5, 0xdb, 0xfe,
0xe7, 0xc1, 0xae, 0xa8, 0xac, 0xfe, 0x2e, 0x2e, 0xfe, 0xb6,
0xc1, 0x5c, 0xe7, 0xb6, 0xaa, 0xb0, 0x54, 0x35, 0x2e, 0x4a,
0xc1, 0xac, 0xbc, 0xfe, 0xfe, 0xaf, 0xa6, 0xac, 0xfe, 0x42,
0x46, 0x5c, 0xfe, 0xfe, 0xfe, 0xe7, 0xc9, 0xae, 0xa9, 0xb0,
0x54, 0x31, 0x37, 0xfe, 0xd3, 0xd3, 0xb8, 0xac, 0xab, 0xb6,
0xe7, 0xfe, 0xfe, 0x68, 0xfe, 0xfe, 0xfe, 0x54, 0xfe, 0xb0,
0xae, 0xb8, 0xfe, 0xe7, 0xc1, 0xb6, 0xe7, 0x46, 0x3d, 0xe7,
0xae, 0xa7, 0xab, 0xdb, 0x3f, 0x37, 0xfe, 0xba, 0xcd, 0x3f,
0x42, 0xc5, 0xab, 0xac, 0xc9, 0x46, 0x3d, 0x54, 0xba, 0xad,
0xb0, 0xfe, 0x68, 0xcd, 0xb0, 0xb0, 0xc9, 0x54, 0x54, 0xfe,
0xfe, 0xfe, 0xfe, 0xe7, 0xcd, 0xbe, 0xba, 0xc5, 0x68, 0x42,
0x46, 0xe7, 0xc5, 0xba, 0xaf, 0xa7, 0xa7, 0xbc, 0xfe, 0x3d,
0x4a, 0x68, 0xfe, 0xfe, 0xfe, 0xe7, 0xbc, 0xaf, 0xae, 0xc5,
0x3d, 0x39, 0xfe, 0xbc, 0xbe, 0xfe, 0x68, 0xc9, 0xa9, 0xa2,
0xaa, 0xbc, 0x3b, 0x2d, 0x35, 0xfe, 0xcd, 0xfe, 0xfe, 0xb4,
0xa9, 0xa6, 0xbc, 0x54, 0x31, 0x31, 0x54, 0xd3, 0xbc, 0xc5,
0xcd, 0xb8, 0xab, 0xa8, 0xb4, 0xfe, 0x54, 0x4e, 0x68, 0x54,
0xfe, 0xc9, 0xba, 0xb4, 0xb4, 0xba, 0xcd, 0x5c, 0x3b, 0x3f,
0x54, 0xfe, 0xcd, 0xaf, 0xa8, 0xac, 0xc5, 0x68, 0xfe, 0xe7,
0xdb, 0xfe, 0xfe, 0xfe, 0xcd, 0xb8, 0xaf, 0xb6, 0xe7, 0x42,
0x5c, 0xcd, 0xbc, 0xfe, 0x46, 0x68, 0xba, 0xb0, 0xab, 0xbc,
0x54, 0x3d, 0x46, 0xc9, 0xb8, 0xbc, 0xdb, 0xd3, 0xb6, 0xb0,
0xb6, 0x5c, 0x37, 0x35, 0x54, 0xc9, 0xba, 0xb4, 0xc1, 0xc9,
0xc1, 0xba, 0xe7, 0x5c, 0x3d, 0x54, 0xfe, 0xcd, 0xc5, 0xb8,
0xae, 0xaf, 0xb4, 0xd3, 0x54, 0x3b, 0x35, 0x46, 0xfe, 0xdb,
0xbc, 0xaf, 0xa9, 0xab, 0xd3, 0x3f, 0x31, 0x3f, 0xfe, 0xe7,
0xdb, 0xcd, 0xb8, 0xae, 0xaa, 0xac, 0xe7, 0x33, 0x2d, 0x3f,
0xd3, 0xdb, 0xfe, 0xfe, 0xbc, 0xaa, 0xa9, 0xb0, 0xfe, 0x31,
0x2f, 0x3d, 0xdb, 0xc5, 0xcd, 0xc9, 0xae, 0xa8, 0xad, 0xfe,
0x46, 0x39, 0x46, 0x5c, 0xfe, 0xcd, 0xc5, 0xc1, 0xb6, 0xb0,
0xbc, 0x68, 0x42, 0x4e, 0xe7, 0xfe, 0x5c, 0xfe, 0xb6, 0xaf,
0xb0, 0xc5, 0xe7, 0x5c, 0x5c, 0xfe, 0xe7, 0xfe, 0x68, 0xe7,
0xb0, 0xac, 0xb2, 0x4a, 0x35, 0x3f, 0xcd, 0xbc, 0xc1, 0xe7,
0xe7, 0xd3, 0xb6, 0xb4, 0xfe, 0x3b, 0x33, 0xfe, 0xba, 0xb4,
0xd3, 0xfe, 0xc9, 0xb4, 0xb4, 0xcd, 0x4a, 0x3b, 0x3b, 0xfe,
0xb8, 0xb6, 0xc5, 0xc5, 0xb8, 0xb0, 0xcd, 0x4a, 0x31, 0x3b,
0x68, 0xcd, 0xc1, 0xba, 0xb4, 0xb0, 0xb0, 0xba, 0x5c, 0x2f,
0x2f, 0x4e, 0xc9, 0xc1, 0xdb, 0xc9, 0xb4 };

207
trunk/apps/leave.h Normal file
View File

@@ -0,0 +1,207 @@
/*
* U-law 8-bit audio data
*
* Source: leave.raw
*
* Copyright (C) 1999, Mark Spencer and Linux Support Services
*
* Distributed under the terms of the GNU General Public License
*
*/
static unsigned char leave[] = {
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xc1, 0x3d,
0x42, 0x46, 0x3f, 0x3f, 0x46, 0x3f, 0x4e, 0xba, 0xbe, 0xbe,
0xbc, 0xba, 0xbe, 0xc5, 0xb6, 0x2e, 0x2c, 0x33, 0x2f, 0x2e,
0x2f, 0x33, 0x2b, 0x54, 0xac, 0xb0, 0xb0, 0xad, 0xaf, 0xb0,
0xae, 0xcd, 0x3b, 0x2f, 0x31, 0x2e, 0x2f, 0x31, 0x2e, 0x46,
0xc5, 0xaf, 0xb0, 0xaf, 0xae, 0xaf, 0xaf, 0xb0, 0xfe, 0x2d,
0x31, 0x31, 0x2e, 0x31, 0x2f, 0x31, 0xfe, 0xae, 0xaf, 0xaf,
0xae, 0xb0, 0xae, 0xaf, 0xfe, 0xdb, 0x2e, 0x2e, 0x31, 0x31,
0x2d, 0x2e, 0xdb, 0x68, 0xaf, 0xad, 0xb0, 0xb0, 0xae, 0xaf,
0x5c, 0xe7, 0x39, 0x2d, 0x31, 0x31, 0x31, 0x2d, 0xfe, 0xfe,
0x68, 0xad, 0xaf, 0xb0, 0xaf, 0xac, 0xbc, 0xfe, 0xd3, 0x2f,
0x2e, 0x33, 0x31, 0x2d, 0x4e, 0xdb, 0xfe, 0xfe, 0xac, 0xaf,
0xb0, 0xac, 0xb6, 0x68, 0xe7, 0xdb, 0x2e, 0x2f, 0x35, 0x2f,
0x31, 0xe7, 0xe7, 0x68, 0xad, 0xac, 0xb0, 0xae, 0xac, 0xfe,
0xfe, 0xdb, 0xfe, 0x2d, 0x33, 0x31, 0x2e, 0xfe, 0xfe, 0xfe,
0xfe, 0xbc, 0xaf, 0xb0, 0xad, 0xfe, 0xfe, 0xfe, 0xe7, 0x5c,
0x2e, 0x33, 0x2e, 0x35, 0xe7, 0xfe, 0xfe, 0xfe, 0xad, 0xb0,
0xaf, 0xc1, 0xfe, 0xe7, 0xfe, 0xe7, 0x3d, 0x31, 0x2f, 0x37,
0xe7, 0xfe, 0xfe, 0xe7, 0xfe, 0xaf, 0xad, 0xbe, 0xfe, 0xdb,
0xfe, 0xfe, 0xdb, 0x35, 0x2d, 0x39, 0xdb, 0xfe, 0xfe, 0xdb,
0xfe, 0xfe, 0xad, 0xaf, 0xfe, 0xfe, 0xe7, 0x68, 0xfe, 0xd3,
0x2e, 0x2c, 0xdb, 0xdb, 0x2c, 0x35, 0xd3, 0x68, 0xaf, 0xad,
0xb0, 0xb0, 0xad, 0xba, 0x68, 0xe7, 0xe7, 0x2e, 0x2f, 0x33,
0x31, 0x2d, 0xdb, 0xd3, 0x5c, 0xae, 0xaa, 0xe7, 0x68, 0xaa,
0xe7, 0xfe, 0xdb, 0xe7, 0xfe, 0xe7, 0xd3, 0x2d, 0xfe, 0xdb,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xc5, 0xfe, 0xe7, 0xe7,
0xfe, 0xfe, 0xe7, 0xe7, 0x3b, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xc5, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7, 0xfe, 0x3b,
0xdb, 0xfe, 0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xb0, 0xfe, 0xfe,
0xe7, 0xfe, 0xfe, 0xfe, 0xdb, 0x2e, 0x5c, 0xdb, 0xfe, 0xfe,
0xe7, 0xe7, 0x68, 0xb0, 0xbe, 0x68, 0xe7, 0xe7, 0xfe, 0xfe,
0xdb, 0x39, 0x2f, 0xdb, 0xfe, 0xfe, 0xe7, 0xe7, 0xfe, 0xbe,
0xaf, 0xe7, 0x68, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0x33, 0x33,
0xdb, 0xfe, 0xfe, 0xdb, 0xe7, 0xfe, 0xb0, 0xb0, 0xfe, 0xfe,
0xe7, 0xfe, 0xfe, 0xfe, 0x35, 0x33, 0xe7, 0xe7, 0xfe, 0xe7,
0xe7, 0xfe, 0xb0, 0xb2, 0xb0, 0xfe, 0xfe, 0xe7, 0xfe, 0xe7,
0x46, 0x35, 0x35, 0x3f, 0xe7, 0xfe, 0xe7, 0xfe, 0xb2, 0xb0,
0xb2, 0xb0, 0xfe, 0xfe, 0xfe, 0xfe, 0x42, 0x35, 0x37, 0x33,
0xe7, 0xfe, 0xfe, 0xfe, 0xb8, 0xb0, 0xb6, 0xb0, 0xba, 0xfe,
0xfe, 0xe7, 0xe7, 0x33, 0x39, 0x39, 0x33, 0xe7, 0xdb, 0xfe,
0xe7, 0xb0, 0xb4, 0xb6, 0xb0, 0xcd, 0xfe, 0xe7, 0xe7, 0x33,
0x39, 0x3b, 0x33, 0x46, 0xd3, 0xfe, 0xfe, 0xb0, 0xb2, 0xb6,
0xb4, 0xb0, 0xfe, 0xfe, 0xdb, 0x35, 0x37, 0x39, 0x39, 0x35,
0x37, 0xdb, 0x68, 0xcd, 0xb2, 0xb6, 0xb6, 0xb4, 0xb4, 0x68,
0xe7, 0x42, 0x37, 0x3b, 0x3b, 0x39, 0x37, 0xdb, 0xfe, 0xcd,
0xb2, 0xb6, 0xb6, 0xb6, 0xb2, 0xb4, 0xfe, 0x54, 0x37, 0x3b,
0x39, 0x3b, 0x3b, 0x39, 0xe7, 0xfe, 0xb6, 0xb6, 0xb6, 0xb4,
0xb6, 0xb6, 0xbc, 0xfe, 0x3f, 0x3b, 0x3b, 0x39, 0x3b, 0x3b,
0x39, 0xe7, 0xb6, 0xb8, 0xb8, 0xb6, 0xb8, 0xb8, 0xb4, 0xfe,
0x3b, 0x3d, 0x3d, 0x3b, 0x39, 0x3d, 0x3b, 0x39, 0xbe, 0xb8,
0xba, 0xb8, 0xb6, 0xb8, 0xba, 0xb4, 0xfe, 0x39, 0x3f, 0x3d,
0x3b, 0x3d, 0x3f, 0x39, 0xdb, 0xb4, 0xba, 0xb8, 0xb6, 0xb8,
0xbc, 0xb4, 0xba, 0x39, 0x42, 0x3f, 0x3d, 0x3d, 0x3f, 0x3f,
0x3b, 0xb8, 0xb6, 0xbc, 0xb8, 0xb8, 0xba, 0xbc, 0xb8, 0xe7,
0x3d, 0x42, 0x3f, 0x3d, 0x3f, 0x42, 0x3d, 0xfe, 0xb8, 0xbc,
0xbc, 0xba, 0xba, 0xbc, 0xba, 0xe7, 0x3d, 0x3f, 0x42, 0x3f,
0x3f, 0x42, 0x42, 0xfe, 0xfe, 0xbc, 0xbc, 0xbe, 0xbc, 0xbe,
0xbc, 0xc5, 0xe7, 0x68, 0x42, 0x46, 0x42, 0x46, 0x42, 0x46,
0xfe, 0xfe, 0xbc, 0xbe, 0xbe, 0xbe, 0xbc, 0xc5, 0xfe, 0xdb,
0x46, 0x46, 0x4a, 0x4a, 0x46, 0x46, 0xe7, 0xfe, 0xd3, 0xbe,
0xc9, 0xc9, 0xc5, 0xc5, 0xe7, 0xdb, 0xd3, 0x4a, 0x4e, 0x54,
0x4e, 0x4e, 0xfe, 0x5c, 0x54, 0xd3, 0xcd, 0xd3, 0xd3, 0xcd,
0xd3, 0xd3, 0xcd, 0xfe, 0x5c, 0x68, 0x5c, 0x5c, 0x5c, 0x68,
0x5c, 0x5c, 0xcd, 0xcd, 0xd3, 0xcd, 0xdb, 0xe7, 0xe7, 0xdb,
0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xe7,
0xfe, 0x5c, 0x5c, 0xfe, 0xfe, 0xfe, 0xfe, 0x46, 0x35, 0x35,
0x37, 0x39, 0x3b, 0x39, 0x35, 0x33, 0x35, 0x5c, 0xd3, 0xcd,
0xdb, 0xfe, 0xfe, 0xd3, 0xb0, 0xb0, 0xb0, 0xb4, 0xb4, 0xb6,
0xb2, 0xb0, 0xb0, 0xb6, 0xcd, 0x5c, 0x68, 0xfe, 0xfe, 0xfe,
0x3b, 0x33, 0x35, 0x37, 0x39, 0x3b, 0x39, 0x37, 0x35, 0x35,
0x3f, 0xdb, 0xcd, 0xcd, 0xdb, 0xfe, 0xe7, 0xc5, 0xb0, 0xb0,
0xb2, 0xb6, 0xb6, 0xb6, 0xb2, 0xb0, 0xb0, 0xcd, 0x5c, 0x5c,
0xfe, 0xe7, 0xe7, 0xfe, 0x35, 0x35, 0x35, 0x39, 0x3b, 0x3b,
0x39, 0x35, 0x33, 0x39, 0xdb, 0xcd, 0xd3, 0xe7, 0xfe, 0xfe,
0xba, 0xb0, 0xb0, 0xb2, 0xb4, 0xb6, 0xb6, 0xb4, 0xb2, 0xb0,
0xb4, 0xc9, 0x5c, 0x68, 0xfe, 0xfe, 0x5c, 0x3b, 0x35, 0x37,
0x39, 0x3b, 0x3b, 0x3b, 0x39, 0x37, 0x37, 0x3d, 0xe7, 0xcd,
0xdb, 0xfe, 0xe7, 0xbe, 0xb2, 0xb2, 0xb4, 0xb4, 0xb6, 0xb6,
0xb6, 0xb4, 0xb0, 0xb0, 0xc5, 0x5c, 0x5c, 0xfe, 0xe7, 0xe7,
0x4e, 0x35, 0x35, 0x37, 0x3b, 0x3b, 0x3b, 0x39, 0x37, 0x37,
0x3b, 0xe7, 0xc9, 0xcd, 0xe7, 0xfe, 0xd3, 0xb4, 0xb2, 0xb2,
0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb4, 0xb2, 0xb4, 0xc1, 0x68,
0x68, 0xfe, 0xfe, 0x42, 0x39, 0x37, 0x39, 0x3b, 0x3b, 0x3b,
0x3b, 0x3b, 0x39, 0x37, 0x3b, 0xfe, 0xd3, 0xdb, 0xfe, 0xcd,
0xb4, 0xb2, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb4, 0xb2,
0xb2, 0xc1, 0x5c, 0x5c, 0xfe, 0xfe, 0xfe, 0x3d, 0x37, 0x37,
0x39, 0x3b, 0x3b, 0x3b, 0x3b, 0x37, 0x37, 0x39, 0xfe, 0xcd,
0xd3, 0xfe, 0xfe, 0xc1, 0xb2, 0xb2, 0xb4, 0xb6, 0xb6, 0xb6,
0xb6, 0xb6, 0xb6, 0xb4, 0xb4, 0xbc, 0x68, 0xfe, 0xfe, 0xfe,
0x3b, 0x39, 0x39, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x39,
0x39, 0x3b, 0xfe, 0xdb, 0xe7, 0xfe, 0xbc, 0xb6, 0xb6, 0xb6,
0xb8, 0xb6, 0xb6, 0xb6, 0xb8, 0xb6, 0xb4, 0xb4, 0xbc, 0xfe,
0x68, 0xfe, 0xe7, 0x5c, 0x3b, 0x39, 0x39, 0x3b, 0x3b, 0x3b,
0x3d, 0x3d, 0x3b, 0x39, 0x3b, 0x68, 0xdb, 0xdb, 0xfe, 0xe7,
0xb8, 0xb6, 0xb6, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb6,
0xb4, 0xb6, 0xdb, 0x68, 0xfe, 0xfe, 0x46, 0x3b, 0x3b, 0x3b,
0x3d, 0x3d, 0x3b, 0x3d, 0x3d, 0x3d, 0x3d, 0x3b, 0x3b, 0x5c,
0xdb, 0xdb, 0xc9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xbc, 0xcd, 0xfe, 0xfe, 0x3d,
0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3b, 0x3d, 0x3d, 0x3d, 0x3d,
0x3b, 0x3d, 0x46, 0xfe, 0xe7, 0xe7, 0xc5, 0xb8, 0xb8, 0xb8,
0xba, 0xba, 0xb8, 0xb8, 0xba, 0xba, 0xb8, 0xb8, 0xb8, 0xcd,
0xfe, 0xfe, 0x68, 0x3f, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
0x3d, 0x3d, 0x3f, 0x3f, 0x3d, 0x3b, 0x4a, 0xfe, 0xdb, 0xbc,
0xb8, 0xba, 0xba, 0xba, 0xba, 0xb8, 0xb8, 0xb8, 0xba, 0xba,
0xba, 0xba, 0xba, 0xc5, 0xfe, 0x54, 0x3f, 0x3f, 0x3f, 0x3f,
0x3f, 0x3f, 0x3d, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x42,
0xfe, 0xe7, 0xdb, 0xbc, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,
0xba, 0xba, 0xbc, 0xba, 0xba, 0xba, 0xc5, 0xfe, 0xfe, 0x4e,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x42,
0x42, 0x42, 0x3f, 0x46, 0xfe, 0xcd, 0xb8, 0xba, 0xbc, 0xbc,
0xbc, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xba, 0xb8,
0xbe, 0xfe, 0x42, 0x3d, 0x3f, 0x42, 0x42, 0x42, 0x3f, 0x3f,
0x3f, 0x42, 0x42, 0x42, 0x42, 0x3f, 0x3f, 0x68, 0xdb, 0xc5,
0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xba, 0xba, 0xba, 0xbc, 0xbc,
0xbc, 0xbc, 0xba, 0xc1, 0xfe, 0xfe, 0x3f, 0x42, 0x46, 0x46,
0x46, 0x42, 0x42, 0x42, 0x42, 0x42, 0x46, 0x46, 0x42, 0x3f,
0x42, 0x68, 0xbe, 0xba, 0xbc, 0xbe, 0xbe, 0xbe, 0xbc, 0xbc,
0xbc, 0xbc, 0xbe, 0xc1, 0xbe, 0xbc, 0xba, 0xbe, 0x68, 0x3f,
0x42, 0x46, 0x4a, 0x4a, 0x46, 0x42, 0x42, 0x42, 0x46, 0x46,
0x46, 0x46, 0x42, 0x42, 0x68, 0xd3, 0xbc, 0xbc, 0xbe, 0xc1,
0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xc1, 0xc1, 0xbe, 0xbe,
0xc1, 0xfe, 0x4e, 0x42, 0x46, 0x4a, 0x4a, 0x4a, 0x46, 0x46,
0x46, 0x46, 0x4a, 0x4a, 0x4a, 0x46, 0x46, 0x68, 0xdb, 0xbe,
0xbe, 0xc1, 0xc5, 0xc1, 0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xc1,
0xc5, 0xc5, 0xbe, 0xbc, 0xc1, 0x4e, 0x46, 0x46, 0x4a, 0x4e,
0x4e, 0x4a, 0x46, 0x46, 0x46, 0x4a, 0x4a, 0x4e, 0x4a, 0x46,
0x46, 0xfe, 0xbe, 0xbe, 0xc1, 0xc9, 0xc5, 0xc5, 0xc1, 0xc1,
0xc1, 0xc1, 0xc5, 0xc5, 0xc5, 0xc5, 0xbe, 0xc1, 0xfe, 0x4a,
0x4a, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4a, 0x4a, 0x4a, 0x4e,
0x54, 0x4e, 0x4a, 0x4a, 0x4e, 0xcd, 0xc1, 0xc5, 0xc5, 0xc9,
0xc5, 0xc5, 0xc5, 0xc5, 0xc9, 0xcd, 0xcd, 0xcd, 0xcd, 0xc9,
0xc9, 0xd3, 0x68, 0x54, 0x5c, 0x68, 0x68, 0x68, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x68, 0x68, 0x5c, 0x54, 0x5c, 0xdb, 0xcd,
0xcd, 0xdb, 0xdb, 0xdb, 0xdb, 0xd3, 0xd3, 0xe7, 0xe7, 0xe7,
0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xe7, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
0xfe, 0xfe, 0xfe };

BIN
trunk/apps/rpt_flow.pdf Normal file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More