Imported in new CVS
git-svn-id: http://yate.null.ro/svn/yate/trunk@2 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
e0f55ab398
commit
628070ee2e
|
@ -0,0 +1,19 @@
|
|||
*.cache
|
||||
Makefile
|
||||
configure
|
||||
config.*
|
||||
yatepaths.h
|
||||
run
|
||||
yate.spec
|
||||
yate
|
||||
yate-config
|
||||
yate.pc
|
||||
core*
|
||||
yate.core*
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.yate
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, 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., 675 Mass Ave, Cambridge, MA 02139, 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.
|
|
@ -0,0 +1,39 @@
|
|||
Sat May 15 2004 Paul Chitescu <paulc-devel@null.ro>
|
||||
- Added pkgconfig support
|
||||
- Improved detection of Postgress' include file path
|
||||
- Support for detecting libraries required for SIP
|
||||
- Better detection of OpenH323 versions
|
||||
|
||||
Sat May 15 2004 Diana Cionoiu <diana@diana.null.ro>
|
||||
- Added SIP channel and registration module
|
||||
|
||||
Wed Apr 28 2004 Paul Chitescu <paulc-devel@null.ro>
|
||||
- Version 0.8.1
|
||||
- Fixed data law selection on Zaptel
|
||||
- Preventing vi swap and backup files from polluting the tarballs
|
||||
|
||||
Mon Apr 26 2004 Paul Chitescu <paulc-devel@null.ro>
|
||||
- Fixed a fatal 16->8 bit conversion bug in DataBlock::convert()
|
||||
- Added valgrind support to the run script
|
||||
|
||||
Sat Apr 11 2004 Paul Chitescu <paulc-devel@null.ro>
|
||||
- Moved hash capabilities to the String class so HString was removed
|
||||
- String encoding and decoding methods for messages
|
||||
- Slightly more useful RManager
|
||||
|
||||
Sun Apr 04 2004 Paul Chitescu <paulc-devel@null.ro>
|
||||
- Added an yate-config script
|
||||
|
||||
Sat Apr 03 2004 Paul Chitescu <paulc-devel@null.ro>
|
||||
- Turned some redundant strings from makefiles into variables
|
||||
- Applied some patches submitted by Cristian Andrei Calin <kman@tfm.ro> :
|
||||
- Patches to allow compiling under NetBSD and FreeBSD
|
||||
- Patch to add -fPIC which supposedly fixes the dlclose() bug in *BSD
|
||||
|
||||
Fri Apr 02 2004 Paul Chitescu <paulc-devel@null.ro>
|
||||
- Imported into new CVS
|
||||
|
||||
Mon Mar 29 2004 Paul Chitescu <paulc-devel@null.ro>
|
||||
- Version 0.8.0
|
||||
- Redesigned the build system so it works trough autoconf
|
||||
- Added a RPM specfile - tested on RedHat 7.1
|
|
@ -0,0 +1,209 @@
|
|||
# Makefile
|
||||
# This file holds the make rules for the Telephony Engine
|
||||
|
||||
# override DESTDIR at install time to prefix the install directory
|
||||
DESTDIR :=
|
||||
|
||||
# override DEBUG at compile time to enable full debug or remove it all
|
||||
DEBUG :=
|
||||
|
||||
CC := g++ -Wall
|
||||
SED := sed
|
||||
DEFS :=
|
||||
LIBAUX:= -ldl -lpthread
|
||||
INCLUDES := -I@srcdir@
|
||||
CFLAGS := -O2 @MODULE_CFLAGS@ -finline -Winline
|
||||
LDFLAGS:=
|
||||
|
||||
PROGS:= yate
|
||||
SLIBS:= libyate.so
|
||||
INCS := telengine.h telephony.h yateversn.h
|
||||
LIBS :=
|
||||
MAN8 := yate.8
|
||||
DOCS := README COPYING ChangeLog
|
||||
ENGOBJS := TelEngine.o String.o DataBlock.o ObjList.o \
|
||||
NamedList.o Configuration.o \
|
||||
Message.o Mutex.o Thread.o \
|
||||
Plugin.o Engine.o
|
||||
OBJS := main.o
|
||||
|
||||
LIBOBJS := $(ENGOBJS) $(TELOBJS)
|
||||
CLEANS = $(PROGS) $(SLIBS) $(LIBS) $(OBJS) $(LIBOBJS) core
|
||||
COMPILE = $(CC) $(DEFS) $(DEBUG) $(INCLUDES) $(CFLAGS)
|
||||
LINK = $(CC) $(LDFLAGS)
|
||||
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
|
||||
bindir = @bindir@
|
||||
libdir = @libdir@
|
||||
incdir = @includedir@/yate
|
||||
mandir = @mandir@
|
||||
docdir = $(prefix)/share/doc/yate-@PACKAGE_VERSION@
|
||||
vardir = @localstatedir@/lib/yate
|
||||
moddir = @libdir@/yate
|
||||
confdir = @sysconfdir@/yate
|
||||
|
||||
.PHONY: all
|
||||
all: engine modules
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-rm $(CLEANS) 2>/dev/null
|
||||
$(MAKE) -C ./modules $@
|
||||
$(MAKE) -C ./test $@
|
||||
|
||||
.PHONY: engine
|
||||
engine: tables yatepaths.h $(LIBS) $(SLIBS) $(PROGS)
|
||||
|
||||
.PHONY: apidocs
|
||||
apidocs: check-topdir
|
||||
kdoc -d docs/api/ $(INCS)
|
||||
|
||||
.PHONY: strip
|
||||
strip: all
|
||||
-strip --strip-debug --discard-locals $(PROGS) $(SLIBS)
|
||||
|
||||
.PHONY: sex
|
||||
sex: strip
|
||||
@echo 'Stripped for you!'
|
||||
|
||||
.PHONY: love
|
||||
love:
|
||||
@echo 'Not war?'
|
||||
|
||||
.PHONY: run
|
||||
run: all
|
||||
-./run
|
||||
|
||||
.PHONY: gdb-run
|
||||
gdb-run: all
|
||||
-./run --gdb
|
||||
|
||||
.PHONY: gdb-core
|
||||
gdb-core: all
|
||||
-./run --core
|
||||
|
||||
.PHONY: test
|
||||
test: engine
|
||||
$(MAKE) -C ./test all
|
||||
|
||||
.PHONY: modules
|
||||
modules: engine
|
||||
$(MAKE) -C ./modules all
|
||||
|
||||
tables: @srcdir@/tables/all.h
|
||||
|
||||
@srcdir@/tables/all.h:
|
||||
$(MAKE) -C @srcdir@/tables -f Makefile.tables all
|
||||
|
||||
yatepaths.h:
|
||||
@echo -en '#define MOD_PATH "$(DESTDIR)$(moddir)"\n#define CFG_PATH "$(DESTDIR)$(confdir)"\n' > $@
|
||||
|
||||
.PHONY: everything
|
||||
everything: all test
|
||||
|
||||
.PHONY: install
|
||||
install: all
|
||||
@mkdir -p "$(DESTDIR)$(libdir)/" && \
|
||||
install $(SLIBS) "$(DESTDIR)$(libdir)/" && ldconfig
|
||||
@mkdir -p "$(DESTDIR)$(bindir)/" && \
|
||||
install $(PROGS) yate-config "$(DESTDIR)$(bindir)/"
|
||||
@mkdir -p "$(DESTDIR)$(libdir)/pkgconfig/" && \
|
||||
install yate.pc "$(DESTDIR)$(libdir)/pkgconfig/"
|
||||
@mkdir -p "$(DESTDIR)$(incdir)/" && \
|
||||
for i in $(INCS) ; do \
|
||||
install -m 0644 @srcdir@/$$i "$(DESTDIR)$(incdir)/" ; \
|
||||
done
|
||||
@mkdir -p "$(DESTDIR)$(mandir)/man8/" && \
|
||||
for i in $(MAN8) ; do \
|
||||
install -m 0644 @srcdir@/$$i "$(DESTDIR)$(mandir)/man8/" ; \
|
||||
done
|
||||
@mkdir -p "$(DESTDIR)$(docdir)/api/" && \
|
||||
for i in $(DOCS) ; do \
|
||||
install -m 0644 @srcdir@/$$i "$(DESTDIR)$(docdir)/" ; \
|
||||
done ;
|
||||
install -m 0644 @srcdir@/docs/*.html "$(DESTDIR)$(docdir)/" && \
|
||||
install -m 0644 @srcdir@/docs/api/*.html "$(DESTDIR)$(docdir)/api/"
|
||||
$(MAKE) -C ./modules $@
|
||||
$(MAKE) -C ./conf.d $@
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall:
|
||||
@-for i in $(SLIBS) ; do \
|
||||
rm "$(DESTDIR)$(libdir)/$$i" ; \
|
||||
done; \
|
||||
ldconfig
|
||||
@-for i in $(PROGS) yate-config ; do \
|
||||
rm "$(DESTDIR)$(bindir)/$$i" ; \
|
||||
done
|
||||
@-rm "$(DESTDIR)$(libdir)/pkgconfig/yate.pc" && \
|
||||
rmdir $(DESTDIR)$(libdir)/pkgconfig
|
||||
@-for i in $(INCS) ; do \
|
||||
rm "$(DESTDIR)$(incdir)/$$i" ; \
|
||||
done; \
|
||||
rmdir "$(DESTDIR)$(incdir)"
|
||||
@-for i in $(MAN8) ; do \
|
||||
rm "$(DESTDIR)$(mandir)/man8/$$i" ; \
|
||||
done
|
||||
@rm -rf "$(DESTDIR)$(docdir)/"
|
||||
$(MAKE) -C ./modules $@
|
||||
$(MAKE) -C ./conf.d $@
|
||||
|
||||
.PHONY: snapshot tarball
|
||||
snapshot tarball: check-topdir clean tables apidocs
|
||||
@if [ $@ = snapshot ]; then ver="`date '+CVS-%Y%m%d'`"; else ver="@PACKAGE_VERSION@"; fi ; \
|
||||
wd=`pwd|sed 's,^.*/,,'`; \
|
||||
mkdir -p tarballs; cd ..; \
|
||||
echo $$wd/tar-exclude >$$wd/tar-exclude; \
|
||||
find $$wd -name Makefile >>$$wd/tar-exclude; \
|
||||
find $$wd/conf.d -name '*.conf' >>$$wd/tar-exclude; \
|
||||
find $$wd -name '*.cache' >>$$wd/tar-exclude; \
|
||||
find $$wd -name '*~' >>$$wd/tar-exclude; \
|
||||
find $$wd -name '.*.swp' >>$$wd/tar-exclude; \
|
||||
if [ $@ = tarball ]; then \
|
||||
find $$wd -name CVS >>$$wd/tar-exclude; \
|
||||
find $$wd -name .cvsignore >>$$wd/tar-exclude; \
|
||||
else \
|
||||
echo "$$wd/yate.spec" >>$$wd/tar-exclude; \
|
||||
fi ; \
|
||||
tar czf $$wd/tarballs/$$wd-$$ver.tar.gz \
|
||||
--exclude $$wd/tarballs \
|
||||
--exclude $$wd/config.status \
|
||||
--exclude $$wd/config.log \
|
||||
--exclude $$wd/run \
|
||||
--exclude $$wd/yate-config \
|
||||
--exclude $$wd/yate.pc \
|
||||
--exclude $$wd/yatepaths.h \
|
||||
-X $$wd/tar-exclude \
|
||||
$$wd; \
|
||||
rm $$wd/tar-exclude
|
||||
|
||||
.PHONY: check-topdir
|
||||
check-topdir:
|
||||
@test -f configure || (echo "Must make this target in the top source directory"; exit 1)
|
||||
|
||||
%.o: @srcdir@/%.cpp @srcdir@/telengine.h
|
||||
$(COMPILE) -c $<
|
||||
|
||||
@srcdir@/configure: @srcdir@/configure.in
|
||||
cd @srcdir@ && autoconf
|
||||
|
||||
config.status: @srcdir@/configure
|
||||
./config.status --recheck
|
||||
|
||||
Makefile: @srcdir@/Makefile.in config.status
|
||||
./config.status
|
||||
|
||||
yate: $(OBJS) libyate.so $(LIBS)
|
||||
$(LINK) -o $@ $^
|
||||
|
||||
libyate.so: $(ENGOBJS) $(LIBS)
|
||||
$(LINK) -shared -o $@ $^ $(LIBAUX)
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo -e 'Usual make targets:\n\
|
||||
all engine modules test everything apidocs\n\
|
||||
install uninstall\n\
|
||||
snapshot tarball'
|
|
@ -0,0 +1,72 @@
|
|||
YATE - Yet Another Telephony Engine
|
||||
-----------------------------------
|
||||
|
||||
The YATE project aims to be a fully featured software PBX.
|
||||
|
||||
It was created to alow developers and users to have more functionality and
|
||||
scalability. To reach this goal YATE is built from two kinds of components:
|
||||
1. The main engine - telengine.
|
||||
2. Modules - routing modules
|
||||
- drivers
|
||||
- script language bindings
|
||||
- billing modules
|
||||
|
||||
Its license is GPL with exceptions (in case of linking with proprietary
|
||||
software). We have chosen this license to help the growth of this project.
|
||||
|
||||
|
||||
Building YATE Software
|
||||
----------------------
|
||||
|
||||
YATE have been tested only on Linux and is in alpha stage yet, so is very
|
||||
possibile to find bugs (even if we have tried by design to minimize the
|
||||
chance of introducing bugs). Please report them at bugs@voip.null.ro
|
||||
|
||||
1. Building the engine
|
||||
|
||||
You have just to run 'make' in the main directory, this will not build any
|
||||
modules or test cases.
|
||||
|
||||
2. Building the modules.
|
||||
|
||||
Run 'make modules' in the main directory or 'make' in the modules directory.
|
||||
|
||||
3. Building the test modules.
|
||||
|
||||
Run 'make test' in the main directory or 'make' in the test directory.
|
||||
|
||||
After you have create the test modules use 'mktestlinks' in the modules
|
||||
directory to make links from test modules into modules directory.
|
||||
|
||||
4. Building the classes API documentation
|
||||
|
||||
Run 'make apidocs' in the main directory. You will need to have kdoc installed.
|
||||
|
||||
|
||||
Alternatively you can just 'make everything' in the main directory which will
|
||||
build them all.
|
||||
|
||||
|
||||
Running YATE
|
||||
------------
|
||||
|
||||
You can run YATE directly from the build directory - just use 'run' script
|
||||
from the main directory.
|
||||
|
||||
You can also install YATE - then you can run it from anywhere.
|
||||
|
||||
On the command line you can use '-v' to increase the verbosity level. If in
|
||||
doubt run "run --help" (or "yate --help" if installed) to get a list of
|
||||
possible options. There is also a manual page - "man yate" and read.
|
||||
|
||||
While running the following signals and keys are trapped and used:
|
||||
- SIGTERM and SIGINT (Ctrl-C) will cleanly stop the engine
|
||||
- SIGHUP and SIGQUIT (Ctrl-\) will reinitialize the modules
|
||||
|
||||
|
||||
Configuring YATE
|
||||
----------------
|
||||
|
||||
Some samples for the configuraton files can be found in the conf.d directory.
|
||||
Note that you must rename them without the .sample extension or create symlinks
|
||||
to them.
|
|
@ -0,0 +1,6 @@
|
|||
Makefile
|
||||
core*
|
||||
*.conf
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -0,0 +1,32 @@
|
|||
# Makefile
|
||||
# This file holds the make rules for the Telephony Engine config files
|
||||
|
||||
# override DESTDIR at install time to prefix the install directory
|
||||
DESTDIR :=
|
||||
|
||||
confdir = @sysconfdir@/yate
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
|
||||
# Install .sample files only if we don't have other versions
|
||||
.PHONY: install
|
||||
install:
|
||||
@mkdir -p "$(DESTDIR)$(confdir)/" && \
|
||||
lst="`ls -1 @srcdir@/*.conf @srcdir@/*.sample @srcdir@/*.sql | sed 's/\.sample//g; s/[^ ]*\*\.[^ ]*//g' | sort | uniq`" ; \
|
||||
for s in $$lst; do \
|
||||
d="$(DESTDIR)$(confdir)/`echo $$s | sed 's,.*/,,'`" ; \
|
||||
if [ -f "$$d" ]; then \
|
||||
echo "Not overwriting existing $$d" ; \
|
||||
else \
|
||||
test -f "$$s" || s="$$s.sample" ; \
|
||||
install -m 0644 "$$s" "$$d" ; \
|
||||
fi ; \
|
||||
done
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall:
|
||||
@-rmdir "$(DESTDIR)$(confdir)" || echo "Remove conf files by hand if you want so"
|
||||
|
||||
Makefile: @srcdir@/Makefile.in ../config.status
|
||||
cd .. && ./config.status
|
|
@ -0,0 +1,3 @@
|
|||
[general]
|
||||
file=/tmp/cdr.tsv
|
||||
tabs=true
|
|
@ -0,0 +1,82 @@
|
|||
[general]
|
||||
; This section sets global variables of the implementation
|
||||
|
||||
; debug: int: OpenH323 debug level
|
||||
;debug=0
|
||||
|
||||
; vendor: string: Vendor name
|
||||
;vendor=Null Team
|
||||
|
||||
; product: string: Product name
|
||||
;product=YATE
|
||||
|
||||
; major: int: Major version number
|
||||
;major=0
|
||||
|
||||
; minor: int: Minor version number
|
||||
;minor=1
|
||||
|
||||
; build: int: Build number
|
||||
;build=1
|
||||
|
||||
; status: keyword: Code status: alpha, beta or release
|
||||
;status=alpha
|
||||
|
||||
|
||||
[codecs]
|
||||
; This section allows to individually enable or disable the codecs
|
||||
|
||||
; g711u: bool: Companded-only G711 mu-law
|
||||
;g711u=enable
|
||||
|
||||
; g711a: bool: Companded-only G711 a-law
|
||||
;g711a=enable
|
||||
|
||||
; gsm0610: bool: European GSM 06.10
|
||||
;gsm0610=enable
|
||||
|
||||
; speexnarrow: bool: Speex narrow
|
||||
;speexnarrow=enable
|
||||
|
||||
; lpc10: bool: LPC 10
|
||||
;lpc10=enable
|
||||
|
||||
|
||||
[ep]
|
||||
; Control the endpoint operation of the module
|
||||
|
||||
; ep: bool: True if you want to activate the h323 endpoint
|
||||
ep = true
|
||||
|
||||
; alias: string: The alias used by h323 module to connect to gatekeeper
|
||||
alias = yate
|
||||
|
||||
; ident: string: Sets the hostname part of the outgoing e.164 (numeric) aliases
|
||||
ident = yate
|
||||
|
||||
; gkclient: bool: If h323 module endpoint should register to a gatekeeper
|
||||
gkclient = false
|
||||
|
||||
; password: string: Password for h.235 authentification
|
||||
;password = 1234
|
||||
|
||||
; gkip: ipaddress: Set the reported ip aaddress of the gatekeeper
|
||||
;gkip = 10.10.10.10
|
||||
|
||||
; gkname: string: Set the name of the gatekeeper
|
||||
;gkname = gigi
|
||||
|
||||
; How the gatekeeper is located
|
||||
; If gkclient is true the endpoint will try first to find the gatekeeper by
|
||||
; using gkip; then, if gkip is unset or is not corect, will try gkname and
|
||||
; then will try to brodcast in the network.
|
||||
|
||||
|
||||
[incoming]
|
||||
; This section sets defaults for the incoming H.323 calls
|
||||
|
||||
; context: string: Input context
|
||||
context=default
|
||||
|
||||
; called: string: Default number to call
|
||||
called=8989989
|
|
@ -0,0 +1,9 @@
|
|||
[general]
|
||||
tcp = yes
|
||||
host = localhost
|
||||
port = 5432
|
||||
database = yate
|
||||
user = yate
|
||||
password =
|
||||
socket =
|
||||
priority = 50
|
|
@ -0,0 +1,64 @@
|
|||
DROP TABLE route;
|
||||
DROP TABLE preroute;
|
||||
DROP TABLE cdr;
|
||||
DROP SEQUENCE route_routeid_seq;
|
||||
DROP SEQUENCE preroute_prerouteid_seq;
|
||||
DROP SEQUENCE cdr_cdrid_seq;
|
||||
CREATE TABLE route (
|
||||
routeid bigserial,
|
||||
context varchar(15),
|
||||
prefix varchar(50),
|
||||
tehno varchar(20),
|
||||
data varchar(100)
|
||||
);
|
||||
|
||||
CREATE TABLE preroute (
|
||||
prerouteid bigserial,
|
||||
tehno varchar(20),
|
||||
channel varchar(20),
|
||||
caller varchar(30),
|
||||
called varchar(30),
|
||||
context varchar(15)
|
||||
);
|
||||
|
||||
CREATE TABLE cdr (
|
||||
cdrid bigserial,
|
||||
time TIMESTAMP with time zone NOT NULL DEFAULT now(),
|
||||
channel varchar(20),
|
||||
caller varchar(30),
|
||||
called varchar(30),
|
||||
billtime INTEGER NOT NULL default '0',
|
||||
ringtime INTEGER NOT NULL default '0',
|
||||
duration INTEGER NOT NULL default '0',
|
||||
status varchar(15)
|
||||
);
|
||||
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('paul','1','SIP','jen');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('bell','112','SIP','bell');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('gigi','4021','ZAP','1');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('whatever','40238','ZAP','g1');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','1','SIP','jen');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','2','SIP','bell');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','3','ZAP','g1');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','4','ZAP','g2');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','11','ZAP','g11');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','12','ZAP','g12');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','13','ZAP','g13');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','14','ZAP','g14');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','15','ZAP','g15');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','16','ZAP','g16');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','17','ZAP','g17');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','18','ZAP','g18');
|
||||
INSERT INTO route (context,prefix,tehno,data) VALUES ('default','19','ZAP','g19');
|
||||
|
||||
|
||||
|
||||
INSERT INTO preroute (tehno,channel,caller,called,context) VALUES ('Zap','1','5556','','default');
|
||||
INSERT INTO preroute (tehno,channel,caller,called,context) VALUES ('Zap','1','1','','default');
|
||||
INSERT INTO preroute (tehno,channel,caller,called,context) VALUES ('Zap','1','2','','bell');
|
||||
INSERT INTO preroute (tehno,channel,caller,called,context) VALUES ('Zap','1','12','','gigi');
|
||||
|
||||
|
||||
|
||||
INSERT INTO cdr (channel,caller,called,billtime,ringtime,duration) VALUES ('Zap','1','12','34','10','45');
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
[priorities]
|
||||
preroute=80
|
||||
route=80
|
||||
|
||||
[contexts]
|
||||
sip:.*=sip
|
||||
iax:.*=iax
|
||||
^1=from-us
|
||||
.*=default
|
||||
|
||||
[default]
|
||||
^1\(.*\)$=us-number-1-\1
|
||||
^40=romania
|
||||
^800=green
|
||||
^\(.\)\(..\)\(...\)$=6digit/\1-\2-\3
|
||||
^.....$=5digit
|
||||
^....$=4digit
|
||||
.*=defaultroute/\0
|
||||
|
||||
[sip]
|
||||
.*:\(.*\)@.*=\1
|
||||
|
||||
[iax]
|
||||
.*:\(.*\)@.*=\1
|
||||
|
||||
[from-us]
|
||||
^1=local
|
||||
.*=defaultroute
|
|
@ -0,0 +1,22 @@
|
|||
DROP TABLE register;
|
||||
DROP TABLE routepaid;
|
||||
|
||||
DROP SEQUENCE register_registerid_seq;
|
||||
DROP SEQUENCE routepaid_routepaidid_seq;
|
||||
CREATE TABLE register (
|
||||
registerid bigserial,
|
||||
username varchar(20),
|
||||
password varchar(20),
|
||||
e164 varchar(30),
|
||||
credit varchar(20),
|
||||
context varchar(15)
|
||||
);
|
||||
CREATE TABLE routepaid (
|
||||
routepaidid bigserial,
|
||||
context varchar(15),
|
||||
prefix varchar(50),
|
||||
tehno varchar(20),
|
||||
data varchar(100),
|
||||
price INTEGER NOT NULL default '0'
|
||||
);
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
[general]
|
||||
; This section controls the behaviour of the Remote Manager
|
||||
|
||||
; port: int: TCP Port to listen on, 0 to disable module
|
||||
;port=5038
|
||||
|
||||
; addr: ipaddress: IP address to bind to
|
||||
;addr=127.0.0.1
|
||||
|
||||
; header: string: Header string to display on connect
|
||||
;header=YATE (http://YATE.null.ro) ready.
|
|
@ -0,0 +1,3 @@
|
|||
[general]
|
||||
noisy=0
|
||||
threads=0
|
|
@ -0,0 +1,26 @@
|
|||
; Warning: all strings are case sensitive
|
||||
;
|
||||
; supported keywords (you can always specify numbers directly):
|
||||
;
|
||||
; swtype= unknown,ni2,dms100,lucent5e,at&t4ess,euroisdn_e1,euroisdn_t1,ni1
|
||||
; dialplan= unknown,international,national,local,private
|
||||
; numplan= unknown,e164,x121,f69,national,private,reserved
|
||||
; numtype= unknown,international,national,net_specific,subscriber,abbreviated,
|
||||
; reserved
|
||||
; presentation= allow_user_not_screened,allow_user_passed,allow_user_failed,
|
||||
; allow_network,prohibit_user_not_screened,prohibit_user_passed,
|
||||
; prohibit_user_failed,prohibit_network,not_available
|
||||
|
||||
[general]
|
||||
;buflen=480
|
||||
;restart=0
|
||||
;dumpevents=no
|
||||
|
||||
[span 1]
|
||||
first=1
|
||||
chans=31
|
||||
dchan=16
|
||||
isnet=yes
|
||||
;swtype=unknown
|
||||
;dialplan=unknown
|
||||
;presentation=allow_user_not_screened
|
|
@ -0,0 +1,263 @@
|
|||
# Process this file with autoconf to produce a configure script.
|
||||
AC_INIT(YATE, 0.8.1)
|
||||
AC_CONFIG_SRCDIR([README])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CC
|
||||
AC_PROG_AWK
|
||||
|
||||
# Checks for header files.
|
||||
AC_HEADER_DIRENT
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS([fcntl.h arpa/inet.h netdb.h netinet/in.h sys/ioctl.h sys/socket.h sys/time.h], , [AC_MSG_ERROR([This header file is required.])])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
AC_C_INLINE
|
||||
AC_HEADER_TIME
|
||||
|
||||
# Checks for library functions.
|
||||
AC_FUNC_MALLOC
|
||||
AC_TYPE_SIGNAL
|
||||
AC_CHECK_FUNCS([gettimeofday inet_ntoa memmove regcomp strerror], , [AC_MSG_ERROR([This function is required.])])
|
||||
|
||||
AC_CACHE_SAVE
|
||||
|
||||
# Checks for required libraries.
|
||||
AC_CHECK_LIB([dl], [dlopen], , [AC_MSG_ERROR([This library is required.])])
|
||||
AC_CHECK_LIB([pthread], [pthread_create], , [AC_MSG_ERROR([This library is required.])])
|
||||
|
||||
# Checks for optional libraries.
|
||||
|
||||
HAVE_PGSQL=no
|
||||
PGSQL_INC=""
|
||||
AC_ARG_WITH(libpq,AC_HELP_STRING([--with-libpq=DIR],[use Postgress SQL from DIR (default /usr)]),[ac_cv_use_libpq=$withval],[ac_cv_use_libpq=/usr])
|
||||
if [[ "x$ac_cv_use_libpq" != "xno" ]]; then
|
||||
AC_MSG_CHECKING([for Postgress SQL in $ac_cv_use_libpq])
|
||||
incpq="$ac_cv_use_libpq/include"
|
||||
libpq="$ac_cv_use_libpq/lib/libpq.so"
|
||||
if [[ ! -f "$incpq/libpq-fe.h" ]]; then
|
||||
incpq="$incpq/pgsql"
|
||||
fi
|
||||
if [[ -f "$incpq/libpq-fe.h" -a -f "$libpq" ]]; then
|
||||
HAVE_PGSQL=yes
|
||||
PGSQL_INC="-I$incpq"
|
||||
fi
|
||||
AC_MSG_RESULT([$HAVE_PGSQL])
|
||||
fi
|
||||
AC_SUBST(HAVE_PGSQL)
|
||||
AC_SUBST(PGSQL_INC)
|
||||
|
||||
HAVE_PRI=no
|
||||
AC_ARG_WITH(libpri,AC_HELP_STRING([--with-libpri],[use ISDN PRI if available (default)]),[ac_cv_use_libpri=$withval],[ac_cv_use_libpri=yes])
|
||||
if [[ "x$ac_cv_use_libpri" != "xno" ]]; then
|
||||
AC_CHECK_HEADER(libpri.h, , [ac_cv_use_libpri=no])
|
||||
fi
|
||||
if [[ "x$ac_cv_use_libpri" != "xno" ]]; then
|
||||
AC_CHECK_LIB([pri], [pri_new], [HAVE_PRI=yes])
|
||||
fi
|
||||
AC_SUBST(HAVE_PRI)
|
||||
|
||||
HAVE_GSM=no
|
||||
AC_ARG_WITH(libgsm,AC_HELP_STRING([--with-libgsm],[use GSM codec if available (default)]),[ac_cv_use_libgsm=$withval],[ac_cv_use_libgsm=yes])
|
||||
if [[ "x$ac_cv_use_libgsm" != "xno" ]]; then
|
||||
AC_CHECK_HEADER(gsm.h, , [ac_cv_use_libgsm=no])
|
||||
fi
|
||||
if [[ "x$ac_cv_use_libgsm" != "xno" ]]; then
|
||||
AC_CHECK_LIB([gsm], [gsm_create], [HAVE_GSM=yes])
|
||||
fi
|
||||
AC_SUBST(HAVE_GSM)
|
||||
|
||||
HAVE_PWLIB=no
|
||||
PWLIB_INC=""
|
||||
PWLIB_LIB=""
|
||||
PWLIB_RUN=""
|
||||
AC_ARG_WITH(pwlib,AC_HELP_STRING([--with-pwlib=DIR],[use Pwlib from DIR (default /usr)]),[ac_cv_use_pwlib=$withval],[ac_cv_use_pwlib=/usr])
|
||||
|
||||
if [[ "x$ac_cv_use_pwlib" != "xno" ]]; then
|
||||
AC_MSG_CHECKING([for Pwlib in $ac_cv_use_pwlib])
|
||||
verpw=`ptlib-config --version 2>/dev/null`
|
||||
# try first installed directory
|
||||
incpw="$ac_cv_use_pwlib/include/ptlib.h"
|
||||
libpw="$ac_cv_use_pwlib/lib/libpt.so"
|
||||
PWLIB_INC="-I$ac_cv_use_pwlib/include/ptlib"
|
||||
if [[ "$verpw" '<' "1.6.0" ]]; then
|
||||
PWLIB_INC="-I$ac_cv_use_pwlib/include/ptlib/unix/ptlib -I$ac_cv_use_pwlib/include/ptlib/unix $PWLIB_INC"
|
||||
fi
|
||||
if [[ -f "$incpw" -a -f "$libpw" ]]; then
|
||||
HAVE_PWLIB=installed
|
||||
PWLIB_LIB="-L$ac_cv_use_pwlib/lib -lpt"
|
||||
else
|
||||
# try source directory style
|
||||
libpw=`echo "$ac_cv_use_pwlib/lib/"libpt*r.so`
|
||||
if [[ -f "$incpw" -a -f "$libpw" ]]; then
|
||||
HAVE_PWLIB=sources
|
||||
PWLIB_LIB="-L$ac_cv_use_pwlib/lib -l`echo "$libpw"|sed 's,^.*/lib,,; s,\.so$,,'`"
|
||||
PWLIB_RUN=":$ac_cv_use_pwlib/lib"
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT([$HAVE_PWLIB $verpw])
|
||||
fi
|
||||
|
||||
HAVE_H323=no
|
||||
H323_INC=""
|
||||
H323_LIB=""
|
||||
H323_RUN=""
|
||||
AC_ARG_WITH(openh323,AC_HELP_STRING([--with-openh323=DIR],[use OpenH323 from DIR (default /usr)]),[ac_cv_use_openh323=$withval],[ac_cv_use_openh323=/usr])
|
||||
|
||||
if [[ "x$HAVE_PWLIB" != "xno" -a "x$ac_cv_use_openh323" != "xno" ]]; then
|
||||
AC_MSG_CHECKING([for OpenH323 in $ac_cv_use_openh323])
|
||||
# try first installed directory
|
||||
inc323="$ac_cv_use_openh323/include/openh323/h323.h"
|
||||
lib323="$ac_cv_use_openh323/lib/libopenh323.so"
|
||||
if [[ -f "$inc323" -a -f "$lib323" ]]; then
|
||||
HAVE_H323=installed
|
||||
H323_INC="-I$ac_cv_use_openh323/include/openh323"
|
||||
H323_LIB="-L$ac_cv_use_openh323/lib -lopenh323"
|
||||
else
|
||||
# try source directory style
|
||||
inc323="$ac_cv_use_openh323/include/h323.h"
|
||||
lib323=`echo "$ac_cv_use_openh323/lib/"libh323*r.so`
|
||||
if [[ -f "$inc323" -a -f "$lib323" ]]; then
|
||||
HAVE_H323=sources
|
||||
H323_INC="-I$ac_cv_use_openh323/include"
|
||||
H323_LIB="-L$ac_cv_use_openh323/lib -l`echo "$lib323"|sed 's,^.*/lib,,; s,\.so$,,'`"
|
||||
H323_RUN=":$ac_cv_use_openh323/lib"
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT([$HAVE_H323])
|
||||
fi
|
||||
|
||||
if [[ "x$HAVE_H323" != "xno" ]]; then
|
||||
H323_INC="$PWLIB_INC $H323_INC"
|
||||
H323_LIB="$PWLIB_LIB $H323_LIB"
|
||||
H323_RUN="$PWLIB_RUN$H323_RUN"
|
||||
fi
|
||||
AC_SUBST(HAVE_H323)
|
||||
AC_SUBST(H323_INC)
|
||||
AC_SUBST(H323_LIB)
|
||||
AC_SUBST(H323_RUN)
|
||||
|
||||
HAVE_ORTP=no
|
||||
ORTP_INC=""
|
||||
ORTP_LIB=""
|
||||
AC_ARG_WITH(libortp,AC_HELP_STRING([--with-libortp=DIR],[use oRTP from DIR (default /usr)]),[ac_cv_use_libortp=$withval],[ac_cv_use_libortp=/usr])
|
||||
if [[ "x$ac_cv_use_libortp" != "xno" ]]; then
|
||||
AC_MSG_CHECKING([for oRTP in $ac_cv_use_libortp])
|
||||
incor="$ac_cv_use_libortp/include/ortp"
|
||||
libor="$ac_cv_use_libortp/lib/libortp.so"
|
||||
if [[ -f "$incor/ortp.h" -a -f "$libor" ]]; then
|
||||
HAVE_ORTP=yes
|
||||
ORTP_INC="-I$incor"
|
||||
ORTP_LIB="-L$ac_cv_use_libortp/lib -lortp"
|
||||
fi
|
||||
AC_MSG_RESULT([$HAVE_ORTP])
|
||||
fi
|
||||
AC_SUBST(HAVE_ORTP)
|
||||
AC_SUBST(ORTP_INC)
|
||||
AC_SUBST(ORTP_LIB)
|
||||
|
||||
HAVE_EXOSIP=no
|
||||
EXOSIP_INC=""
|
||||
EXOSIP_LIB=""
|
||||
AC_ARG_WITH(libexosip,AC_HELP_STRING([--with-libexosip=DIR],[use eXosip from DIR (default /usr)]),[ac_cv_use_libexosip=$withval],[ac_cv_use_libexosip=/usr])
|
||||
if [[ "x$ac_cv_use_libexosip" != "xno" ]]; then
|
||||
AC_MSG_CHECKING([for eXosip in $ac_cv_use_libexosip])
|
||||
incexo="$ac_cv_use_libexosip/include/eXosip"
|
||||
libexo="$ac_cv_use_libexosip/lib/libeXosip.so"
|
||||
if [[ -f "$incexo/eXosip.h" -a -f "$libexo" ]]; then
|
||||
HAVE_EXOSIP=yes
|
||||
EXOSIP_INC="-I$incexo"
|
||||
EXOSIP_LIB="-L$ac_cv_use_libexosip/lib -leXosip"
|
||||
fi
|
||||
AC_MSG_RESULT([$HAVE_EXOSIP])
|
||||
fi
|
||||
AC_SUBST(HAVE_EXOSIP)
|
||||
AC_SUBST(EXOSIP_INC)
|
||||
AC_SUBST(EXOSIP_LIB)
|
||||
|
||||
HAVE_GLIB2=no
|
||||
GLIB2_INC=""
|
||||
GLIB2_LIB=""
|
||||
AC_ARG_WITH(libglib2,AC_HELP_STRING([--with-libglib2=DIR],[use Glib 2.0 from DIR (default /usr)]),[ac_cv_use_libglib2=$withval],[ac_cv_use_libglib2=yes])
|
||||
if [[ "x$ac_cv_use_libglib2" = "xyes" ]]; then
|
||||
AC_MSG_CHECKING([for Glib 2.0 using pkg-config])
|
||||
verg2=`pkg-config --modversion glib-2.0 2>/dev/null`
|
||||
incg2=`pkg-config --cflags-only-I glib-2.0 2>/dev/null`
|
||||
libg2=`pkg-config --libs glib-2.0 2>/dev/null`
|
||||
if [[ "x$incg2" != "x" -a "x$libg2" != "x" ]]; then
|
||||
HAVE_GLIB2=yes
|
||||
GLIB2_INC="$incg2"
|
||||
GLIB2_LIB="$libg2"
|
||||
ac_cv_use_libglib2="no"
|
||||
else
|
||||
ac_cv_use_libglib2="/usr"
|
||||
verg2="no"
|
||||
fi
|
||||
AC_MSG_RESULT([$verg2])
|
||||
fi
|
||||
if [[ "x$ac_cv_use_libglib2" != "xno" ]]; then
|
||||
AC_MSG_CHECKING([for Glib 2.0 in $ac_cv_use_libglib2])
|
||||
incg2="$ac_cv_use_libglib2"
|
||||
libg2="$ac_cv_use_libglib2/lib"
|
||||
if [[ -f "$incg2/include/glib-2.0/glib.h" -a -f "$libg2/libglib-2.0.so" ]]; then
|
||||
HAVE_GLIB2=yes
|
||||
GLIB2_INC="-I$incg2/include/glib-2.0 -I$libg2/glib-2.0/include"
|
||||
GLIB2_LIB="-L$libg2 -lglib-2.0"
|
||||
fi
|
||||
AC_MSG_RESULT([$HAVE_GLIB2])
|
||||
fi
|
||||
AC_SUBST(HAVE_GLIB2)
|
||||
AC_SUBST(GLIB2_INC)
|
||||
AC_SUBST(GLIB2_LIB)
|
||||
|
||||
HAVE_IAX2=no
|
||||
IAX2_INC=""
|
||||
IAX2_LIB=""
|
||||
AC_ARG_WITH(libIAX2,AC_HELP_STRING([--with-libiax2=DIR],[use IAX 2 from DIR (default /usr)]),[ac_cv_use_libiax2=$withval],[ac_cv_use_libiax2=yes])
|
||||
if [[ "x$ac_cv_use_libiax2" = "xyes" ]]; then
|
||||
AC_MSG_CHECKING([for IAX 2 using iax-config])
|
||||
veri2=`iax-config --version 2>/dev/null`
|
||||
inci2=`iax-config --cflags 2>/dev/null`
|
||||
libi2=`iax-config --libs 2>/dev/null`
|
||||
if [[ "x$inci2" != "x" -a "x$libi2" != "x" ]]; then
|
||||
HAVE_IAX2=yes
|
||||
IAX2_INC="$inci2"
|
||||
IAX2_LIB="$libi2"
|
||||
ac_cv_use_libiax2="no"
|
||||
else
|
||||
ac_cv_use_libiax2="/usr"
|
||||
verg2="no"
|
||||
fi
|
||||
AC_MSG_RESULT([$veri2])
|
||||
fi
|
||||
if [[ "x$ac_cv_use_libiax2" != "xno" ]]; then
|
||||
AC_MSG_CHECKING([for IAX 2 in $ac_cv_use_libiax2])
|
||||
inci2="$ac_cv_use_libiax2"
|
||||
libi2="$ac_cv_use_libiax2/lib"
|
||||
if [[ -f "$inci2/include/iax/iax2.h" -a -f "$libi2/libiax.so" ]]; then
|
||||
HAVE_IAX2=yes
|
||||
IAX2_INC="-I$inci2/include/iax"
|
||||
IAX2_LIB="-L$libi2 -liax"
|
||||
fi
|
||||
AC_MSG_RESULT([$HAVE_IAX2])
|
||||
fi
|
||||
AC_SUBST(HAVE_IAX2)
|
||||
AC_SUBST(IAX2_INC)
|
||||
AC_SUBST(IAX2_LIB)
|
||||
|
||||
MODULE_CFLAGS="-fno-exceptions -fno-check-new -frtti -fPIC"
|
||||
MODULE_LDFLAGS="-export-dynamic -shared -Wl,--retain-symbols-file,/dev/null"
|
||||
AC_SUBST(MODULE_CFLAGS)
|
||||
AC_SUBST(MODULE_LDFLAGS)
|
||||
|
||||
AC_CONFIG_FILES([yate.spec
|
||||
yate.pc
|
||||
Makefile
|
||||
modules/Makefile
|
||||
conf.d/Makefile
|
||||
test/Makefile])
|
||||
AC_CONFIG_FILES([yate-config],[chmod +x yate-config])
|
||||
AC_CONFIG_FILES([run],[chmod +x run])
|
||||
AC_OUTPUT
|
|
@ -0,0 +1,4 @@
|
|||
core*
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -0,0 +1,5 @@
|
|||
core*
|
||||
*.html
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -0,0 +1,18 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>YATE - Data flows</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 align="center">YATE</h1>
|
||||
<p align="center">Data flows</p>
|
||||
<h2>Data blocks</h2>
|
||||
<p></p>
|
||||
<h2>Data nodes</h2>
|
||||
<h2>Data endpoints</h2>
|
||||
<h2>Links</h2>
|
||||
<a href="index.html">Overview</a>.<br />
|
||||
The <a href="api/TelEngine__DataBlock.html">DataBlock</a> class.<br />
|
||||
The <a href="api/TelEngine__DataNode.html">DataNode</a> class.<br />
|
||||
The <a href="api/TelEngine__DataEndpoint.html">DataEndpoint</a> class.<br />
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,119 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>YATE - External module</title>
|
||||
</head>
|
||||
<body>
|
||||
The work on this document is still in progress.
|
||||
Please do not use it as reference, the specifications are likely to change.
|
||||
|
||||
<h1>External module command flow</h1>
|
||||
|
||||
<h2>File handles used</h2>
|
||||
The external user application or script comunicates with the module trough
|
||||
several file descriptors:<br />
|
||||
0 (stdin) - Carries commands and notifications from engine to application<br />
|
||||
1 (stdout) - Carries commands and answers from application to the engine<br />
|
||||
2 (stderr) - Has the usual meaning of reporting errors and is directed to the engine's stderr or logfile<br />
|
||||
3 (optional) - Transports audio data from the engine to the application<br />
|
||||
4 (optional) - Transports audio data from the application to the engine<br />
|
||||
|
||||
File descriptors 3 and 4 are open only for audio capable applications.<br />
|
||||
|
||||
|
||||
<h2>Format of commands and notifications</h2>
|
||||
|
||||
In the following description the _ (underscore) character is used to denote
|
||||
the TAB character (\t, ^I, decimal 9) used as element separator.<br />
|
||||
|
||||
Words enclosed in <angle brackets> must be replaced with the proper value.<br />
|
||||
|
||||
Elements in [square brackets] are optional. An ellipsis ... denotes optional
|
||||
repetition of the last element.<br />
|
||||
|
||||
Every command is sent on its own newline (\n, ^J, decimal 10) delimited line.<br />
|
||||
|
||||
Any value that contains special characters (ASCII code lower than 32) MUST have
|
||||
them converted to %<hexcode> where <hexcode> is the 2 character hexadecimal
|
||||
representation of that ASCII code. The % character itself MUST be converted to
|
||||
its %25 representation. Characters with codes higher than 32 (except %) SHOULD
|
||||
not be escaped but may be so. An %-escaped code may be received instead of an
|
||||
unescaped character anywhere except in the initial keyword or the delimiting
|
||||
TAB characters. Anywhere in the line except the initial keyword a % character
|
||||
not followed by 2 hexadecimal digits is an error.<br />
|
||||
|
||||
<p><b>Keyword: %%=error</b><br />
|
||||
%%=error_<original><br />
|
||||
The engine sends this notification as answer to a syntactically incorrect line
|
||||
it received from the application.<br />
|
||||
<original> - the original line exactly as received (not escaped or something)<br />
|
||||
</p>
|
||||
|
||||
<p><b>Keyword: %%=init</b><br />
|
||||
%%=init<br />
|
||||
This command is sent to the other party requesting it to (re)initialize.<br />
|
||||
</p>
|
||||
|
||||
<p><b>Keyword: %%=halt</b><br />
|
||||
%%=halt[_<exitcode>]<br />
|
||||
Command from application to the engine asking it to terminate. An optional
|
||||
exit code may be provided.<br />
|
||||
<exitcode> - positive numeric engine exit code<br />
|
||||
</p>
|
||||
|
||||
<p><b>Keyword: %%=output</b><br />
|
||||
%%=output_<message><br />
|
||||
Asks the engine to display a message in its console output.<br />
|
||||
<message> - message line to display as-is<br />
|
||||
</p>
|
||||
|
||||
<p><b>Keyword: %%=debug</b><br />
|
||||
%%=debug_[<facility>]_<level>_<message><br />
|
||||
Asks the engine to emit a debugging message to the console output.<br />
|
||||
<facility> - optional facility text to display<br />
|
||||
<level> - numeric debug level (0-9) at which to output<br />
|
||||
<message> - debug message to display<br />
|
||||
</p>
|
||||
|
||||
<p><b>Keyword: %%>message</b><br />
|
||||
%%>message_<id>_<name>_<retvalue>[_<key>=<value>...]<br />
|
||||
This is sent by a party (engine or application) to ask the other to run a
|
||||
message trough its handlers. The local message must be held until an answer is
|
||||
received.<br />
|
||||
<id> - obscure unique message ID string generated by the sender<br />
|
||||
<name> - name of the message<br />
|
||||
<retvalue> - default textual return value of the message<br />
|
||||
<key>=<value> - enumeration of the key-value pairs of the message<br />
|
||||
</p>
|
||||
|
||||
<p><b>Keyword: %%<message</b><br />
|
||||
%%<message_<id>_<processed>_[<name>]_<retvalue>[_<key>=<value>...]<br />
|
||||
One of this answer is required for every received message (%%>message). When a
|
||||
party gets a line in this format it should paste the provided values back into
|
||||
the message and let it continue.<br />
|
||||
<id> - same message ID string received trough %%>message<br />
|
||||
<processed> - boolean ("true" or "false") indication if the message has been
|
||||
processed or it should be passed to the next handler<br />
|
||||
<name> - new name of the message, if empty keep unchanged<br />
|
||||
<retvalue> - new textual return value of the message<br />
|
||||
<key>=<value> - new key-value pairs to set in the message; delete the key if
|
||||
the value is an empty string<br />
|
||||
</p>
|
||||
|
||||
<p><b>Keyword: %%>install</b><br />
|
||||
%%>install_<name>[_<priority>]<br />
|
||||
Always from the application to the engine, requests the installing of a message
|
||||
handler<br />
|
||||
<name> - name of the messages for that a handler should be installed<br />
|
||||
<priority> - priority, use default if missing<br />
|
||||
</p>
|
||||
|
||||
<p><b>Keyword: %%<install</b><br />
|
||||
%%<install_<name>_<priority><br />
|
||||
Confirmation from engine to the application that the handler has been installed
|
||||
properly or not.<br />
|
||||
<name> - name of the messages asked to handle<br />
|
||||
<priority> - priority of the installed handler, -1 if it failed<br />
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>YATE - Yet Another Telephony Engine</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 align="center">YATE</h1>
|
||||
<p align="center">Yet Another Telephony Engine</p>
|
||||
<h2>Purpose</h2>
|
||||
<p>YATE is a telephony engine designed to implement PBX and IVR solutions for
|
||||
small to large scale projects.</p>
|
||||
<h2>Architecture</h2>
|
||||
<p>YATE is based mainly on messages and data flows. Messages are used for
|
||||
signalling and control while the data flows carry the voice or other media
|
||||
data.</p>
|
||||
<h2>API Documentation</h2>
|
||||
<a href="api/index.html">Class list</a> also available in
|
||||
<a href="api/index-long.html">annotated format</a>.<br />
|
||||
<a href="api/hier.html">Class hierarcy</a><br />
|
||||
<a href="messages.html">Messages</a><br />
|
||||
<a href="dataflow.html">Data flows</a><br />
|
||||
<!-- <a href="extmodule.html">External module</a><br /> -->
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,16 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>YATE - Standard and typical messages</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 align="center">YATE</h1>
|
||||
<p align="center">Standard and typical messages</p>
|
||||
<h2>Message format</h2>
|
||||
<p>Messages in YATE</p>
|
||||
<h2>Standard messages</h2>
|
||||
<h2>Other messages</h2>
|
||||
<h2>Links</h2>
|
||||
<a href="index.html">Overview</a>.<br />
|
||||
The <a href="api/TelEngine__Message.html">Message</a> class.<br />
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,204 @@
|
|||
/**
|
||||
* Configuration.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telengine.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
Configuration::Configuration()
|
||||
{
|
||||
}
|
||||
|
||||
Configuration::Configuration(const char *filename)
|
||||
: String(filename)
|
||||
{
|
||||
load();
|
||||
}
|
||||
|
||||
ObjList *Configuration::getSectHolder(const String §) const
|
||||
{
|
||||
if (sect.null())
|
||||
return 0;
|
||||
ObjList *l = const_cast<ObjList *>(&m_sections);
|
||||
for (;l;l=l->next()) {
|
||||
NamedList *n = static_cast<NamedList *>(l->get());
|
||||
if (n && *n == sect)
|
||||
return l;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ObjList *Configuration::makeSectHolder(const String §)
|
||||
{
|
||||
if (sect.null())
|
||||
return 0;
|
||||
ObjList *l = getSectHolder(sect);
|
||||
if (!l)
|
||||
l = m_sections.append(new NamedList(sect));
|
||||
return l;
|
||||
}
|
||||
|
||||
NamedList *Configuration::getSection(unsigned int index) const
|
||||
{
|
||||
const ObjList *l = m_sections[index];
|
||||
return l ? static_cast<NamedList *>(l->get()) : 0;
|
||||
}
|
||||
|
||||
NamedList *Configuration::getSection(const String §) const
|
||||
{
|
||||
ObjList *l = getSectHolder(sect);
|
||||
return l ? static_cast<NamedList *>(l->get()) : 0;
|
||||
}
|
||||
|
||||
NamedString *Configuration::getKey(const String §, const String &key) const
|
||||
{
|
||||
NamedList *l = getSection(sect);
|
||||
return l ? l->getParam(key) : 0;
|
||||
}
|
||||
|
||||
const char *Configuration::getValue(const String §, const String &key, const char *defvalue) const
|
||||
{
|
||||
const NamedString *s = getKey(sect,key);
|
||||
return s ? s->c_str() : defvalue;
|
||||
}
|
||||
|
||||
int Configuration::getIntValue(const String §, const String &key, int defvalue) const
|
||||
{
|
||||
const NamedString *s = getKey(sect,key);
|
||||
return s ? s->toInteger(defvalue) : defvalue;
|
||||
}
|
||||
|
||||
int Configuration::getIntValue(const String §, const String &key, const TokenDict *tokens, int defvalue) const
|
||||
{
|
||||
const NamedString *s = getKey(sect,key);
|
||||
return s ? s->toInteger(tokens,defvalue) : defvalue;
|
||||
}
|
||||
|
||||
bool Configuration::getBoolValue(const String §, const String &key, bool defvalue) const
|
||||
{
|
||||
const NamedString *s = getKey(sect,key);
|
||||
return s ? s->toBoolean(defvalue) : defvalue;
|
||||
}
|
||||
|
||||
void Configuration::clearSection(const char *sect)
|
||||
{
|
||||
if (sect) {
|
||||
ObjList *l = getSectHolder(sect);
|
||||
if (l)
|
||||
l->remove();
|
||||
}
|
||||
else
|
||||
m_sections.clear();
|
||||
}
|
||||
|
||||
void Configuration::clearKey(const String §, const String &key)
|
||||
{
|
||||
NamedList *l = getSection(sect);
|
||||
if (l)
|
||||
l->clearParam(key);
|
||||
}
|
||||
|
||||
void Configuration::setValue(const String §, const char *key, const char *value)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"Configuration::setValue(\"%s\",\"%s\",\"%s\")",sect.c_str(),key,value);
|
||||
#endif
|
||||
ObjList *l = makeSectHolder(sect);
|
||||
if (!l)
|
||||
return;
|
||||
NamedList *n = static_cast<NamedList *>(l->get());
|
||||
if (n)
|
||||
n->setParam(key,value);
|
||||
}
|
||||
|
||||
void Configuration::setValue(const String §, const char *key, int value)
|
||||
{
|
||||
char buf[32];
|
||||
::sprintf(buf,"%d",value);
|
||||
setValue(sect,key,buf);
|
||||
}
|
||||
|
||||
void Configuration::setValue(const String §, const char *key, bool value)
|
||||
{
|
||||
setValue(sect,key,value ? "true" : "false");
|
||||
}
|
||||
|
||||
bool Configuration::load()
|
||||
{
|
||||
m_sections.clear();
|
||||
if (null())
|
||||
return false;
|
||||
FILE *f = ::fopen(c_str(),"r");
|
||||
if (f) {
|
||||
String sect;
|
||||
for (;;) {
|
||||
char buf[1024];
|
||||
if (!::fgets(buf,sizeof(buf),f))
|
||||
break;
|
||||
|
||||
char *pc = ::strchr(buf,'\r');
|
||||
if (pc)
|
||||
*pc = 0;
|
||||
pc = ::strchr(buf,'\n');
|
||||
if (pc)
|
||||
*pc = 0;
|
||||
pc = buf;
|
||||
while (*pc == ' ' || *pc == '\t')
|
||||
pc++;
|
||||
switch (*pc) {
|
||||
case 0:
|
||||
case ';':
|
||||
continue;
|
||||
}
|
||||
String s(pc);
|
||||
if (s[0] == '[') {
|
||||
int r = s.find(']');
|
||||
if (r > 0) {
|
||||
sect = s.substr(1,r-1);
|
||||
makeSectHolder(sect);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
int q = s.find('=');
|
||||
if (q > 0)
|
||||
setValue(sect,s.substr(0,q).trimBlanks(),s.substr(q+1).trimBlanks());
|
||||
}
|
||||
::fclose(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Configuration::save() const
|
||||
{
|
||||
if (null())
|
||||
return false;
|
||||
FILE *f = ::fopen(c_str(),"w");
|
||||
if (f) {
|
||||
ObjList *ol = const_cast<ObjList *>(&m_sections);
|
||||
for (;ol;ol=ol->next()) {
|
||||
NamedList *nl = static_cast<NamedList *>(ol->get());
|
||||
if (!nl)
|
||||
continue;
|
||||
::fprintf(f,"[%s]\n",nl->c_str());
|
||||
unsigned int n = nl->length();
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
NamedString *ns = nl->getParam(i);
|
||||
if (ns) {
|
||||
const char *v = ns->c_str();
|
||||
if (!v)
|
||||
v = "";
|
||||
::fprintf(f,"%s=%s\n",ns->name().c_str(),v);
|
||||
}
|
||||
}
|
||||
}
|
||||
::fclose(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,543 @@
|
|||
/**
|
||||
* DataBlock.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telephony.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace TelEngine{
|
||||
|
||||
extern "C" {
|
||||
#include "tables/all.h"
|
||||
}
|
||||
|
||||
class ThreadedSourcePrivate : public Thread
|
||||
{
|
||||
public:
|
||||
ThreadedSourcePrivate(ThreadedSource *source, const char *name)
|
||||
: Thread(name), m_source(source) { }
|
||||
|
||||
protected:
|
||||
virtual void run()
|
||||
{ m_source->run(); }
|
||||
|
||||
virtual void cleanup()
|
||||
{ m_source->m_thread = 0; m_source->cleanup(); }
|
||||
|
||||
private:
|
||||
ThreadedSource *m_source;
|
||||
};
|
||||
|
||||
class SimpleTranslator : public DataTranslator
|
||||
{
|
||||
public:
|
||||
SimpleTranslator(const String &sFormat, const String &dFormat)
|
||||
: DataTranslator(sFormat,dFormat) { }
|
||||
virtual void Consume(const DataBlock &data)
|
||||
{
|
||||
ref();
|
||||
if (getTransSource()) {
|
||||
DataBlock oblock;
|
||||
if (oblock.convert(data, m_format, getTransSource()->getFormat()))
|
||||
getTransSource()->Forward(oblock);
|
||||
}
|
||||
deref();
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
DataBlock::DataBlock()
|
||||
: m_data(0), m_length(0)
|
||||
{
|
||||
}
|
||||
|
||||
DataBlock::DataBlock(const DataBlock &value)
|
||||
: m_data(0), m_length(0)
|
||||
{
|
||||
assign(value.data(),value.length());
|
||||
}
|
||||
|
||||
DataBlock::DataBlock(void *value, unsigned int len, bool copyData)
|
||||
: m_data(0), m_length(0)
|
||||
{
|
||||
assign(value,len,copyData);
|
||||
}
|
||||
|
||||
DataBlock::~DataBlock()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void DataBlock::clear(bool deleteData)
|
||||
{
|
||||
m_length = 0;
|
||||
if (m_data) {
|
||||
void *data = m_data;
|
||||
m_data = 0;
|
||||
if (deleteData)
|
||||
::free(data);
|
||||
}
|
||||
}
|
||||
|
||||
DataBlock& DataBlock::assign(void *value, unsigned int len, bool copyData)
|
||||
{
|
||||
if ((value != m_data) || (len != m_length)) {
|
||||
void *odata = m_data;
|
||||
m_length = 0;
|
||||
m_data = 0;
|
||||
if (len) {
|
||||
if (copyData) {
|
||||
void *data = ::malloc(len);
|
||||
if (value)
|
||||
::memcpy(data,value,len);
|
||||
else
|
||||
::memset(data,0,len);
|
||||
m_data = data;
|
||||
}
|
||||
else
|
||||
m_data = value;
|
||||
if (m_data)
|
||||
m_length = len;
|
||||
}
|
||||
if (odata && (odata != m_data))
|
||||
::free(odata);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DataBlock::truncate(unsigned int len)
|
||||
{
|
||||
if (!len)
|
||||
clear();
|
||||
else if (len < m_length)
|
||||
assign(m_data,len);
|
||||
}
|
||||
|
||||
void DataBlock::cut(int len)
|
||||
{
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
int ofs = 0;
|
||||
if (len < 0)
|
||||
ofs = len = -len;
|
||||
|
||||
if ((unsigned)len >= m_length) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
assign(ofs+(char *)m_data,m_length - len);
|
||||
}
|
||||
|
||||
DataBlock& DataBlock::operator=(const DataBlock &value)
|
||||
{
|
||||
assign(value.data(),value.length());
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DataBlock::append(const DataBlock &value)
|
||||
{
|
||||
if (m_length) {
|
||||
if (value.length()) {
|
||||
unsigned int len = m_length+value.length();
|
||||
void *data = ::malloc(len);
|
||||
::memcpy(data,m_data,m_length);
|
||||
::memcpy(m_length+(char*)data,value.data(),value.length());
|
||||
assign(data,len,false);
|
||||
}
|
||||
}
|
||||
else
|
||||
assign(value.data(),value.length());
|
||||
}
|
||||
|
||||
void DataBlock::insert(const DataBlock &value)
|
||||
{
|
||||
unsigned int vl = value.length();
|
||||
if (m_length) {
|
||||
if (vl) {
|
||||
unsigned int len = m_length+vl;
|
||||
void *data = ::malloc(len);
|
||||
::memcpy(data,value.data(),vl);
|
||||
::memcpy(vl+(char*)data,m_data,m_length);
|
||||
assign(data,len,false);
|
||||
}
|
||||
}
|
||||
else
|
||||
assign(value.data(),vl);
|
||||
}
|
||||
|
||||
bool DataBlock::convert(const DataBlock &src, const String &sFormat,
|
||||
const String &dFormat, unsigned maxlen)
|
||||
{
|
||||
if (sFormat == dFormat) {
|
||||
operator=(src);
|
||||
return true;
|
||||
}
|
||||
unsigned sl = 0, dl = 0;
|
||||
void *ctable = 0;
|
||||
if (sFormat == "slin") {
|
||||
sl = 2;
|
||||
dl = 1;
|
||||
if (dFormat == "alaw")
|
||||
ctable = s2a;
|
||||
else if (dFormat == "mulaw")
|
||||
ctable = s2u;
|
||||
}
|
||||
else if (sFormat == "alaw") {
|
||||
sl = 1;
|
||||
if (dFormat == "mulaw") {
|
||||
dl = 1;
|
||||
ctable = a2u;
|
||||
}
|
||||
else if (dFormat == "slin") {
|
||||
dl = 2;
|
||||
ctable = a2s;
|
||||
}
|
||||
}
|
||||
else if (sFormat == "mulaw") {
|
||||
sl = 1;
|
||||
if (dFormat == "alaw") {
|
||||
dl = 1;
|
||||
ctable = u2a;
|
||||
}
|
||||
else if (dFormat == "slin") {
|
||||
dl = 2;
|
||||
ctable = u2s;
|
||||
}
|
||||
}
|
||||
clear();
|
||||
if (!ctable)
|
||||
return false;
|
||||
unsigned len = src.length();
|
||||
if (maxlen && (maxlen < len))
|
||||
len = maxlen;
|
||||
len /= sl;
|
||||
if (!len)
|
||||
return true;
|
||||
assign(0,len*dl);
|
||||
if ((sl == 1) && (dl == 1)) {
|
||||
unsigned char *s = (unsigned char *) src.data();
|
||||
unsigned char *d = (unsigned char *) data();
|
||||
unsigned char *c = (unsigned char *) ctable;
|
||||
while (len--)
|
||||
*d++ = c[*s++];
|
||||
}
|
||||
else if ((sl == 1) && (dl == 2)) {
|
||||
unsigned char *s = (unsigned char *) src.data();
|
||||
unsigned short *d = (unsigned short *) data();
|
||||
unsigned short *c = (unsigned short *) ctable;
|
||||
while (len--)
|
||||
*d++ = c[*s++];
|
||||
}
|
||||
else if ((sl == 2) && (dl == 1)) {
|
||||
unsigned short *s = (unsigned short *) src.data();
|
||||
unsigned char *d = (unsigned char *) data();
|
||||
unsigned char *c = (unsigned char *) ctable;
|
||||
while (len--)
|
||||
*d++ = c[*s++];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataSource::Forward(const DataBlock &data)
|
||||
{
|
||||
Lock lock(m_mutex);
|
||||
ref();
|
||||
ObjList *l = &m_consumers;
|
||||
for (; l; l=l->next()) {
|
||||
DataConsumer *c = static_cast<DataConsumer *>(l->get());
|
||||
if (c)
|
||||
c->Consume(data);
|
||||
}
|
||||
deref();
|
||||
}
|
||||
|
||||
bool DataSource::attach(DataConsumer *consumer)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"DataSource [%p] attaching consumer [%p]",this,consumer);
|
||||
#endif
|
||||
Lock lock(m_mutex);
|
||||
consumer->ref();
|
||||
if (consumer->getConnSource())
|
||||
consumer->getConnSource()->detach(consumer);
|
||||
m_consumers.append(consumer);
|
||||
consumer->setSource(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DataSource::detach(DataConsumer *consumer)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"DataSource [%p] detaching consumer [%p]",this,consumer);
|
||||
#endif
|
||||
Lock lock(m_mutex);
|
||||
DataConsumer *temp = static_cast<DataConsumer *>(m_consumers.remove(consumer,false));
|
||||
if (temp) {
|
||||
temp->setSource(0);
|
||||
temp->deref();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DataEndpoint::~DataEndpoint()
|
||||
{
|
||||
disconnect();
|
||||
setSource();
|
||||
setConsumer();
|
||||
}
|
||||
|
||||
bool DataEndpoint::connect(DataEndpoint *peer)
|
||||
{
|
||||
Debug(DebugInfo,"DataEndpoint peer address is [%p]",peer);
|
||||
if (!peer) {
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
if (peer == m_peer)
|
||||
return true;
|
||||
|
||||
ref();
|
||||
disconnect();
|
||||
peer->ref();
|
||||
peer->disconnect();
|
||||
bool native = (name() == peer->name()) && nativeConnect(peer);
|
||||
|
||||
if (!native) {
|
||||
DataSource *s = getSource();
|
||||
DataConsumer *c = peer->getConsumer();
|
||||
if (s && c)
|
||||
DataTranslator::attachChain(s,c);
|
||||
|
||||
s = peer->getSource();
|
||||
c = getConsumer();
|
||||
if (s && c)
|
||||
DataTranslator::attachChain(s,c);
|
||||
}
|
||||
|
||||
m_peer = peer;
|
||||
peer->setPeer(this);
|
||||
connected();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataEndpoint::disconnect()
|
||||
{
|
||||
if (!m_peer)
|
||||
return;
|
||||
|
||||
DataSource *s = getSource();
|
||||
DataConsumer *c = m_peer->getConsumer();
|
||||
if (s && c)
|
||||
DataTranslator::detachChain(s,c);
|
||||
|
||||
s = m_peer->getSource();
|
||||
c = getConsumer();
|
||||
if (s && c)
|
||||
DataTranslator::detachChain(s,c);
|
||||
|
||||
DataEndpoint *temp = m_peer;
|
||||
m_peer = 0;
|
||||
temp->setPeer(0);
|
||||
temp->deref();
|
||||
disconnected();
|
||||
deref();
|
||||
}
|
||||
|
||||
void DataEndpoint::setPeer(DataEndpoint *peer)
|
||||
{
|
||||
m_peer = peer;
|
||||
if (m_peer)
|
||||
connected();
|
||||
else
|
||||
disconnected();
|
||||
}
|
||||
|
||||
void DataEndpoint::setSource(DataSource *source)
|
||||
{
|
||||
if (source == m_source)
|
||||
return;
|
||||
DataConsumer *consumer = m_peer ? m_peer->getConsumer() : 0;
|
||||
DataSource *temp = m_source;
|
||||
if (source) {
|
||||
source->ref();
|
||||
if (consumer)
|
||||
DataTranslator::attachChain(source,consumer);
|
||||
}
|
||||
m_source = source;
|
||||
if (temp) {
|
||||
if (consumer)
|
||||
DataTranslator::detachChain(temp,consumer);
|
||||
temp->deref();
|
||||
}
|
||||
}
|
||||
|
||||
void DataEndpoint::setConsumer(DataConsumer *consumer)
|
||||
{
|
||||
if (consumer == m_consumer)
|
||||
return;
|
||||
DataSource *source = m_peer ? m_peer->getSource() : 0;
|
||||
DataConsumer *temp = m_consumer;
|
||||
if (consumer) {
|
||||
consumer->ref();
|
||||
if (source)
|
||||
DataTranslator::attachChain(source,consumer);
|
||||
}
|
||||
m_consumer = consumer;
|
||||
if (temp) {
|
||||
if (source)
|
||||
DataTranslator::detachChain(source,temp);
|
||||
temp->deref();
|
||||
}
|
||||
}
|
||||
|
||||
ThreadedSource::~ThreadedSource()
|
||||
{
|
||||
if (m_thread)
|
||||
delete m_thread;
|
||||
}
|
||||
|
||||
void ThreadedSource::start(const char *name)
|
||||
{
|
||||
if (!m_thread)
|
||||
m_thread = new ThreadedSourcePrivate(this,name);
|
||||
}
|
||||
|
||||
void ThreadedSource::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
DataTranslator::DataTranslator(const char *sFormat, const char *dFormat)
|
||||
: DataConsumer(sFormat)
|
||||
{
|
||||
m_tsource = new DataSource(dFormat);
|
||||
m_tsource->setTranslator(this);
|
||||
}
|
||||
|
||||
DataTranslator::DataTranslator(const char *sFormat, DataSource *source)
|
||||
: DataConsumer(sFormat), m_tsource(source)
|
||||
{
|
||||
m_tsource->setTranslator(this);
|
||||
}
|
||||
|
||||
DataTranslator::~DataTranslator()
|
||||
{
|
||||
DataSource *temp = m_tsource;
|
||||
m_tsource = 0;
|
||||
if (temp) {
|
||||
temp->setTranslator(0);
|
||||
temp->deref();
|
||||
}
|
||||
}
|
||||
|
||||
Mutex DataTranslator::s_mutex;
|
||||
ObjList DataTranslator::s_factories;
|
||||
|
||||
void DataTranslator::install(TranslatorFactory *factory)
|
||||
{
|
||||
s_mutex.lock();
|
||||
s_factories.append(factory);
|
||||
s_mutex.unlock();
|
||||
}
|
||||
|
||||
void DataTranslator::uninstall(TranslatorFactory *factory)
|
||||
{
|
||||
s_mutex.lock();
|
||||
s_factories.remove(factory,false);
|
||||
s_mutex.unlock();
|
||||
}
|
||||
|
||||
DataTranslator *DataTranslator::create(const String &sFormat, const String &dFormat)
|
||||
{
|
||||
if (sFormat == dFormat) {
|
||||
Debug(DebugInfo,"Not creating identity DataTranslator for \"%s\"",sFormat.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
DataTranslator *trans = 0;
|
||||
|
||||
s_mutex.lock();
|
||||
ObjList *l = &s_factories;
|
||||
for (; l; l=l->next()) {
|
||||
TranslatorFactory *f = static_cast<TranslatorFactory *>(l->get());
|
||||
if (f) {
|
||||
trans = f->create(sFormat,dFormat);
|
||||
if (trans)
|
||||
break;
|
||||
}
|
||||
}
|
||||
s_mutex.unlock();
|
||||
|
||||
|
||||
if (!trans) {
|
||||
DataBlock empty,probe;
|
||||
if (probe.convert(empty,sFormat,dFormat))
|
||||
trans = new SimpleTranslator(sFormat,dFormat);
|
||||
}
|
||||
|
||||
if (trans)
|
||||
Debug(DebugAll,"Created DataTranslator [%p] for \"%s\" -> \"%s\"",
|
||||
trans,sFormat.c_str(),dFormat.c_str());
|
||||
else
|
||||
Debug(DebugWarn,"No DataTranslator created for \"%s\" -> \"%s\"",
|
||||
sFormat.c_str(),dFormat.c_str());
|
||||
return trans;
|
||||
}
|
||||
|
||||
bool DataTranslator::attachChain(DataSource *source, DataConsumer *consumer)
|
||||
{
|
||||
if (!source || !consumer)
|
||||
return false;
|
||||
|
||||
bool retv = false;
|
||||
if (source->getFormat() == consumer->getFormat()) {
|
||||
source->attach(consumer);
|
||||
retv = true;
|
||||
}
|
||||
else {
|
||||
// TODO: try to create a chain of translators, recurse if we have to
|
||||
DataTranslator *trans = create(source->getFormat(),consumer->getFormat());
|
||||
if (trans) {
|
||||
trans->getTransSource()->attach(consumer);
|
||||
source->attach(trans);
|
||||
retv = true;
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
Debug(DebugAll,"DataTranslator::attachChain [%p] \"%s\" -> [%p] \"%s\" %s",
|
||||
source,source->getFormat().c_str(),consumer,consumer->getFormat().c_str(),
|
||||
retv ? "succeeded" : "failed");
|
||||
#endif
|
||||
return retv;
|
||||
}
|
||||
|
||||
bool DataTranslator::detachChain(DataSource *source, DataConsumer *consumer)
|
||||
{
|
||||
Debugger debug(DebugAll,"DataTranslator::detachChain","(%p,%p)",source,consumer);
|
||||
if (!source || !consumer)
|
||||
return false;
|
||||
|
||||
if (source->detach(consumer))
|
||||
return true;
|
||||
|
||||
DataSource *tsource = consumer->getConnSource();
|
||||
if (tsource) {
|
||||
DataTranslator *trans = tsource->getTranslator();
|
||||
if (trans && detachChain(source,trans)) {
|
||||
trans->deref();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Debug(DebugWarn,"DataTranslator failed to detach chain [%p] -> [%p]",source,consumer);
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,532 @@
|
|||
/**
|
||||
* Engine.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telengine.h"
|
||||
#include "yatepaths.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace TelEngine {
|
||||
|
||||
class EnginePrivate : public Thread
|
||||
{
|
||||
public:
|
||||
EnginePrivate()
|
||||
: Thread("EnginePrivate")
|
||||
{ count++; }
|
||||
~EnginePrivate()
|
||||
{ count--; }
|
||||
virtual void run();
|
||||
static int count;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
#ifndef MOD_PATH
|
||||
#define MOD_PATH "./modules"
|
||||
#endif
|
||||
#ifndef CFG_PATH
|
||||
#define CFG_PATH "./conf.d"
|
||||
#endif
|
||||
#define DLL_SUFFIX ".yate"
|
||||
#define CFG_SUFFIX ".conf"
|
||||
|
||||
static unsigned long long s_nextinit = 0;
|
||||
static bool s_makeworker = true;
|
||||
static bool s_keepclosing = false;
|
||||
|
||||
static void sighandler(int signal)
|
||||
{
|
||||
switch (signal) {
|
||||
case SIGHUP:
|
||||
case SIGQUIT:
|
||||
if (s_nextinit <= Time::now())
|
||||
Engine::init();
|
||||
s_nextinit = Time::now() + 2000000;
|
||||
break;
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
Engine::halt(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String Engine::s_cfgpath(CFG_PATH);
|
||||
String Engine::s_cfgsuffix(CFG_SUFFIX);
|
||||
String Engine::s_modpath(MOD_PATH);
|
||||
String Engine::s_modsuffix(DLL_SUFFIX);
|
||||
|
||||
Engine *Engine::s_self = 0;
|
||||
int Engine::s_haltcode = -1;
|
||||
bool Engine::s_init = false;
|
||||
bool Engine::s_dynplugin = false;
|
||||
int Engine::s_maxworkers = 10;
|
||||
int EnginePrivate::count = 0;
|
||||
|
||||
ObjList plugins;
|
||||
|
||||
class SLib : public GenObject
|
||||
{
|
||||
public:
|
||||
virtual ~SLib();
|
||||
static SLib *load(const char *file);
|
||||
private:
|
||||
SLib(void *handle, const char *file);
|
||||
const char *m_file;
|
||||
void *m_handle;
|
||||
};
|
||||
|
||||
SLib::SLib(void *handle, const char *file)
|
||||
: m_handle(handle)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"SLib::SLib(%p,\"%s\") [%p]",handle,file,this);
|
||||
#endif
|
||||
}
|
||||
|
||||
SLib::~SLib()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("SLib::~SLib()"," [%p]",this);
|
||||
#endif
|
||||
int err = dlclose(m_handle);
|
||||
if (err)
|
||||
Debug(DebugGoOn,"Error %d on dlclose(%p)",err,m_handle);
|
||||
else if (s_keepclosing) {
|
||||
int tries;
|
||||
for (tries=0; tries<10; tries++)
|
||||
if (dlclose(m_handle))
|
||||
break;
|
||||
if (tries)
|
||||
Debug(DebugGoOn,"Made %d attempts to dlclose(%p)",tries,m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
SLib *SLib::load(const char *file)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("SLib::load","(\"%s\")",file);
|
||||
#endif
|
||||
void *handle = ::dlopen(file,RTLD_NOW);
|
||||
if (handle)
|
||||
return new SLib(handle,file);
|
||||
Debug(DebugWarn,dlerror());
|
||||
return 0;
|
||||
}
|
||||
|
||||
class EngineStatusHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
EngineStatusHandler() : MessageHandler("status",0) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
bool EngineStatusHandler::received(Message &msg)
|
||||
{
|
||||
const char *sel = msg.getValue("module");
|
||||
if (sel && ::strcmp(sel,"engine"))
|
||||
return false;
|
||||
msg.retValue() << "engine";
|
||||
msg.retValue() << ",plugins=" << plugins.count();
|
||||
msg.retValue() << ",workers=" << EnginePrivate::count;
|
||||
msg.retValue() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
void EnginePrivate::run()
|
||||
{
|
||||
for (;;) {
|
||||
s_makeworker = false;
|
||||
Engine::self()->m_dispatcher.dequeue();
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
Engine::Engine()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("Engine::Engine()"," [%p]",this);
|
||||
#endif
|
||||
}
|
||||
|
||||
Engine::~Engine()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("Engine::~Engine()"," [%p]",this);
|
||||
#endif
|
||||
assert(this == s_self);
|
||||
m_dispatcher.clear();
|
||||
plugins.clear();
|
||||
s_self = 0;
|
||||
}
|
||||
|
||||
int Engine::run()
|
||||
{
|
||||
Debug(DebugInfo,"Engine::run()");
|
||||
install(new EngineStatusHandler);
|
||||
loadPlugins();
|
||||
Debug(DebugInfo,"plugins.count() = %d",plugins.count());
|
||||
initPlugins();
|
||||
::signal(SIGINT,sighandler);
|
||||
::signal(SIGTERM,sighandler);
|
||||
Debug(DebugInfo,"Engine entering main loop");
|
||||
dispatch("engine.start");
|
||||
unsigned long corr = 0;
|
||||
::signal(SIGHUP,sighandler);
|
||||
::signal(SIGQUIT,sighandler);
|
||||
while (s_haltcode == -1) {
|
||||
if (s_init) {
|
||||
s_init = false;
|
||||
initPlugins();
|
||||
}
|
||||
|
||||
// Create worker thread if we didn't hear about any of them in a while
|
||||
if (s_makeworker && (EnginePrivate::count < s_maxworkers)) {
|
||||
Debug(DebugInfo,"Creating new message dispatching thread");
|
||||
new EnginePrivate;
|
||||
}
|
||||
s_makeworker = true;
|
||||
|
||||
// Attempt to sleep until the next full second
|
||||
unsigned long t = (Time::now() + corr) % 1000000;
|
||||
::usleep(1000000 - t);
|
||||
Message m("engine.timer");
|
||||
m.addParam("time",String((int)m.msgTime().sec()));
|
||||
// Try to fine tune the ticker
|
||||
t = m.msgTime().usec() % 1000000;
|
||||
if (t > 500000)
|
||||
corr -= (1000000-t)/10;
|
||||
else
|
||||
corr += t/10;
|
||||
dispatch(&m);
|
||||
}
|
||||
Debug(DebugInfo,"Engine exiting with code %d",s_haltcode);
|
||||
dispatch("engine.halt");
|
||||
m_dispatcher.dequeue();
|
||||
Thread::killall();
|
||||
m_dispatcher.dequeue();
|
||||
::signal(SIGINT,SIG_DFL);
|
||||
::signal(SIGTERM,SIG_DFL);
|
||||
::signal(SIGHUP,SIG_DFL);
|
||||
::signal(SIGQUIT,SIG_DFL);
|
||||
delete this;
|
||||
Debug(DebugInfo,"Exiting with %d locked mutexes",Mutex::locks());
|
||||
return s_haltcode;
|
||||
}
|
||||
|
||||
Engine *Engine::self()
|
||||
{
|
||||
if (!s_self)
|
||||
s_self = new Engine;
|
||||
return s_self;
|
||||
}
|
||||
|
||||
bool Engine::Register(const Plugin *plugin, bool reg)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"Engine::Register(%p,%d)",plugin,reg);
|
||||
#endif
|
||||
ObjList *p = plugins.find(plugin);
|
||||
if (reg) {
|
||||
if (p)
|
||||
return false;
|
||||
p = plugins.append(plugin);
|
||||
p->setDelete(s_dynplugin);
|
||||
}
|
||||
else if (p)
|
||||
p->remove(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Engine::loadPlugin(const char *file)
|
||||
{
|
||||
s_dynplugin = false;
|
||||
SLib *lib = SLib::load(file);
|
||||
s_dynplugin = true;
|
||||
if (lib) {
|
||||
m_libs.append(lib);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Engine::loadPlugins()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("Engine::loadPlugins()");
|
||||
#endif
|
||||
Configuration cfg(configFile("yate"));
|
||||
bool defload = cfg.getBoolValue("general","modload",true);
|
||||
const char *name = cfg.getValue("general","modpath");
|
||||
if (name)
|
||||
s_modpath = name;
|
||||
NamedList *l = cfg.getSection("preload");
|
||||
if (l) {
|
||||
unsigned int len = l->length();
|
||||
for (unsigned int i=0; i<len; i++) {
|
||||
NamedString *n = l->getParam(i);
|
||||
if (n && n->toBoolean())
|
||||
loadPlugin(n->name());
|
||||
}
|
||||
}
|
||||
DIR *dir = ::opendir(s_modpath);
|
||||
if (!dir) {
|
||||
Debug(DebugFail,"Engine::loadPlugins() failed opendir()");
|
||||
return;
|
||||
}
|
||||
struct dirent *entry;
|
||||
while ((entry = ::readdir(dir)) != 0) {
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"Found dir entry %s",entry->d_name);
|
||||
#endif
|
||||
int n = ::strlen(entry->d_name) - s_modsuffix.length();
|
||||
if ((n > 0) && !::strcmp(entry->d_name+n,s_modsuffix)) {
|
||||
if (cfg.getBoolValue("modules",entry->d_name,defload))
|
||||
loadPlugin(s_modpath+"/"+entry->d_name);
|
||||
}
|
||||
}
|
||||
::closedir(dir);
|
||||
l = cfg.getSection("postload");
|
||||
if (l) {
|
||||
unsigned int len = l->length();
|
||||
for (unsigned int i=0; i<len; i++) {
|
||||
NamedString *n = l->getParam(i);
|
||||
if (n && n->toBoolean())
|
||||
loadPlugin(n->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::initPlugins()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("Engine::initPlugins()");
|
||||
#else
|
||||
Debug(DebugInfo,"Engine::initPlugins()");
|
||||
#endif
|
||||
dispatch("engine.init");
|
||||
ObjList *l = &plugins;
|
||||
for (; l; l = l->next()) {
|
||||
Plugin *p = static_cast<Plugin *>(l->get());
|
||||
if (p)
|
||||
p->initialize();
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::halt(unsigned int code)
|
||||
{
|
||||
s_haltcode = code;
|
||||
}
|
||||
|
||||
void Engine::init()
|
||||
{
|
||||
s_init = true;
|
||||
}
|
||||
|
||||
bool Engine::install(MessageHandler *handler)
|
||||
{
|
||||
return s_self ? s_self->m_dispatcher.install(handler) : false;
|
||||
}
|
||||
|
||||
bool Engine::uninstall(MessageHandler *handler)
|
||||
{
|
||||
return s_self ? s_self->m_dispatcher.uninstall(handler) : false;
|
||||
}
|
||||
|
||||
bool Engine::enqueue(Message *msg)
|
||||
{
|
||||
return (msg && s_self) ? s_self->m_dispatcher.enqueue(msg) : false;
|
||||
}
|
||||
|
||||
bool Engine::dispatch(Message *msg)
|
||||
{
|
||||
return (msg && s_self) ? s_self->m_dispatcher.dispatch(*msg) : false;
|
||||
}
|
||||
|
||||
bool Engine::dispatch(Message &msg)
|
||||
{
|
||||
return s_self ? s_self->m_dispatcher.dispatch(msg) : false;
|
||||
}
|
||||
|
||||
bool Engine::dispatch(const char *name)
|
||||
{
|
||||
if (!(s_self && name && *name))
|
||||
return false;
|
||||
Message msg(name);
|
||||
return s_self->m_dispatcher.dispatch(msg);
|
||||
}
|
||||
|
||||
|
||||
static void usage(FILE *f)
|
||||
{
|
||||
::fprintf(f,
|
||||
"Usage: yate [options]\n"
|
||||
" -h Help message (this one)\n"
|
||||
" -v Verbose debugging (you can use more than once)\n"
|
||||
" -q Quieter debugging (you can use more than once)\n"
|
||||
" -d Daemonify, suppress output unless logged\n"
|
||||
" -l filename Log to file\n"
|
||||
" -c pathname Path to conf files directory (" CFG_PATH ")\n"
|
||||
" -m pathname Path to modules directory (" MOD_PATH ")\n"
|
||||
#ifndef NDEBUG
|
||||
" -D[options] Special debugging options\n"
|
||||
" c Call dlclose() until it gets an error\n"
|
||||
" i Reinitialize after 1st initialization\n"
|
||||
" x Exit immediately after initialization\n"
|
||||
" w Delay creation of 1st worker thread\n"
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
static void badopt(char chr, const char *opt)
|
||||
{
|
||||
if (chr)
|
||||
::fprintf(stderr,"Invalid character '%c' in option '%s'\n",chr,opt);
|
||||
else
|
||||
::fprintf(stderr,"Invalid option '%s'\n",opt);
|
||||
usage(stderr);
|
||||
}
|
||||
|
||||
static void noarg(const char *opt)
|
||||
{
|
||||
::fprintf(stderr,"Missing parameter to option '%s'\n",opt);
|
||||
usage(stderr);
|
||||
}
|
||||
|
||||
int Engine::main(int argc, const char **argv, const char **environ)
|
||||
{
|
||||
bool daemonic = false;
|
||||
int debug_level = debugLevel();
|
||||
const char *logfile = 0;
|
||||
int i;
|
||||
bool inopt = true;
|
||||
for (i=1;i<argc;i++) {
|
||||
const char *pc = argv[i];
|
||||
if (inopt && (pc[0] == '-') && pc[1]) {
|
||||
while (pc && *++pc) {
|
||||
switch (*pc) {
|
||||
case '-':
|
||||
if (!*++pc) {
|
||||
inopt=false;
|
||||
pc=0;
|
||||
continue;
|
||||
}
|
||||
if (!::strcmp(pc,"help")) {
|
||||
usage(stdout);
|
||||
return 0;
|
||||
}
|
||||
badopt(0,argv[i]);
|
||||
return EINVAL;
|
||||
break;
|
||||
case 'h':
|
||||
usage(stdout);
|
||||
return 0;
|
||||
case 'v':
|
||||
debug_level++;
|
||||
break;
|
||||
case 'q':
|
||||
debug_level--;
|
||||
break;
|
||||
case 'd':
|
||||
daemonic = true;
|
||||
break;
|
||||
case 'l':
|
||||
if (i+1 >= argc) {
|
||||
noarg(argv[i]);
|
||||
return ENOENT;
|
||||
}
|
||||
pc = 0;
|
||||
logfile=argv[++i];
|
||||
break;
|
||||
case 'c':
|
||||
if (i+1 >= argc) {
|
||||
noarg(argv[i]);
|
||||
return ENOENT;
|
||||
}
|
||||
pc = 0;
|
||||
s_cfgpath=argv[++i];
|
||||
break;
|
||||
case 'm':
|
||||
if (i+1 >= argc) {
|
||||
noarg(argv[i]);
|
||||
return ENOENT;
|
||||
}
|
||||
pc = 0;
|
||||
s_modpath=argv[++i];
|
||||
break;
|
||||
#ifndef NDEBUG
|
||||
case 'D':
|
||||
while (*++pc) {
|
||||
switch (*pc) {
|
||||
case 'c':
|
||||
s_keepclosing = true;
|
||||
break;
|
||||
case 'i':
|
||||
s_init = true;
|
||||
break;
|
||||
case 'x':
|
||||
s_haltcode++;
|
||||
break;
|
||||
case 'w':
|
||||
s_makeworker = false;
|
||||
break;
|
||||
default:
|
||||
badopt(*pc,argv[i]);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
pc = 0;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
badopt(*pc,argv[i]);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
::fprintf(stderr,"Invalid non-option '%s'\n",pc);
|
||||
usage(stderr);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
if (daemonic) {
|
||||
Debugger::enableOutput(false);
|
||||
if (::daemon(1,0) == -1) {
|
||||
int err = errno;
|
||||
::fprintf(stderr,"Daemonification failed: %s (%d)\n",::strerror(err),err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (logfile) {
|
||||
int fd = ::open(logfile,O_WRONLY|O_CREAT|O_APPEND,0640);
|
||||
if (fd >= 0) {
|
||||
// Redirect stdout and stderr to the new file
|
||||
::fflush(stdout);
|
||||
::dup2(fd,1);
|
||||
::fflush(stderr);
|
||||
::dup2(fd,2);
|
||||
::close(fd);
|
||||
Debugger::enableOutput(true);
|
||||
}
|
||||
}
|
||||
debugLevel(debug_level);
|
||||
return self()->run();
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
/**
|
||||
* Message.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telengine.h"
|
||||
#include <string.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
Message::Message(const char *name, const char *retval)
|
||||
: NamedList(name), m_return(retval), m_data(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"Message::Message(\"%s\",\"%s\") [%p]",name,retval,this);
|
||||
#endif
|
||||
}
|
||||
|
||||
String Message::encode(const char *id) const
|
||||
{
|
||||
String s("%%>message:");
|
||||
s << String::msgEscape(id,':') << ":" << (unsigned int)m_time.sec() << ":";
|
||||
commonEncode(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
String Message::encode(bool received, const char *id) const
|
||||
{
|
||||
String s("%%<message:");
|
||||
s << String::msgEscape(id,':') << ":" << received << ":";
|
||||
commonEncode(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
int Message::decode(const char *str, String &id)
|
||||
{
|
||||
String s("%%>message:");
|
||||
if (!str || ::strncmp(str,s.c_str(),s.length()))
|
||||
return -1;
|
||||
// locate the SEP after id
|
||||
const char *sep = ::strchr(str+s.length(),':');
|
||||
if (!sep)
|
||||
return s.length();
|
||||
// locate the SEP after time
|
||||
const char *sep2 = ::strchr(sep+1,':');
|
||||
if (!sep2)
|
||||
return sep-str;
|
||||
id.assign(str+s.length(),(sep-str)-s.length()-1);
|
||||
int err = -1;
|
||||
id = id.msgUnescape(&err,':');
|
||||
if (err >= 0)
|
||||
return err+s.length();
|
||||
String t(sep+1,sep2-sep-1);
|
||||
unsigned int tm;
|
||||
t >> tm;
|
||||
if (!t.null())
|
||||
return sep-str;
|
||||
m_time=1000000ULL*tm;
|
||||
return commonDecode(str,sep2-str);
|
||||
}
|
||||
|
||||
int Message::decode(const char *str, bool &received, const char *id)
|
||||
{
|
||||
String s("%%<message:");
|
||||
s << id << ":";
|
||||
if (!str || ::strncmp(str,s.c_str(),s.length()))
|
||||
return -1;
|
||||
// locate the SEP after received
|
||||
const char *sep = ::strchr(str+s.length(),':');
|
||||
if (!sep)
|
||||
return s.length();
|
||||
String rcvd(str+s.length(),(sep-str)-s.length()-1);
|
||||
rcvd >> received;
|
||||
if (!rcvd.null())
|
||||
return s.length();
|
||||
return commonDecode(str,sep-str);
|
||||
}
|
||||
|
||||
void Message::commonEncode(String &str) const
|
||||
{
|
||||
str << msgEscape(':') << ":" << m_return.msgEscape(':');
|
||||
unsigned n = length();
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
NamedString *s = getParam(i);
|
||||
if (s)
|
||||
str << ":" << s->name().msgEscape(':') << "=" << s->msgEscape(':');
|
||||
}
|
||||
}
|
||||
|
||||
int Message::commonDecode(const char *str, int offs)
|
||||
{
|
||||
str += offs;
|
||||
// locate SEP after name
|
||||
const char *sep = ::strchr(str,':');
|
||||
if (!sep)
|
||||
return offs;
|
||||
String chunk(str,sep-str);
|
||||
int err = -1;
|
||||
chunk = chunk.msgUnescape(&err,':');
|
||||
if (err >= 0)
|
||||
return offs+err;
|
||||
if (!chunk.null())
|
||||
*this = chunk;
|
||||
offs += (sep-str+1);
|
||||
str = sep+1;
|
||||
// locate SEP or EOL after retval
|
||||
sep = ::strchr(str,':');
|
||||
if (sep)
|
||||
chunk.assign(str,sep-str);
|
||||
else
|
||||
chunk.assign(str);
|
||||
chunk = chunk.msgUnescape(&err,':');
|
||||
if (err >= 0)
|
||||
return offs+err;
|
||||
m_return = chunk;
|
||||
// find and assign name=value pairs
|
||||
while (sep) {
|
||||
offs += (sep-str+1);
|
||||
str = sep+1;
|
||||
sep = ::strchr(str,':');
|
||||
if (sep)
|
||||
chunk.assign(str,sep-str);
|
||||
else
|
||||
chunk.assign(str);
|
||||
if (chunk.null())
|
||||
continue;
|
||||
chunk = chunk.msgUnescape(&err,':');
|
||||
if (err >= 0)
|
||||
return offs+err;
|
||||
int pos = chunk.find('=');
|
||||
switch (pos) {
|
||||
case -1:
|
||||
clearParam(chunk);
|
||||
break;
|
||||
case 0:
|
||||
return offs+err;
|
||||
default:
|
||||
setParam(chunk.substr(0,pos),chunk.substr(pos+1));
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
MessageHandler::MessageHandler(const char *name, unsigned priority)
|
||||
: String(name), m_priority(priority), m_dispatcher(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"MessageHandler::MessageHandler(\"%s\",%u) [%p]",name,priority,this);
|
||||
#endif
|
||||
}
|
||||
|
||||
MessageHandler::~MessageHandler()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"MessageHandler::~MessageHandler() [%p]",this);
|
||||
#endif
|
||||
if (m_dispatcher)
|
||||
m_dispatcher->uninstall(this);
|
||||
}
|
||||
|
||||
MessageDispatcher::MessageDispatcher()
|
||||
: m_hook(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"MessageDispatcher::MessageDispatcher() [%p]",this);
|
||||
#endif
|
||||
}
|
||||
|
||||
MessageDispatcher::~MessageDispatcher()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"MessageDispatcher::~MessageDispatcher() [%p]",this);
|
||||
#endif
|
||||
m_handlers.clear();
|
||||
}
|
||||
|
||||
bool MessageDispatcher::install(MessageHandler *handler)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"MessageDispatcher::install(%p)",handler);
|
||||
#endif
|
||||
if (!handler)
|
||||
return false;
|
||||
ObjList *l = m_handlers.find(handler);
|
||||
if (l)
|
||||
return false;
|
||||
unsigned p = handler->priority();
|
||||
int pos = 0;
|
||||
for (l=&m_handlers; l; l=l->next(),pos++) {
|
||||
MessageHandler *h = static_cast<MessageHandler *>(l->get());
|
||||
if (h && (h->priority() > p))
|
||||
break;
|
||||
}
|
||||
if (l) {
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"Inserting handler [%p] on place #%d",handler,pos);
|
||||
#endif
|
||||
l->insert(handler);
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"Appending handler [%p] on place #%d",handler,pos);
|
||||
#endif
|
||||
m_handlers.append(handler);
|
||||
}
|
||||
handler->m_dispatcher = this;
|
||||
if (handler->null())
|
||||
Debug(DebugInfo,"Registered broadcast message handler %p",handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessageDispatcher::uninstall(MessageHandler *handler)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"MessageDispatcher::uninstall(%p)",handler);
|
||||
#endif
|
||||
handler = static_cast<MessageHandler *>(m_handlers.remove(handler,false));
|
||||
if (handler)
|
||||
handler->m_dispatcher = 0;
|
||||
return (handler != 0);
|
||||
}
|
||||
|
||||
bool MessageDispatcher::dispatch(Message &msg)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("MessageDispatcher::dispatch","(%p) (\"%s\")",&msg,msg.c_str());
|
||||
#endif
|
||||
bool retv = false;
|
||||
ObjList *l = &m_handlers;
|
||||
for (; l; l=l->next()) {
|
||||
MessageHandler *h = static_cast<MessageHandler *>(l->get());
|
||||
if (h && (h->null() || *h == msg) && h->received(msg)) {
|
||||
retv = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
msg.dispatched(retv);
|
||||
if (m_hook)
|
||||
(*m_hook)(msg,retv);
|
||||
return retv;
|
||||
}
|
||||
|
||||
bool MessageDispatcher::enqueue(Message *msg)
|
||||
{
|
||||
if (!msg || m_messages.find(msg))
|
||||
return false;
|
||||
m_mutex.lock();
|
||||
m_messages.append(msg);
|
||||
m_mutex.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessageDispatcher::dequeueOne()
|
||||
{
|
||||
m_mutex.lock();
|
||||
Message *msg = static_cast<Message *>(m_messages.remove(false));
|
||||
m_mutex.unlock();
|
||||
if (!msg)
|
||||
return false;
|
||||
dispatch(*msg);
|
||||
msg->destruct();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessageDispatcher::dequeue()
|
||||
{
|
||||
while (dequeueOne())
|
||||
;
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/**
|
||||
* Mutex.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telengine.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace TelEngine {
|
||||
|
||||
class MutexPrivate {
|
||||
public:
|
||||
MutexPrivate();
|
||||
~MutexPrivate();
|
||||
inline void ref()
|
||||
{ ++m_refcount; }
|
||||
inline void deref()
|
||||
{ if (!--m_refcount) delete this; }
|
||||
bool lock(long long int maxwait);
|
||||
void unlock();
|
||||
static int m_count;
|
||||
static int m_locks;
|
||||
private:
|
||||
pthread_mutex_t m_mutex;
|
||||
int m_refcount;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
int MutexPrivate::m_count = 0;
|
||||
int MutexPrivate::m_locks = 0;
|
||||
|
||||
// WARNING!!!
|
||||
// No debug messages are allowed in mutexes since the debug output itself
|
||||
// is serialized using a mutex!
|
||||
|
||||
MutexPrivate::MutexPrivate()
|
||||
: m_refcount(1)
|
||||
{
|
||||
m_count++;
|
||||
::pthread_mutex_init(&m_mutex,0);
|
||||
}
|
||||
|
||||
MutexPrivate::~MutexPrivate()
|
||||
{
|
||||
m_count--;
|
||||
::pthread_mutex_destroy(&m_mutex);
|
||||
}
|
||||
|
||||
bool MutexPrivate::lock(long long int maxwait)
|
||||
{
|
||||
bool rval = false;
|
||||
ref();
|
||||
if (maxwait < 0)
|
||||
rval = !::pthread_mutex_lock(&m_mutex);
|
||||
else if (!maxwait)
|
||||
rval = !::pthread_mutex_trylock(&m_mutex);
|
||||
else {
|
||||
unsigned long long t = Time::now() + maxwait;
|
||||
do {
|
||||
rval = !::pthread_mutex_trylock(&m_mutex);
|
||||
if (rval)
|
||||
break;
|
||||
::usleep(1);
|
||||
} while (t > Time::now());
|
||||
}
|
||||
if (rval)
|
||||
m_locks++;
|
||||
else
|
||||
deref();
|
||||
return rval;
|
||||
}
|
||||
|
||||
void MutexPrivate::unlock()
|
||||
{
|
||||
::pthread_mutex_unlock(&m_mutex);
|
||||
m_locks--;
|
||||
deref();
|
||||
}
|
||||
|
||||
Mutex::Mutex()
|
||||
: m_private(0)
|
||||
{
|
||||
m_private = new MutexPrivate;
|
||||
}
|
||||
|
||||
Mutex::Mutex(const Mutex &original)
|
||||
: m_private(original.privDataCopy())
|
||||
{
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
MutexPrivate *priv = m_private;
|
||||
m_private = 0;
|
||||
if (priv)
|
||||
priv->deref();
|
||||
}
|
||||
|
||||
Mutex& Mutex::operator=(const Mutex &original)
|
||||
{
|
||||
MutexPrivate *priv = m_private;
|
||||
m_private = original.privDataCopy();
|
||||
if (priv)
|
||||
priv->deref();
|
||||
return *this;
|
||||
}
|
||||
|
||||
MutexPrivate *Mutex::privDataCopy() const
|
||||
{
|
||||
if (m_private)
|
||||
m_private->ref();
|
||||
return m_private;
|
||||
}
|
||||
|
||||
bool Mutex::lock(long long int maxwait)
|
||||
{
|
||||
return m_private ? m_private->lock(maxwait) : false;
|
||||
}
|
||||
|
||||
void Mutex::unlock()
|
||||
{
|
||||
if (m_private)
|
||||
m_private->unlock();
|
||||
}
|
||||
|
||||
int Mutex::count()
|
||||
{
|
||||
return MutexPrivate::m_count;
|
||||
}
|
||||
|
||||
int Mutex::locks()
|
||||
{
|
||||
return MutexPrivate::m_locks;
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* NamedList.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telengine.h"
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
NamedList::NamedList(const char *name)
|
||||
: String(name)
|
||||
{
|
||||
}
|
||||
|
||||
NamedList &NamedList::addParam(NamedString *param)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"NamedList::addParam(%p) [\"%s\",\"%s\"]",
|
||||
param,param->name().c_str(),param->c_str());
|
||||
#endif
|
||||
m_params.append(param);
|
||||
return *this;
|
||||
}
|
||||
|
||||
NamedList &NamedList::addParam(const char *name, const char *value)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"NamedList::addParam(\"%s\",\"%s\")",name,value);
|
||||
#endif
|
||||
m_params.append(new NamedString(name, value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
NamedList &NamedList::setParam(NamedString *param)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"NamedList::setParam(%p) [\"%s\",\"%s\"]",
|
||||
param,param->name().c_str(),param->c_str());
|
||||
#endif
|
||||
NamedString *s = getParam(param->name());
|
||||
if (s) {
|
||||
*s = param->c_str();
|
||||
param->destruct();
|
||||
}
|
||||
else
|
||||
m_params.append(param);
|
||||
return *this;
|
||||
}
|
||||
|
||||
NamedList &NamedList::setParam(const char *name, const char *value)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"NamedList::setParam(\"%s\",\"%s\")",name,value);
|
||||
#endif
|
||||
NamedString *s = getParam(name);
|
||||
if (s)
|
||||
*s = value;
|
||||
else
|
||||
m_params.append(new NamedString(name, value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
NamedList &NamedList::clearParam(const String &name)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"NamedList::clearParam(\"%s\")",name.c_str());
|
||||
#endif
|
||||
ObjList *p = &m_params;
|
||||
while (p) {
|
||||
NamedString *s = static_cast<NamedString *>(p->get());
|
||||
if (s && (s->name() == name))
|
||||
p->remove();
|
||||
else
|
||||
p = p->next();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
NamedString *NamedList::getParam(const String &name) const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"NamedList::getParam(\"%s\")",name.c_str());
|
||||
#endif
|
||||
const ObjList *p = &m_params;
|
||||
for (;p;p=p->next()) {
|
||||
NamedString *s = static_cast<NamedString *>(p->get());
|
||||
if (s && (s->name() == name))
|
||||
return s;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
NamedString *NamedList::getParam(unsigned int index) const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"NamedList::getParam(%u)",index);
|
||||
#endif
|
||||
const ObjList *p = m_params[index];
|
||||
return p ? static_cast<NamedString *>(p->get()) : 0;
|
||||
}
|
||||
|
||||
const char *NamedList::getValue(const String &name, const char *defvalue) const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"NamedList::getValue(\"%s\",\"%s\")",name.c_str(),defvalue);
|
||||
#endif
|
||||
const NamedString *s = getParam(name);
|
||||
return s ? s->c_str() : defvalue;
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
/**
|
||||
* ObjList.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telengine.h"
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
ObjList::ObjList()
|
||||
: m_next(0), m_obj(0), m_delete(true)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"ObjList::ObjList() [%p]",this);
|
||||
#endif
|
||||
}
|
||||
|
||||
ObjList::~ObjList()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("ObjList::~ObjList()"," [%p]",this);
|
||||
#endif
|
||||
if (m_obj) {
|
||||
GenObject *tmp = m_obj;
|
||||
m_obj = 0;
|
||||
if (m_delete) {
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"ObjList::~ObjList() deleting %p",tmp);
|
||||
#endif
|
||||
tmp->destruct();
|
||||
}
|
||||
}
|
||||
if (m_next)
|
||||
m_next->destruct();
|
||||
}
|
||||
|
||||
unsigned int ObjList::length() const
|
||||
{
|
||||
unsigned int c = 0;
|
||||
const ObjList *n = this;
|
||||
while (n) {
|
||||
c++;
|
||||
n = n->next();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
unsigned int ObjList::count() const
|
||||
{
|
||||
unsigned int c = 0;
|
||||
const ObjList *n = this;
|
||||
while (n) {
|
||||
if (n->get())
|
||||
c++;
|
||||
n = n->next();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
ObjList *ObjList::last() const
|
||||
{
|
||||
const ObjList *n = this;
|
||||
while (n->next())
|
||||
n = n->next();
|
||||
return const_cast<ObjList *>(n);
|
||||
}
|
||||
|
||||
ObjList *ObjList::operator[](int index) const
|
||||
{
|
||||
if (index < 0)
|
||||
return 0;
|
||||
ObjList *obj = const_cast<ObjList *>(this);
|
||||
for (;obj;obj=obj->next(),index--)
|
||||
if (!index) break;
|
||||
return obj;
|
||||
}
|
||||
|
||||
ObjList *ObjList::find(const GenObject *obj) const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("ObjList::find","(%p) [%p]",obj,this);
|
||||
#endif
|
||||
const ObjList *n = this;
|
||||
while (n && (n->get() != obj))
|
||||
n = n->next();
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"ObjList::find returning %p",n);
|
||||
#endif
|
||||
return const_cast<ObjList *>(n);
|
||||
}
|
||||
|
||||
GenObject *ObjList::set(const GenObject *obj, bool delold)
|
||||
{
|
||||
if (m_obj == obj)
|
||||
return 0;
|
||||
GenObject *tmp = m_obj;
|
||||
m_obj = const_cast<GenObject *>(obj);
|
||||
if (delold && tmp) {
|
||||
tmp->destruct();
|
||||
return 0;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
ObjList *ObjList::insert(const GenObject *obj)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("ObjList::insert","(%p) [%p]",obj,this);
|
||||
#endif
|
||||
if (m_obj) {
|
||||
ObjList *n = new ObjList();
|
||||
n->set(m_obj);
|
||||
set(obj,false);
|
||||
n->m_next = m_next;
|
||||
m_next = n;
|
||||
}
|
||||
else
|
||||
m_obj = const_cast<GenObject *>(obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
ObjList *ObjList::append(const GenObject *obj)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("ObjList::append","(%p) [%p]",obj,this);
|
||||
#endif
|
||||
ObjList *n = last();
|
||||
if (n->get()) {
|
||||
n->m_next = new ObjList();
|
||||
n = n->m_next;
|
||||
}
|
||||
n->set(obj);
|
||||
return n;
|
||||
}
|
||||
|
||||
GenObject *ObjList::remove(bool delobj)
|
||||
{
|
||||
GenObject *tmp = m_obj;
|
||||
|
||||
if (m_next) {
|
||||
ObjList *n = m_next;
|
||||
m_obj = n->get();
|
||||
m_next = n->next();
|
||||
n->m_obj = 0;
|
||||
n->m_next = 0;
|
||||
n->destruct();
|
||||
}
|
||||
else
|
||||
m_obj = 0;
|
||||
|
||||
if (delobj && tmp) {
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"ObjList::remove() deleting %p",tmp);
|
||||
#endif
|
||||
tmp->destruct();
|
||||
tmp = 0;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
GenObject *ObjList::remove(GenObject *obj, bool delobj)
|
||||
{
|
||||
ObjList *n = find(obj);
|
||||
return n ? n->remove(delobj) : 0;
|
||||
}
|
||||
|
||||
void ObjList::clear()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("ObjList::clear()"," [%p]",this);
|
||||
#endif
|
||||
ObjList *n = m_next;
|
||||
m_next = 0;
|
||||
remove(m_delete);
|
||||
if (n)
|
||||
n->destruct();
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* Plugin.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telengine.h"
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
Plugin::Plugin()
|
||||
{
|
||||
Debug(DebugAll,"Plugin::Plugin() [%p]",this);
|
||||
Engine::Register(this);
|
||||
}
|
||||
|
||||
Plugin::Plugin(const char *name)
|
||||
{
|
||||
Debug(DebugAll,"Plugin::Plugin(\"%s\") [%p]",name,this);
|
||||
Engine::Register(this);
|
||||
}
|
||||
|
||||
Plugin::~Plugin()
|
||||
{
|
||||
Debugger debug("Plugin::~Plugin()"," [%p]",this);
|
||||
Engine::Register(this,false);
|
||||
}
|
|
@ -0,0 +1,774 @@
|
|||
/**
|
||||
* String.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telengine.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <regex.h>
|
||||
|
||||
namespace TelEngine {
|
||||
|
||||
String operator+(const String &s1, const String &s2)
|
||||
{
|
||||
String s(s1.c_str());
|
||||
s += s2.c_str();
|
||||
return s;
|
||||
}
|
||||
|
||||
String operator+(const String &s1, const char *s2)
|
||||
{
|
||||
String s(s1.c_str());
|
||||
s += s2;
|
||||
return s;
|
||||
}
|
||||
|
||||
String operator+(const char *s1, const String &s2)
|
||||
{
|
||||
String s(s1);
|
||||
s += s2;
|
||||
return s;
|
||||
}
|
||||
|
||||
int lookup(const char *str, const TokenDict *tokens, int defvalue, int base)
|
||||
{
|
||||
if (!str)
|
||||
return defvalue;
|
||||
if (tokens) {
|
||||
for (; tokens->token; tokens++)
|
||||
if (!::strcmp(str,tokens->token))
|
||||
return tokens->value;
|
||||
}
|
||||
char *eptr = 0;
|
||||
long int val= ::strtol(str,&eptr,base);
|
||||
if (!eptr || *eptr)
|
||||
return defvalue;
|
||||
return val;
|
||||
}
|
||||
|
||||
const char *lookup(int value, const TokenDict *tokens, const char *defvalue)
|
||||
{
|
||||
if (tokens) {
|
||||
for (; tokens->token; tokens++)
|
||||
if (value == tokens->value)
|
||||
return tokens->token;
|
||||
}
|
||||
return defvalue;
|
||||
}
|
||||
|
||||
#define MAX_MATCH 9
|
||||
|
||||
class StringMatchPrivate
|
||||
{
|
||||
public:
|
||||
StringMatchPrivate();
|
||||
void fixup();
|
||||
void clear();
|
||||
int count;
|
||||
regmatch_t rmatch[MAX_MATCH+1];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static bool isWordBreak(char c, bool nullOk = false)
|
||||
{
|
||||
return (c == ' ' || c == '\t' || c == '\n' || (nullOk && !c));
|
||||
}
|
||||
|
||||
StringMatchPrivate::StringMatchPrivate()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"StringMatchPrivate::StringMatchPrivate() [%p]",this);
|
||||
#endif
|
||||
clear();
|
||||
}
|
||||
|
||||
void StringMatchPrivate::clear()
|
||||
{
|
||||
count = 0;
|
||||
for (int i = 0; i <= MAX_MATCH; i++) {
|
||||
rmatch[i].rm_so = -1;
|
||||
rmatch[i].rm_eo = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void StringMatchPrivate::fixup()
|
||||
{
|
||||
count = 0;
|
||||
rmatch[0].rm_so = rmatch[1].rm_so;
|
||||
rmatch[0].rm_eo = 0;
|
||||
int i, c = 0;
|
||||
for (i = 1; i <= MAX_MATCH; i++) {
|
||||
if (rmatch[i].rm_so != -1) {
|
||||
rmatch[0].rm_eo = rmatch[i].rm_eo - rmatch[0].rm_so;
|
||||
rmatch[i].rm_eo -= rmatch[i].rm_so;
|
||||
c = i;
|
||||
}
|
||||
}
|
||||
// Cope with the regexp stupidity.
|
||||
if (c > 1) {
|
||||
for (i = 0; i < c; i++)
|
||||
rmatch[i] = rmatch[i+1];
|
||||
rmatch[c].rm_so = -1;
|
||||
c--;
|
||||
}
|
||||
count = c;
|
||||
}
|
||||
|
||||
String::String()
|
||||
: m_string(0), m_length(0), m_hash(0), m_matches(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"String::String() [%p]",this);
|
||||
#endif
|
||||
}
|
||||
|
||||
String::String(const char *value, int len)
|
||||
: m_string(0), m_length(0), m_hash(0), m_matches(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"String::String(\"%s\",%d) [%p]",value,len,this);
|
||||
#endif
|
||||
assign(value,len);
|
||||
}
|
||||
|
||||
String::String(const String &value)
|
||||
: m_string(0), m_length(0), m_hash(0), m_matches(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"String::String(%p) [%p]",&value,this);
|
||||
#endif
|
||||
if (!value.null()) {
|
||||
m_string = ::strdup(value.c_str());
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
String::String(char value, unsigned int repeat)
|
||||
: m_string(0), m_length(0), m_hash(0), m_matches(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"String::String('%c',%d) [%p]",value,repeat,this);
|
||||
#endif
|
||||
if (value && repeat) {
|
||||
m_string = (char *) ::malloc(repeat+1);
|
||||
::memset(m_string,value,repeat);
|
||||
m_string[repeat] = 0;
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
String::String(int value)
|
||||
: m_string(0), m_length(0), m_hash(0), m_matches(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"String::String(%d) [%p]",value,this);
|
||||
#endif
|
||||
char buf[64];
|
||||
::sprintf(buf,"%d",value);
|
||||
m_string = ::strdup(buf);
|
||||
changed();
|
||||
}
|
||||
|
||||
String::String(unsigned int value)
|
||||
: m_string(0), m_length(0), m_hash(0), m_matches(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"String::String(%u) [%p]",value,this);
|
||||
#endif
|
||||
char buf[64];
|
||||
::sprintf(buf,"%u",value);
|
||||
m_string = ::strdup(buf);
|
||||
changed();
|
||||
}
|
||||
|
||||
String::String(bool value)
|
||||
: m_string(0), m_length(0), m_hash(0), m_matches(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"String::String(%u) [%p]",value,this);
|
||||
#endif
|
||||
m_string = ::strdup(value ? "true" : "false");
|
||||
changed();
|
||||
}
|
||||
|
||||
String::~String()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"String::~String() [%p] (\"%s\")",this,m_string);
|
||||
#endif
|
||||
if (m_matches) {
|
||||
StringMatchPrivate *odata = m_matches;
|
||||
m_matches = 0;
|
||||
delete odata;
|
||||
}
|
||||
if (m_string) {
|
||||
char *odata = m_string;
|
||||
m_length = 0;
|
||||
m_string = 0;
|
||||
::free(odata);
|
||||
}
|
||||
}
|
||||
|
||||
String& String::assign(const char *value, int len)
|
||||
{
|
||||
if (len && value && *value) {
|
||||
if (len < 0)
|
||||
len = ::strlen(value);
|
||||
if (value != m_string || len != (int)m_length) {
|
||||
char *data = (char *) ::malloc(len+1);
|
||||
::memcpy(data,value,len);
|
||||
data[len] = 0;
|
||||
char *odata = m_string;
|
||||
m_string = data;
|
||||
changed();
|
||||
if (odata)
|
||||
::free(odata);
|
||||
}
|
||||
}
|
||||
else
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void String::changed()
|
||||
{
|
||||
clearMatches();
|
||||
m_hash = 0;
|
||||
m_length = m_string ? ::strlen(m_string) : 0;
|
||||
}
|
||||
|
||||
void String::clear()
|
||||
{
|
||||
if (m_string) {
|
||||
char *odata = m_string;
|
||||
m_string = 0;
|
||||
changed();
|
||||
::free(odata);
|
||||
}
|
||||
}
|
||||
|
||||
char String::at(int index) const
|
||||
{
|
||||
if ((index < 0) || ((unsigned)index >= m_length) || !m_string)
|
||||
return 0;
|
||||
return m_string[index];
|
||||
}
|
||||
|
||||
String String::substr(int offs, int len) const
|
||||
{
|
||||
if (offs < 0) {
|
||||
offs += m_length;
|
||||
if (offs < 0)
|
||||
offs = 0;
|
||||
}
|
||||
if ((unsigned int)offs >= m_length)
|
||||
return String();
|
||||
return String(c_str()+offs,len);
|
||||
}
|
||||
|
||||
int String::toInteger(int defvalue, int base) const
|
||||
{
|
||||
if (!m_string)
|
||||
return defvalue;
|
||||
char *eptr = 0;
|
||||
long int val= ::strtol(m_string,&eptr,base);
|
||||
if (!eptr || *eptr)
|
||||
return defvalue;
|
||||
return val;
|
||||
}
|
||||
|
||||
int String::toInteger(const TokenDict *tokens, int defvalue, int base) const
|
||||
{
|
||||
if (!m_string)
|
||||
return defvalue;
|
||||
if (tokens) {
|
||||
for (; tokens->token; tokens++)
|
||||
if (operator==(tokens->token))
|
||||
return tokens->value;
|
||||
}
|
||||
return toInteger(defvalue,base);
|
||||
}
|
||||
|
||||
static const char *str_false[] = { "false", "no", "off", "disable", 0 };
|
||||
static const char *str_true[] = { "true", "yes", "on", "enable", 0 };
|
||||
|
||||
bool String::toBoolean(bool defvalue) const
|
||||
{
|
||||
if (!m_string)
|
||||
return defvalue;
|
||||
const char **test;
|
||||
for (test=str_false; *test; test++)
|
||||
if (!::strcmp(m_string,*test))
|
||||
return false;
|
||||
for (test=str_true; *test; test++)
|
||||
if (!::strcmp(m_string,*test))
|
||||
return true;
|
||||
return defvalue;
|
||||
}
|
||||
|
||||
String& String::trimBlanks()
|
||||
{
|
||||
if (m_string) {
|
||||
const char *s = m_string;
|
||||
while (*s == ' ' || *s == '\t')
|
||||
s++;
|
||||
const char *e = s;
|
||||
for (const char *p = e; *p; p++)
|
||||
if (*p != ' ' && *p != '\t')
|
||||
e = p+1;
|
||||
assign(s,e-s);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator=(const char *value)
|
||||
{
|
||||
if (value != c_str()) {
|
||||
char *tmp = m_string;
|
||||
m_string = value ? ::strdup(value) : 0;
|
||||
changed();
|
||||
if (tmp)
|
||||
::free(tmp);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator+=(const char *value)
|
||||
{
|
||||
if (value && *value) {
|
||||
if (m_string) {
|
||||
char *tmp1 = m_string;
|
||||
char *tmp2 = (char *) ::malloc(::strlen(value)+length()+1);
|
||||
::strcpy(tmp2,m_string);
|
||||
::strcat(tmp2,value);
|
||||
m_string = tmp2;
|
||||
::free(tmp1);
|
||||
}
|
||||
else
|
||||
m_string = ::strdup(value);
|
||||
changed();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator=(char value)
|
||||
{
|
||||
char buf[2] = {value,0};
|
||||
return operator=(buf);
|
||||
}
|
||||
|
||||
String& String::operator=(int value)
|
||||
{
|
||||
char buf[64];
|
||||
::sprintf(buf,"%d",value);
|
||||
return operator=(buf);
|
||||
}
|
||||
|
||||
String& String::operator=(unsigned int value)
|
||||
{
|
||||
char buf[64];
|
||||
::sprintf(buf,"%u",value);
|
||||
return operator=(buf);
|
||||
}
|
||||
|
||||
String& String::operator+=(char value)
|
||||
{
|
||||
char buf[2] = {value,0};
|
||||
return operator+=(buf);
|
||||
}
|
||||
|
||||
String& String::operator+=(int value)
|
||||
{
|
||||
char buf[64];
|
||||
::sprintf(buf,"%d",value);
|
||||
return operator+=(buf);
|
||||
}
|
||||
|
||||
String& String::operator+=(unsigned int value)
|
||||
{
|
||||
char buf[64];
|
||||
::sprintf(buf,"%u",value);
|
||||
return operator+=(buf);
|
||||
}
|
||||
|
||||
String& String::operator>>(const char *skip)
|
||||
{
|
||||
if (m_string && skip && *skip) {
|
||||
const char *loc = ::strstr(m_string,skip);
|
||||
if (loc)
|
||||
assign(loc+::strlen(skip));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator>>(char &store)
|
||||
{
|
||||
if (m_string) {
|
||||
store = m_string[0];
|
||||
assign(m_string+1);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator>>(int &store)
|
||||
{
|
||||
if (m_string) {
|
||||
char *end = 0;
|
||||
long int l = ::strtol(m_string,&end,0);
|
||||
if (end && (m_string != end)) {
|
||||
store = l;
|
||||
assign(end);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator>>(unsigned int &store)
|
||||
{
|
||||
if (m_string) {
|
||||
char *end = 0;
|
||||
unsigned long int l = ::strtoul(m_string,&end,0);
|
||||
if (end && (m_string != end)) {
|
||||
store = l;
|
||||
assign(end);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator>>(bool &store)
|
||||
{
|
||||
if (m_string) {
|
||||
const char *s = m_string;
|
||||
while (*s == ' ' || *s == '\t')
|
||||
s++;
|
||||
const char **test;
|
||||
for (test=str_false; *test; test++) {
|
||||
int l = ::strlen(*test);
|
||||
if (!::strncmp(s,*test,l) && isWordBreak(s[l],true)) {
|
||||
store = false;
|
||||
assign(s+l);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
for (test=str_true; *test; test++) {
|
||||
int l = ::strlen(*test);
|
||||
if (!::strncmp(s,*test,l) && isWordBreak(s[l],true)) {
|
||||
store = true;
|
||||
assign(s+l);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool String::operator==(const char *value) const
|
||||
{
|
||||
if (!m_string)
|
||||
return !(value && *value);
|
||||
return value && !::strcmp(m_string,value);
|
||||
}
|
||||
|
||||
bool String::operator!=(const char *value) const
|
||||
{
|
||||
if (!m_string)
|
||||
return value && *value;
|
||||
return (!value) || ::strcmp(m_string,value);
|
||||
}
|
||||
|
||||
bool String::operator==(const String &value) const
|
||||
{
|
||||
if (hash() != value.hash())
|
||||
return false;
|
||||
return operator==(value.c_str());
|
||||
}
|
||||
|
||||
bool String::operator!=(const String &value) const
|
||||
{
|
||||
if (hash() != value.hash())
|
||||
return true;
|
||||
return operator!=(value.c_str());
|
||||
}
|
||||
|
||||
int String::find(char what, unsigned int offs) const
|
||||
{
|
||||
if (!m_string || (offs > m_length))
|
||||
return -1;
|
||||
const char *s = ::strchr(m_string+offs,what);
|
||||
return s ? s-m_string : -1;
|
||||
}
|
||||
|
||||
int String::find(const char *what, unsigned int offs) const
|
||||
{
|
||||
if (!(m_string && what && *what) || (offs > m_length))
|
||||
return -1;
|
||||
const char *s = ::strstr(m_string+offs,what);
|
||||
return s ? s-m_string : -1;
|
||||
}
|
||||
|
||||
int String::rfind(char what) const
|
||||
{
|
||||
if (!m_string)
|
||||
return -1;
|
||||
const char *s = ::strrchr(m_string,what);
|
||||
return s ? s-m_string : -1;
|
||||
}
|
||||
|
||||
bool String::startsWith(const char *what, bool wordBreak) const
|
||||
{
|
||||
if (!(m_string && what && *what))
|
||||
return false;
|
||||
unsigned int l = ::strlen(what);
|
||||
if (m_length < l)
|
||||
return false;
|
||||
else if (wordBreak && (m_length > l) && !isWordBreak(m_string[l]))
|
||||
return false;
|
||||
return (::strncmp(m_string,what,l) == 0);
|
||||
}
|
||||
|
||||
bool String::endsWith(const char *what, bool wordBreak) const
|
||||
{
|
||||
if (!(m_string && what && *what))
|
||||
return false;
|
||||
unsigned int l = ::strlen(what);
|
||||
if (m_length < l)
|
||||
return false;
|
||||
else if (wordBreak && (m_length > l) && !isWordBreak(m_string[m_length-l-1]))
|
||||
return false;
|
||||
return (::strncmp(m_string+m_length-l,what,l) == 0);
|
||||
}
|
||||
|
||||
bool String::matches(Regexp &rexp)
|
||||
{
|
||||
if (m_matches)
|
||||
clearMatches();
|
||||
else
|
||||
m_matches = new StringMatchPrivate;
|
||||
if (rexp.matches(c_str(),m_matches)) {
|
||||
m_matches->fixup();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int String::matchOffset(int index) const
|
||||
{
|
||||
if ((!m_matches) || (index < 0) || (index > m_matches->count))
|
||||
return -1;
|
||||
return m_matches->rmatch[index].rm_so;
|
||||
}
|
||||
|
||||
int String::matchLength(int index) const
|
||||
{
|
||||
if ((!m_matches) || (index < 0) || (index > m_matches->count))
|
||||
return 0;
|
||||
return m_matches->rmatch[index].rm_eo;
|
||||
}
|
||||
|
||||
int String::matchCount() const
|
||||
{
|
||||
if (!m_matches)
|
||||
return 0;
|
||||
return m_matches->count;
|
||||
}
|
||||
|
||||
String String::replaceMatches(const String &templ) const
|
||||
{
|
||||
String s;
|
||||
int pos, ofs = 0;
|
||||
for (;;) {
|
||||
pos = templ.find('\\',ofs);
|
||||
if (pos < 0) {
|
||||
s += templ.substr(ofs);
|
||||
break;
|
||||
}
|
||||
s += templ.substr(ofs,pos-ofs);
|
||||
pos++;
|
||||
char c = templ[pos];
|
||||
if (c == '\\') {
|
||||
pos++;
|
||||
s += "\\";
|
||||
}
|
||||
else if ('0' <= c && c <= '9') {
|
||||
pos++;
|
||||
s += matchString(c - '0');
|
||||
}
|
||||
ofs = pos;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void String::clearMatches()
|
||||
{
|
||||
if (m_matches)
|
||||
m_matches->clear();
|
||||
}
|
||||
|
||||
String String::msgEscape(const char *str, char extraEsc)
|
||||
{
|
||||
if (!str)
|
||||
str = "";
|
||||
String s;
|
||||
char c;
|
||||
while ((c=*str++)) {
|
||||
if (c < ' ' || c == extraEsc) {
|
||||
c += '@';
|
||||
s += '%';
|
||||
}
|
||||
else if (c == '%')
|
||||
s += c;
|
||||
s += c;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
String String::msgUnescape(const char *str, int *errptr, char extraEsc)
|
||||
{
|
||||
if (!str)
|
||||
str = "";
|
||||
if (extraEsc)
|
||||
extraEsc += '@';
|
||||
const char *pos = str;
|
||||
String s;
|
||||
char c;
|
||||
while ((c=*pos++)) {
|
||||
if (c < ' ') {
|
||||
if (errptr)
|
||||
*errptr = (pos-str);
|
||||
return s;
|
||||
}
|
||||
else if (c == '%') {
|
||||
c=*pos++;
|
||||
if ((c > '@' && c <= '_') || c == extraEsc)
|
||||
c -= '@';
|
||||
else if (c != '%') {
|
||||
if (errptr)
|
||||
*errptr = (pos-str);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
s += c;
|
||||
}
|
||||
if (errptr)
|
||||
*errptr = -1;
|
||||
return s;
|
||||
}
|
||||
|
||||
unsigned int String::hash() const
|
||||
{
|
||||
if (!m_hash)
|
||||
m_hash = hash(m_string);
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
unsigned int String::hash(const char *value)
|
||||
{
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
unsigned int h = 0;
|
||||
while (unsigned char c = (unsigned char) *value++)
|
||||
h = (h << 1) + c;
|
||||
return h;
|
||||
}
|
||||
|
||||
Regexp::Regexp()
|
||||
: m_regexp(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"Regexp::Regexp() [%p]",this);
|
||||
#endif
|
||||
}
|
||||
|
||||
Regexp::Regexp(const char *value)
|
||||
: String(value), m_regexp(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"Regexp::Regexp(\"%s\") [%p]",value,this);
|
||||
#endif
|
||||
}
|
||||
|
||||
Regexp::Regexp(const Regexp &value)
|
||||
: String(value.c_str()), m_regexp(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"Regexp::Regexp(%p) [%p]",&value,this);
|
||||
#endif
|
||||
}
|
||||
|
||||
Regexp::~Regexp()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
bool Regexp::matches(const char *value, StringMatchPrivate *matches)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"Regexp::matches(\"%s\",%p)",value,matches);
|
||||
#endif
|
||||
if (!value)
|
||||
return false;
|
||||
if (!compile())
|
||||
return false;
|
||||
int mm = matches ? MAX_MATCH : 0;
|
||||
regmatch_t *mt = matches ? (matches->rmatch)+1 : 0;
|
||||
return !::regexec((regex_t *)m_regexp,value,mm,mt,0);
|
||||
}
|
||||
|
||||
bool Regexp::matches(const char *value)
|
||||
{
|
||||
return matches(value,0);
|
||||
}
|
||||
|
||||
void Regexp::changed()
|
||||
{
|
||||
cleanup();
|
||||
String::changed();
|
||||
}
|
||||
|
||||
bool Regexp::compile()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"Regexp::compile()");
|
||||
#endif
|
||||
if (c_str() && !m_regexp) {
|
||||
regex_t *data = (regex_t *) ::malloc(sizeof(regex_t));
|
||||
if (::regcomp(data,c_str(),0)) {
|
||||
Debug(DebugWarn,"Regexp::compile() \"%s\" failed",c_str());
|
||||
::regfree(data);
|
||||
::free(data);
|
||||
}
|
||||
else
|
||||
m_regexp = (void *)data;
|
||||
}
|
||||
return (m_regexp != 0);
|
||||
}
|
||||
|
||||
void Regexp::cleanup()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugInfo,"Regexp::cleanup()");
|
||||
#endif
|
||||
if (m_regexp) {
|
||||
regex_t *data = (regex_t *)m_regexp;
|
||||
m_regexp = 0;
|
||||
::regfree(data);
|
||||
::free(data);
|
||||
}
|
||||
}
|
||||
|
||||
NamedString::NamedString(const char *name, const char *value)
|
||||
: String(value), m_name(name)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"NamedString::NamedString(\"%s\",\"%s\") [%p]",name,value,this);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
/**
|
||||
* TelEngine.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telengine.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
namespace TelEngine {
|
||||
|
||||
#define DebugMin DebugFail
|
||||
#define DebugMax DebugAll
|
||||
|
||||
static int s_debug = DebugWarn;
|
||||
static int s_indent = 0;
|
||||
static bool s_debugging = true;
|
||||
|
||||
static void dbg_stderr_func(const char *buf)
|
||||
{
|
||||
::fwrite(buf,1,::strlen(buf),stderr);
|
||||
::fflush(stderr);
|
||||
}
|
||||
|
||||
static void (*s_output)(const char *) = dbg_stderr_func;
|
||||
static void (*s_intout)(const char *) = 0;
|
||||
|
||||
static Mutex out_mux;
|
||||
|
||||
static void common_output(char *buf)
|
||||
{
|
||||
int n = ::strlen(buf);
|
||||
if (n && (buf[n-1] == '\n'))
|
||||
n--;
|
||||
buf[n] = '\n';
|
||||
buf[n+1] = '\0';
|
||||
// serialize the output strings
|
||||
out_mux.lock();
|
||||
if (s_output)
|
||||
s_output(buf);
|
||||
if (s_intout)
|
||||
s_intout(buf);
|
||||
out_mux.unlock();
|
||||
}
|
||||
|
||||
static void dbg_output(const char *prefix, const char *format, va_list ap)
|
||||
{
|
||||
if (!(s_output || s_intout))
|
||||
return;
|
||||
char buf[1024];
|
||||
unsigned int n = s_indent*2;
|
||||
if (n >= sizeof(buf))
|
||||
n = sizeof(buf)-1;
|
||||
::memset(buf,' ',n);
|
||||
buf[n] = 0;
|
||||
unsigned int l = sizeof(buf)-n-2;
|
||||
if (prefix)
|
||||
::strncpy(buf+n,prefix,l);
|
||||
n = ::strlen(buf);
|
||||
l = sizeof(buf)-n-2;
|
||||
if (format) {
|
||||
::vsnprintf(buf+n,l,format,ap);
|
||||
}
|
||||
common_output(buf);
|
||||
}
|
||||
|
||||
void Output(const char *format, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
if (!((s_output || s_intout) && format && *format))
|
||||
return;
|
||||
va_list va;
|
||||
va_start(va,format);
|
||||
::vsnprintf(buf,sizeof(buf)-2,format,va);
|
||||
va_end(va);
|
||||
common_output(buf);
|
||||
}
|
||||
|
||||
bool Debug(int level, const char *format, ...)
|
||||
{
|
||||
if (level <= s_debug) {
|
||||
if (!s_debugging)
|
||||
return true;
|
||||
if (!format)
|
||||
format = "";
|
||||
char buf[32];
|
||||
::sprintf(buf,"<%d> ",level);
|
||||
va_list va;
|
||||
va_start(va,format);
|
||||
dbg_output(buf,format,va);
|
||||
va_end(va);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Debug(const char *facility, int level, const char *format, ...)
|
||||
{
|
||||
if (level <= s_debug) {
|
||||
if (!s_debugging)
|
||||
return true;
|
||||
if (!format)
|
||||
format = "";
|
||||
char buf[64];
|
||||
::snprintf(buf,sizeof(buf),"<%s:%d> ",facility,level);
|
||||
va_list va;
|
||||
va_start(va,format);
|
||||
dbg_output(buf,format,va);
|
||||
va_end(va);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int debugLevel()
|
||||
{
|
||||
return s_debug;
|
||||
}
|
||||
|
||||
int debugLevel(int level)
|
||||
{
|
||||
if (level < DebugMin)
|
||||
level = DebugMin;
|
||||
if (level > DebugMax)
|
||||
level = DebugMax;
|
||||
return (s_debug = level);
|
||||
}
|
||||
|
||||
bool debugAt(int level)
|
||||
{
|
||||
return (s_debugging && (level <= s_debug));
|
||||
}
|
||||
|
||||
Debugger::Debugger(const char *name, const char *format, ...)
|
||||
: m_name(name)
|
||||
{
|
||||
if (s_debugging && m_name && (s_debug >= DebugAll)) {
|
||||
char buf[64];
|
||||
::snprintf(buf,sizeof(buf),">>> %s",m_name);
|
||||
va_list va;
|
||||
va_start(va,format);
|
||||
dbg_output(buf,format,va);
|
||||
va_end(va);
|
||||
s_indent++;
|
||||
}
|
||||
else
|
||||
m_name = 0;
|
||||
}
|
||||
|
||||
Debugger::Debugger(int level, const char *name, const char *format, ...)
|
||||
: m_name(name)
|
||||
{
|
||||
if (s_debugging && m_name && (s_debug >= level)) {
|
||||
char buf[64];
|
||||
::snprintf(buf,sizeof(buf),">>> %s",m_name);
|
||||
va_list va;
|
||||
va_start(va,format);
|
||||
dbg_output(buf,format,va);
|
||||
va_end(va);
|
||||
s_indent++;
|
||||
}
|
||||
else
|
||||
m_name = 0;
|
||||
}
|
||||
|
||||
Debugger::~Debugger()
|
||||
{
|
||||
if (m_name) {
|
||||
s_indent--;
|
||||
if (s_debugging) {
|
||||
char buf[64];
|
||||
::snprintf(buf,sizeof(buf),"<<< %s",m_name);
|
||||
char *format = 0;
|
||||
va_list va = 0;
|
||||
dbg_output(buf,format,va);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::setOutput(void (*outFunc)(const char *))
|
||||
{
|
||||
s_output = outFunc ? outFunc : dbg_stderr_func;
|
||||
}
|
||||
|
||||
void Debugger::setIntOut(void (*outFunc)(const char *))
|
||||
{
|
||||
s_intout = outFunc;
|
||||
}
|
||||
|
||||
void Debugger::enableOutput(bool enable)
|
||||
{
|
||||
s_debugging = enable;
|
||||
}
|
||||
|
||||
unsigned long long Time::now()
|
||||
{
|
||||
struct timeval tv;
|
||||
return ::gettimeofday(&tv,0) ? 0 : fromTimeval(&tv);
|
||||
}
|
||||
|
||||
unsigned long long Time::fromTimeval(struct timeval *tv)
|
||||
{
|
||||
unsigned long long rval = 0;
|
||||
if (tv) {
|
||||
// Please keep it this way or the compiler may b0rk
|
||||
rval = tv->tv_sec;
|
||||
rval *= 1000000;
|
||||
rval += tv->tv_usec;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
void Time::toTimeval(struct timeval *tv, unsigned long long usec)
|
||||
{
|
||||
if (tv) {
|
||||
tv->tv_usec = usec % 1000000;
|
||||
tv->tv_sec = usec / 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
RefObject::~RefObject()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (m_refcount)
|
||||
Debug(DebugMild,"RefObject [%p] destroyed with count=%d",this,m_refcount);
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,330 @@
|
|||
/**
|
||||
* Thread.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telengine.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace TelEngine {
|
||||
|
||||
class ThreadPrivate : public GenObject {
|
||||
friend class Thread;
|
||||
public:
|
||||
ThreadPrivate(Thread *t,const char *name);
|
||||
~ThreadPrivate();
|
||||
void run();
|
||||
bool cancel();
|
||||
void cleanup();
|
||||
void destroy();
|
||||
void pubdestroy();
|
||||
static ThreadPrivate *create(Thread *t,const char *name);
|
||||
static void killall();
|
||||
static Thread *current();
|
||||
Thread *m_thread;
|
||||
pthread_t thread;
|
||||
bool m_running;
|
||||
bool m_updest;
|
||||
const char *m_name;
|
||||
private:
|
||||
static void *startFunc(void *arg);
|
||||
static void cleanupFunc(void *arg);
|
||||
static void destroyFunc(void *arg);
|
||||
static void keyAllocFunc();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static pthread_key_t current_key;
|
||||
static pthread_once_t current_key_once = PTHREAD_ONCE_INIT;
|
||||
ObjList threads;
|
||||
Mutex tmutex;
|
||||
|
||||
ThreadPrivate *ThreadPrivate::create(Thread *t,const char *name)
|
||||
{
|
||||
ThreadPrivate *p = new ThreadPrivate(t,name);
|
||||
int e = ::pthread_create(&p->thread,0,startFunc,p);
|
||||
if (e) {
|
||||
Debug(DebugFail,"Error %d while creating pthread in '%s' [%p]",e,name,p);
|
||||
p->m_thread = 0;
|
||||
p->destroy();
|
||||
return 0;
|
||||
}
|
||||
p->m_running = true;
|
||||
return p;
|
||||
}
|
||||
|
||||
ThreadPrivate::ThreadPrivate(Thread *t,const char *name)
|
||||
: m_thread(t), m_running(false), m_updest(true), m_name(name)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("ThreadPrivate::ThreadPrivate","(%p,\"%s\") [%p]",t,name,this);
|
||||
#endif
|
||||
Lock lock(tmutex);
|
||||
threads.append(this);
|
||||
}
|
||||
|
||||
ThreadPrivate::~ThreadPrivate()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("ThreadPrivate::~ThreadPrivate()"," '%s' [%p]",m_name,this);
|
||||
#endif
|
||||
m_running = false;
|
||||
tmutex.lock();
|
||||
threads.remove(this,false);
|
||||
if (m_thread && m_updest) {
|
||||
Thread *t = m_thread;
|
||||
m_thread = 0;
|
||||
delete t;
|
||||
}
|
||||
tmutex.unlock();
|
||||
}
|
||||
|
||||
void ThreadPrivate::destroy()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"ThreadPrivate::destroy() '%s' [%p]",m_name,this);
|
||||
#endif
|
||||
cleanup();
|
||||
delete this;
|
||||
}
|
||||
|
||||
void ThreadPrivate::pubdestroy()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"ThreadPrivate::pubdestroy() '%s' [%p]",m_name,this);
|
||||
#endif
|
||||
m_updest = false;
|
||||
if (!cancel()) {
|
||||
cleanup();
|
||||
m_thread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPrivate::run()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"ThreadPrivate::run() '%s' [%p]",m_name,this);
|
||||
#endif
|
||||
::pthread_once(¤t_key_once,keyAllocFunc);
|
||||
::pthread_setspecific(current_key,this);
|
||||
pthread_cleanup_push(cleanupFunc,this);
|
||||
::pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,0);
|
||||
|
||||
while (!m_running)
|
||||
::usleep(10);
|
||||
m_thread->run();
|
||||
pthread_cleanup_pop(1);
|
||||
}
|
||||
|
||||
bool ThreadPrivate::cancel()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"ThreadPrivate::cancel() '%s' [%p]",m_name,this);
|
||||
#endif
|
||||
bool ret = true;
|
||||
if (m_running) {
|
||||
ret = !::pthread_cancel(thread);
|
||||
if (ret) {
|
||||
m_running = false;
|
||||
::usleep(10);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ThreadPrivate::cleanup()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"ThreadPrivate::cleanup() '%s' [%p]",m_name,this);
|
||||
#endif
|
||||
if (m_thread && m_thread->m_private) {
|
||||
m_thread->m_private = 0;
|
||||
m_thread->cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
Thread *ThreadPrivate::current()
|
||||
{
|
||||
ThreadPrivate *t = reinterpret_cast<ThreadPrivate *>(::pthread_getspecific(current_key));
|
||||
return t ? t->m_thread : 0;
|
||||
}
|
||||
|
||||
void ThreadPrivate::killall()
|
||||
{
|
||||
Debugger debug("ThreadPrivate::killall()");
|
||||
ThreadPrivate *t;
|
||||
bool sledgehammer = false;
|
||||
int c = 1;
|
||||
tmutex.lock();
|
||||
ObjList *l = &threads;
|
||||
while (l && (t = static_cast<ThreadPrivate *>(l->get())) != 0)
|
||||
{
|
||||
Debug(DebugInfo,"Trying to kill ThreadPrivate '%s' [%p], attempt %d",t->m_name,t,c);
|
||||
tmutex.unlock();
|
||||
bool ok = t->cancel();
|
||||
tmutex.lock();
|
||||
if (ok)
|
||||
::usleep(10);
|
||||
if (t != l->get())
|
||||
c = 1;
|
||||
else {
|
||||
if (ok) {
|
||||
Debug(DebugGoOn,"Could not kill %p but seems OK to delete it (pthread bug?)",t);
|
||||
tmutex.unlock();
|
||||
t->destroy();
|
||||
tmutex.lock();
|
||||
continue;
|
||||
}
|
||||
::usleep(10);
|
||||
if (++c >= 10) {
|
||||
Debug(DebugFail,"Could not kill %p, will use sledgehammer later.",t);
|
||||
sledgehammer = true;
|
||||
t->m_thread = 0;
|
||||
l = l->next();
|
||||
c = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
tmutex.unlock();
|
||||
// last solution - a REALLY BIG tool!
|
||||
// usually too big since many libraries have threads of their own...
|
||||
if (sledgehammer) {
|
||||
#ifdef __linux__
|
||||
Debug(DebugFail,"Brutally killing remaining threads!");
|
||||
::pthread_kill_other_threads_np();
|
||||
#else
|
||||
Debug(DebugFail,"Aargh! I cannot kill remaining threads on this platform!");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPrivate::destroyFunc(void *arg)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("ThreadPrivate::destroyFunc","(%p)",arg);
|
||||
#endif
|
||||
ThreadPrivate *t = reinterpret_cast<ThreadPrivate *>(arg);
|
||||
if (t)
|
||||
t->destroy();
|
||||
}
|
||||
|
||||
void ThreadPrivate::cleanupFunc(void *arg)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"ThreadPrivate::cleanupFunc(%p)",arg);
|
||||
#endif
|
||||
ThreadPrivate *t = reinterpret_cast<ThreadPrivate *>(arg);
|
||||
if (t)
|
||||
t->cleanup();
|
||||
}
|
||||
|
||||
void ThreadPrivate::keyAllocFunc()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"ThreadPrivate::keyAllocFunc()");
|
||||
#endif
|
||||
if (::pthread_key_create(¤t_key,destroyFunc))
|
||||
Debug(DebugGoOn,"Failed to create current thread key!");
|
||||
}
|
||||
|
||||
void *ThreadPrivate::startFunc(void *arg)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"ThreadPrivate::startFunc(%p)",arg);
|
||||
#endif
|
||||
ThreadPrivate *t = reinterpret_cast<ThreadPrivate *>(arg);
|
||||
t->run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Thread::Thread(const char *name)
|
||||
: m_private(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debugger debug("Thread::Thread","(\"%s\") [%p]",name,this);
|
||||
#endif
|
||||
m_private = ThreadPrivate::create(this,name);
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"Thread::~Thread() [%p]",this);
|
||||
#endif
|
||||
if (m_private)
|
||||
m_private->pubdestroy();
|
||||
}
|
||||
|
||||
bool Thread::error() const
|
||||
{
|
||||
return !m_private;
|
||||
}
|
||||
|
||||
bool Thread::running() const
|
||||
{
|
||||
return m_private ? m_private->m_running : false;
|
||||
}
|
||||
|
||||
Thread *Thread::current()
|
||||
{
|
||||
return ThreadPrivate::current();
|
||||
}
|
||||
|
||||
int Thread::count()
|
||||
{
|
||||
Lock lock(tmutex);
|
||||
return threads.count();
|
||||
}
|
||||
|
||||
void Thread::cleanup()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"Thread::cleanup() [%p]",this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Thread::killall()
|
||||
{
|
||||
if (!ThreadPrivate::current())
|
||||
ThreadPrivate::killall();
|
||||
}
|
||||
|
||||
void Thread::exit()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"Thread::exit()");
|
||||
#endif
|
||||
::pthread_exit(0);
|
||||
}
|
||||
|
||||
void Thread::cancel()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug(DebugAll,"Thread::cancel() [%p]",this);
|
||||
#endif
|
||||
if (m_private)
|
||||
m_private->cancel();
|
||||
}
|
||||
|
||||
void Thread::yield()
|
||||
{
|
||||
::usleep(1);
|
||||
}
|
||||
|
||||
int Thread::fork()
|
||||
{
|
||||
// pid_t __fork(void);
|
||||
return ::fork();
|
||||
}
|
||||
|
||||
void Thread::preExec()
|
||||
{
|
||||
#ifdef __linux__
|
||||
::pthread_kill_other_threads_np();
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* main.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
|
||||
#include "telengine.h"
|
||||
|
||||
extern "C" int main(int argc, const char **argv, const char **environ)
|
||||
{
|
||||
return TelEngine::Engine::main(argc,argv,environ);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
Makefile
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.yate
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -0,0 +1,123 @@
|
|||
# Makefile
|
||||
# This file holds the make rules for the Telephony Engine modules
|
||||
|
||||
# override DESTDIR at install time to prefix the install directory
|
||||
DESTDIR :=
|
||||
|
||||
# override DEBUG at compile time to enable full debug or remove it all
|
||||
DEBUG :=
|
||||
|
||||
CC := g++ -Wall
|
||||
SED := sed
|
||||
DEFS :=
|
||||
INCLUDES := -I@top_srcdir@
|
||||
CFLAGS := -O2 @MODULE_CFLAGS@
|
||||
LDFLAGS:= -L.. -lyate
|
||||
MODFLAGS:= @MODULE_LDFLAGS@
|
||||
INCFILES := @top_srcdir@/telengine.h @top_srcdir@/telephony.h @top_srcdir@/yateversn.h
|
||||
|
||||
SUBDIRS :=
|
||||
PROGS := cdrbuild.yate cdrfile.yate \
|
||||
regexroute.yate \
|
||||
tonegen.yate wavefile.yate \
|
||||
rmanager.yate extmodule.yate
|
||||
LIBS :=
|
||||
|
||||
ifneq (@HAVE_PGSQL@,no)
|
||||
PROGS := $(PROGS) pgsqlroute.yate cdrpgsql.yate register.yate
|
||||
endif
|
||||
|
||||
ifneq (@HAVE_PRI@,no)
|
||||
PROGS := $(PROGS) zapchan.yate
|
||||
endif
|
||||
|
||||
ifneq (@HAVE_H323@,no)
|
||||
PROGS := $(PROGS) h323chan.yate
|
||||
endif
|
||||
|
||||
ifeq (@HAVE_EXOSIP@_@HAVE_ORTP@_@HAVE_GLIB2@,yes_yes_yes)
|
||||
PROGS := $(PROGS) sipchan.yate
|
||||
endif
|
||||
|
||||
ifneq (@HAVE_IAX2@,no)
|
||||
PROGS := $(PROGS) iaxchan.yate
|
||||
endif
|
||||
|
||||
ifneq (@HAVE_GSM@,no)
|
||||
PROGS := $(PROGS) gsmcodec.yate
|
||||
endif
|
||||
|
||||
LOCALFLAGS =
|
||||
LOCALLIBS =
|
||||
COMPILE = $(CC) $(DEFS) $(DEBUG) $(INCLUDES) $(CFLAGS)
|
||||
LINK = $(CC) $(LDFLAGS)
|
||||
MODLINK = $(CC) $(MODFLAGS) $(LDFLAGS)
|
||||
MODCOMP = $(COMPILE) $(MODFLAGS) $(LDFLAGS)
|
||||
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
moddir = @libdir@/yate
|
||||
|
||||
.PHONY: all
|
||||
all: do-all $(LIBS) $(PROGS)
|
||||
|
||||
.PHONY: strip
|
||||
strip: all do-strip
|
||||
strip --strip-debug --discard-locals $(PROGS)
|
||||
|
||||
.PHONY: clean
|
||||
clean: do-clean
|
||||
@echo rm $(PROGS) $(LIBS) *.o core
|
||||
@rm $(PROGS) $(LIBS) *.o core 2>/dev/null; true
|
||||
|
||||
.PHONY: install
|
||||
install: all do-install
|
||||
@mkdir -p "$(DESTDIR)$(moddir)/" && \
|
||||
install $(PROGS) "$(DESTDIR)$(moddir)/"
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall: do-uninstall
|
||||
@-for i in $(PROGS) ; do \
|
||||
rm "$(DESTDIR)$(moddir)/$$i" ; \
|
||||
done; \
|
||||
rmdir "$(DESTDIR)$(moddir)"
|
||||
|
||||
%.o: @srcdir@/%.cpp $(INCFILES)
|
||||
$(COMPILE) -c $<
|
||||
|
||||
%.o: @srcdir@/%.c
|
||||
$(COMPILE) -c $<
|
||||
|
||||
do-all do-strip do-clean do-install do-uninstall:
|
||||
$(if $(SUBDIRS),\
|
||||
@target=`echo $@ | $(SED) -e 's/^do-//'`; \
|
||||
for i in $(SUBDIRS) ; do \
|
||||
if test -f ./$$i/Makefile ; then \
|
||||
$(MAKE) -C ./$$i $${target} || exit 1;\
|
||||
fi; \
|
||||
done \
|
||||
)
|
||||
|
||||
Makefile: @srcdir@/Makefile.in ../config.status
|
||||
cd .. && ./config.status
|
||||
|
||||
lib%.so: %.o
|
||||
$(LINK) -shared -o $@ $^
|
||||
|
||||
%.yate: @srcdir@/%.cpp $(INCFILES)
|
||||
$(MODCOMP) -o $@ $(LOCALFLAGS) $< $(LOCALLIBS)
|
||||
|
||||
|
||||
# Take special care of the modules that depend on optional libs
|
||||
|
||||
zapchan.yate: LOCALFLAGS = -lpri
|
||||
|
||||
h323chan.yate: LOCALFLAGS = -DPHAS_TEMPLATES -D_REENTRANT -DP_HAS_SEMAPHORES @H323_INC@ @H323_LIB@
|
||||
|
||||
pgsqlroute.yate cdrpgsql.yate register.yate: LOCALFLAGS = @PGSQL_INC@ -lpq
|
||||
|
||||
sipchan.yate: LOCALFLAGS = @EXOSIP_INC@ @ORTP_INC@ @GLIB2_INC@ @EXOSIP_LIB@ @ORTP_LIB@ @GLIB2_LIB@
|
||||
|
||||
iaxchan.yate: LOCALFLAGS = @IAX2_INC@ @IAX2_LIB@
|
||||
|
||||
gsmcodec.yate: LOCALLIBS = -lgsm
|
|
@ -0,0 +1,228 @@
|
|||
/**
|
||||
* cdrbuild.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Cdr builder
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
#include <telephony.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
enum {
|
||||
CdrRing,
|
||||
CdrCall,
|
||||
CdrRinging,
|
||||
CdrAnswer,
|
||||
CdrHangup,
|
||||
CdrDrop,
|
||||
EngHalt
|
||||
};
|
||||
|
||||
class CdrHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
CdrHandler(const char *name, int type)
|
||||
: MessageHandler(name), m_type(type) { }
|
||||
virtual bool received(Message &msg);
|
||||
private:
|
||||
int m_type;
|
||||
};
|
||||
|
||||
class StatusHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
StatusHandler() : MessageHandler("status") { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class CdrBuilder : public String
|
||||
{
|
||||
public:
|
||||
CdrBuilder(const char *name, const char *caller, const char *called);
|
||||
virtual ~CdrBuilder();
|
||||
void update(int type, unsigned long long val);
|
||||
inline void setStatus(const char *status)
|
||||
{ m_status = status; }
|
||||
String getStatus() const;
|
||||
static CdrBuilder *find(String &id);
|
||||
private:
|
||||
inline static int sec(unsigned long long usec)
|
||||
{ return (usec + 500000) / 1000000; }
|
||||
unsigned long long
|
||||
m_ring,
|
||||
m_call,
|
||||
m_ringing,
|
||||
m_answer,
|
||||
m_hangup;
|
||||
String m_caller;
|
||||
String m_called;
|
||||
String m_status;
|
||||
};
|
||||
|
||||
static ObjList cdrs;
|
||||
|
||||
CdrBuilder::CdrBuilder(const char *name, const char *caller, const char *called)
|
||||
: String(name), m_caller(caller), m_called(called), m_status("unknown")
|
||||
{
|
||||
m_ring = m_call = m_ringing = m_answer = m_hangup = 0;
|
||||
}
|
||||
|
||||
CdrBuilder::~CdrBuilder()
|
||||
{
|
||||
if (!m_hangup)
|
||||
m_hangup = Time::now();
|
||||
if (!m_ring)
|
||||
m_ring = m_call;
|
||||
if (!m_call)
|
||||
m_call = m_ring;
|
||||
if (!m_ringing)
|
||||
m_ringing = m_call;
|
||||
if (!m_answer)
|
||||
m_answer = m_hangup;
|
||||
|
||||
Message *m = new Message("cdr");
|
||||
m->addParam("time",String(sec(m_ring)));
|
||||
m->addParam("chan",c_str());
|
||||
m->addParam("caller",m_caller);
|
||||
m->addParam("called",m_called);
|
||||
m->addParam("duration",String(sec(m_hangup - m_ring)));
|
||||
m->addParam("billtime",String(sec(m_hangup - m_answer)));
|
||||
m->addParam("ringtime",String(sec(m_answer - m_ringing)));
|
||||
m->addParam("status",m_status);
|
||||
Engine::enqueue(m);
|
||||
}
|
||||
|
||||
String CdrBuilder::getStatus() const
|
||||
{
|
||||
String s(m_status);
|
||||
s << "/" << m_caller << "/" << m_called;
|
||||
return s;
|
||||
}
|
||||
|
||||
void CdrBuilder::update(int type, unsigned long long val)
|
||||
{
|
||||
switch (type) {
|
||||
case CdrRing:
|
||||
m_ring = val;
|
||||
break;
|
||||
case CdrCall:
|
||||
m_call = val;
|
||||
break;
|
||||
case CdrRinging:
|
||||
if (!m_ringing)
|
||||
m_ringing = val;
|
||||
break;
|
||||
case CdrAnswer:
|
||||
m_answer = val;
|
||||
break;
|
||||
case CdrHangup:
|
||||
m_hangup = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CdrBuilder *CdrBuilder::find(String &id)
|
||||
{
|
||||
ObjList *l = &cdrs;
|
||||
for (; l; l=l->next()) {
|
||||
CdrBuilder *b = static_cast<CdrBuilder *>(l->get());
|
||||
if (b && (*b == id))
|
||||
return b;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CdrHandler::received(Message &msg)
|
||||
{
|
||||
static Mutex mutex;
|
||||
Lock lock(mutex);
|
||||
if (m_type == EngHalt) {
|
||||
cdrs.clear();
|
||||
return false;
|
||||
}
|
||||
String id(msg.getValue("id"));
|
||||
if (id.null()) {
|
||||
id = msg.getValue("driver");
|
||||
id += "/";
|
||||
id += msg.getValue("span");
|
||||
id += "/";
|
||||
id += msg.getValue("channel");
|
||||
}
|
||||
CdrBuilder *b = CdrBuilder::find(id);
|
||||
if (!b && ((m_type == CdrRing) || (m_type == CdrCall))) {
|
||||
b = new CdrBuilder(id,msg.getValue("caller"),msg.getValue("called"));
|
||||
cdrs.append(b);
|
||||
}
|
||||
if (b) {
|
||||
const char *s = msg.getValue("status");
|
||||
if (s)
|
||||
b->setStatus(s);
|
||||
b->update(m_type,msg.msgTime().usec());
|
||||
if (m_type == CdrHangup) {
|
||||
cdrs.remove(b);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
Debug("CdrBuilder",DebugGoOn,"Got message '%s' for untracked id '%s'",
|
||||
msg.c_str(),id.c_str());
|
||||
return false;
|
||||
};
|
||||
|
||||
bool StatusHandler::received(Message &msg)
|
||||
{
|
||||
const char *sel = msg.getValue("module");
|
||||
if (sel && ::strcmp(sel,"cdrbuild"))
|
||||
return false;
|
||||
String st("cdrbuild,type=cdr,cdrs=");
|
||||
st << cdrs.count() << ",[LIST]";
|
||||
ObjList *l = &cdrs;
|
||||
for (; l; l=l->next()) {
|
||||
CdrBuilder *b = static_cast<CdrBuilder *>(l->get());
|
||||
if (b) {
|
||||
st << "," << *b << "=" << b->getStatus();
|
||||
}
|
||||
}
|
||||
msg.retValue() << st << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
class CdrBuildPlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
CdrBuildPlugin();
|
||||
virtual void initialize();
|
||||
private:
|
||||
bool m_first;
|
||||
};
|
||||
|
||||
CdrBuildPlugin::CdrBuildPlugin()
|
||||
: m_first(true)
|
||||
{
|
||||
Output("Loaded module CdrBuild");
|
||||
}
|
||||
|
||||
void CdrBuildPlugin::initialize()
|
||||
{
|
||||
Output("Initializing module CdrBuild");
|
||||
if (m_first) {
|
||||
m_first = false;
|
||||
Engine::install(new CdrHandler("ring",CdrRing));
|
||||
Engine::install(new CdrHandler("call",CdrCall));
|
||||
Engine::install(new CdrHandler("ringing",CdrRinging));
|
||||
Engine::install(new CdrHandler("answer",CdrAnswer));
|
||||
Engine::install(new CdrHandler("hangup",CdrHangup));
|
||||
Engine::install(new CdrHandler("dropcdr",CdrDrop));
|
||||
Engine::install(new CdrHandler("engine.halt",EngHalt));
|
||||
Engine::install(new StatusHandler);
|
||||
}
|
||||
}
|
||||
|
||||
INIT_PLUGIN(CdrBuildPlugin);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* cdrfile.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Write the CDR to a text file
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
class CdrFileHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
CdrFileHandler(const char *name)
|
||||
: MessageHandler(name), m_tabs(0), m_file(0) { }
|
||||
virtual ~CdrFileHandler();
|
||||
virtual bool received(Message &msg);
|
||||
void init(const char *fname, bool tabsep);
|
||||
private:
|
||||
bool m_tabs;
|
||||
FILE *m_file;
|
||||
Mutex m_lock;
|
||||
};
|
||||
|
||||
CdrFileHandler::~CdrFileHandler()
|
||||
{
|
||||
Lock lock(m_lock);
|
||||
if (m_file) {
|
||||
::fclose(m_file);
|
||||
m_file = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CdrFileHandler::init(const char *fname, bool tabsep)
|
||||
{
|
||||
Lock lock(m_lock);
|
||||
if (m_file)
|
||||
::fclose(m_file);
|
||||
m_tabs = tabsep;
|
||||
m_file = fname ? ::fopen(fname,"a") : 0;
|
||||
}
|
||||
|
||||
bool CdrFileHandler::received(Message &msg)
|
||||
{
|
||||
Lock lock(m_lock);
|
||||
if (m_file) {
|
||||
const char *format = m_tabs
|
||||
? "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
|
||||
: "%s,\"%s\",\"%s\",\"%s\",%s,%s,%s,\"%s\"\n";
|
||||
::fprintf(m_file,format,
|
||||
c_safe(msg.getValue("time")),
|
||||
c_safe(msg.getValue("chan")),
|
||||
c_safe(msg.getValue("caller")),
|
||||
c_safe(msg.getValue("called")),
|
||||
c_safe(msg.getValue("billtime")),
|
||||
c_safe(msg.getValue("ringtime")),
|
||||
c_safe(msg.getValue("duration")),
|
||||
c_safe(msg.getValue("status"))
|
||||
);
|
||||
::fflush(m_file);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
class CdrFilePlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
CdrFilePlugin();
|
||||
virtual void initialize();
|
||||
private:
|
||||
CdrFileHandler *m_handler;
|
||||
};
|
||||
|
||||
CdrFilePlugin::CdrFilePlugin()
|
||||
: m_handler(0)
|
||||
{
|
||||
Output("Loaded module CdrFile");
|
||||
}
|
||||
|
||||
void CdrFilePlugin::initialize()
|
||||
{
|
||||
Output("Initializing module CdrFile");
|
||||
Configuration cfg(Engine::configFile("cdrfile"));
|
||||
const char *file = cfg.getValue("general","file");
|
||||
if (file && !m_handler) {
|
||||
m_handler = new CdrFileHandler("cdr");
|
||||
Engine::install(m_handler);
|
||||
}
|
||||
if (m_handler)
|
||||
m_handler->init(file,cfg.getBoolValue("general","tabs"));
|
||||
}
|
||||
|
||||
INIT_PLUGIN(CdrFilePlugin);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,129 @@
|
|||
/**
|
||||
* cdrpgsql.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Write the CDR to a PostgreSQL database
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <libpq-fe.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static PGconn *conn=0;
|
||||
Mutex dbmutex;
|
||||
|
||||
class CdrPgsqlHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
CdrPgsqlHandler(const char *name)
|
||||
: MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
private:
|
||||
};
|
||||
|
||||
bool CdrPgsqlHandler::received(Message &msg)
|
||||
{
|
||||
// const char *calltime = c_safe(msg.getValue("time"));
|
||||
const char *channel = c_safe(msg.getValue("channel"));
|
||||
const char *called = c_safe(msg.getValue("called"));
|
||||
const char *caller = c_safe(msg.getValue("caller"));
|
||||
const char *billtime = c_safe(msg.getValue("billtime"));
|
||||
const char *ringtime = c_safe(msg.getValue("ringtime"));
|
||||
const char *duration = c_safe(msg.getValue("duration"));
|
||||
const char *status = c_safe(msg.getValue("status"));
|
||||
|
||||
Lock lock(dbmutex);
|
||||
if (!conn)
|
||||
return false;
|
||||
|
||||
char buffer[2048];
|
||||
snprintf(buffer,sizeof(buffer),"INSERT INTO cdr"
|
||||
" (channel,caller,called,billtime,ringtime,duration,status)"
|
||||
" VALUES ('%s','%s','%s','%s','%s','%s','%s')",
|
||||
channel,caller,called,billtime,ringtime,duration,status);
|
||||
|
||||
PGresult *respgsql = PQexec(conn,buffer);
|
||||
if (!respgsql || PQresultStatus(respgsql) != PGRES_COMMAND_OK)
|
||||
Debug(DebugFail,"Failed to insert in database: %s",
|
||||
PQerrorMessage(conn));
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
class StatusHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
StatusHandler(const char *name, unsigned prio = 1)
|
||||
: MessageHandler(name,prio) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
bool StatusHandler::received(Message &msg)
|
||||
{
|
||||
// msg.addParam("mod","cdrpgsql");
|
||||
msg.retValue() << "CdrPgsql,conn=" << (conn != 0) <<"\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
class CdrPgsqlPlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
CdrPgsqlPlugin();
|
||||
~CdrPgsqlPlugin();
|
||||
virtual void initialize();
|
||||
private:
|
||||
CdrPgsqlHandler *m_handler;
|
||||
};
|
||||
|
||||
CdrPgsqlPlugin::CdrPgsqlPlugin()
|
||||
: m_handler(0)
|
||||
{
|
||||
Output("Loaded module CdrFile");
|
||||
}
|
||||
|
||||
CdrPgsqlPlugin::~CdrPgsqlPlugin()
|
||||
{
|
||||
if (conn) {
|
||||
PQfinish(conn);
|
||||
conn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CdrPgsqlPlugin::initialize()
|
||||
{
|
||||
char *pgoptions=NULL,
|
||||
*pgtty=NULL;
|
||||
Output("Initializing module Cdr for PostgreSQL");
|
||||
Configuration cfg(Engine::configFile("cdrpgsql"));
|
||||
const char *pghost = c_safe(cfg.getValue("general","host","localhost"));
|
||||
const char *pgport = c_safe(cfg.getValue("general","port","5432"));
|
||||
const char *dbName = c_safe(cfg.getValue("general","database","yate"));
|
||||
const char *dbUser = c_safe(cfg.getValue("general","user","postgres"));
|
||||
const char *dbPass = c_safe(cfg.getValue("general","password"));
|
||||
|
||||
Lock lock(dbmutex);
|
||||
if (conn)
|
||||
PQfinish(conn);
|
||||
conn = PQsetdbLogin(pghost,pgport,pgoptions,pgtty,dbName,dbUser,dbPass);
|
||||
if (PQstatus(conn) == CONNECTION_BAD) {
|
||||
Debug(DebugFail, "Connection to database '%s' failed.", dbName);
|
||||
Debug(DebugFail, "%s", PQerrorMessage(conn));
|
||||
PQfinish(conn);
|
||||
conn = 0;
|
||||
return;
|
||||
}
|
||||
if (!m_handler) {
|
||||
Output("Installing Cdr for PostgreSQL handler");
|
||||
m_handler = new CdrPgsqlHandler("cdr");
|
||||
Engine::install(m_handler);
|
||||
Engine::install(new StatusHandler("status"));
|
||||
}
|
||||
}
|
||||
|
||||
INIT_PLUGIN(CdrPgsqlPlugin);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,481 @@
|
|||
/**
|
||||
* extmodule.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Some parts of this code have been stolen shamelessly from app_agi.
|
||||
* I think that AGI is great idea.
|
||||
*
|
||||
* External module handler
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
#include <telephony.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static Configuration s_cfg;
|
||||
|
||||
class ExtModReceiver;
|
||||
|
||||
class ExtModSource : public ThreadedSource
|
||||
{
|
||||
public:
|
||||
ExtModSource(int fd);
|
||||
~ExtModSource();
|
||||
virtual void run();
|
||||
private:
|
||||
int m_fd;
|
||||
unsigned m_brate;
|
||||
unsigned m_total;
|
||||
};
|
||||
|
||||
class ExtModConsumer : public DataConsumer
|
||||
{
|
||||
public:
|
||||
ExtModConsumer(int fd);
|
||||
~ExtModConsumer();
|
||||
virtual void Consume(const DataBlock &data);
|
||||
private:
|
||||
int m_fd;
|
||||
unsigned m_total;
|
||||
};
|
||||
|
||||
class ExtModChan : public DataEndpoint
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
DataNone = 0,
|
||||
DataRead,
|
||||
DataWrite,
|
||||
DataBoth
|
||||
};
|
||||
ExtModChan(const char *file, int type);
|
||||
~ExtModChan();
|
||||
virtual void disconnected();
|
||||
private:
|
||||
ExtModReceiver *m_recv;
|
||||
};
|
||||
|
||||
class MsgHolder : public GenObject
|
||||
{
|
||||
public:
|
||||
MsgHolder(Message &msg);
|
||||
Message &m_msg;
|
||||
bool m_ret;
|
||||
String m_id;
|
||||
bool decode(const char *s);
|
||||
};
|
||||
|
||||
class ExtModReceiver : public MessageReceiver
|
||||
{
|
||||
public:
|
||||
ExtModReceiver(const char *script, const char *args,
|
||||
int ain = -1, int aout = -1, ExtModChan *chan = 0);
|
||||
~ExtModReceiver();
|
||||
virtual bool received(Message &msg, int id);
|
||||
void processLine(const char *line);
|
||||
void outputLine(const char *line);
|
||||
bool create(const char *script, const char *args);
|
||||
void run();
|
||||
private:
|
||||
pid_t m_pid;
|
||||
int m_in, m_out, m_ain, m_aout;
|
||||
ExtModChan *m_chan;
|
||||
String m_script, m_args;
|
||||
ObjList m_waiting;
|
||||
};
|
||||
|
||||
class ExtThread : public Thread
|
||||
{
|
||||
public:
|
||||
ExtThread(ExtModReceiver *receiver) : Thread("ExtModule"), m_receiver(receiver)
|
||||
{ }
|
||||
virtual void run()
|
||||
{ m_receiver->run(); }
|
||||
private:
|
||||
ExtModReceiver *m_receiver;
|
||||
};
|
||||
|
||||
class ExtModHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
ExtModHandler(const char *name) : MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class ExtModulePlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
ExtModulePlugin();
|
||||
~ExtModulePlugin();
|
||||
virtual void initialize();
|
||||
private:
|
||||
ExtModHandler *m_handler;
|
||||
};
|
||||
|
||||
ExtModSource::ExtModSource(int fd)
|
||||
: m_fd(fd), m_brate(16000), m_total(0)
|
||||
{
|
||||
Debug(DebugAll,"ExtModSource::ExtModSource(%d) [%p]",fd,this);
|
||||
if (m_fd >= 0)
|
||||
start("ExtModSource");
|
||||
}
|
||||
|
||||
ExtModSource::~ExtModSource()
|
||||
{
|
||||
Debug(DebugAll,"ExtModSource::~ExtModSource() [%p] total=%u",this,m_total);
|
||||
if (m_fd >= 0) {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void ExtModSource::run()
|
||||
{
|
||||
DataBlock data(0,480);
|
||||
int r = 0;
|
||||
unsigned long long tpos = Time::now();
|
||||
do {
|
||||
r = ::read(m_fd,data.data(),data.length());
|
||||
if (r < 0) {
|
||||
if (errno == EINTR) {
|
||||
r = 1;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (r < (int)data.length())
|
||||
data.assign(data.data(),r);
|
||||
long long dly = tpos - Time::now();
|
||||
if (dly > 0) {
|
||||
#ifdef DEBUG
|
||||
Debug("ExtModSource",DebugAll,"Sleeping for %lld usec",dly);
|
||||
#endif
|
||||
::usleep((unsigned long)dly);
|
||||
}
|
||||
Forward(data);
|
||||
m_total += r;
|
||||
tpos += (r*1000000ULL/m_brate);
|
||||
} while (r > 0);
|
||||
Debug(DebugAll,"ExtModSource [%p] end of data total=%u",this,m_total);
|
||||
}
|
||||
|
||||
ExtModConsumer::ExtModConsumer(int fd)
|
||||
: m_fd(fd), m_total(0)
|
||||
{
|
||||
Debug(DebugAll,"ExtModConsumer::ExtModConsumer(%d) [%p]",fd,this);
|
||||
}
|
||||
|
||||
ExtModConsumer::~ExtModConsumer()
|
||||
{
|
||||
Debug(DebugAll,"ExtModConsumer::~ExtModConsumer() [%p] total=%u",this,m_total);
|
||||
if (m_fd >= 0) {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void ExtModConsumer::Consume(const DataBlock &data)
|
||||
{
|
||||
if ((m_fd >= 0) && !data.null()) {
|
||||
::write(m_fd,data.data(),data.length());
|
||||
m_total += data.length();
|
||||
}
|
||||
}
|
||||
|
||||
ExtModChan::ExtModChan(const char *file, int type)
|
||||
: DataEndpoint("ExtModule"), m_recv(0)
|
||||
{
|
||||
Debug(DebugAll,"ExtModChan::ExtModChan(%d) [%p]",type,this);
|
||||
int wfifo[2] = { -1, -1 };
|
||||
int rfifo[2] = { -1, -1 };
|
||||
switch (type) {
|
||||
case DataWrite:
|
||||
case DataBoth:
|
||||
::pipe(wfifo);
|
||||
setConsumer(new ExtModConsumer(wfifo[1]));
|
||||
getConsumer()->deref();
|
||||
}
|
||||
switch (type) {
|
||||
case DataRead:
|
||||
case DataBoth:
|
||||
::pipe(rfifo);
|
||||
setSource(new ExtModSource(rfifo[0]));
|
||||
getSource()->deref();
|
||||
}
|
||||
m_recv = new ExtModReceiver(file,"",wfifo[0],rfifo[1],this);
|
||||
}
|
||||
|
||||
ExtModChan::~ExtModChan()
|
||||
{
|
||||
Debug(DebugAll,"ExtModChan::~ExtModChan() [%p]",this);
|
||||
}
|
||||
|
||||
void ExtModChan::disconnected()
|
||||
{
|
||||
Debugger debug("ExtModChan::disconnected()"," [%p]",this);
|
||||
// destruct();
|
||||
}
|
||||
|
||||
MsgHolder::MsgHolder(Message &msg)
|
||||
: m_msg(msg), m_ret(false)
|
||||
{
|
||||
m_id = (int)this;
|
||||
}
|
||||
|
||||
bool MsgHolder::decode(const char *s)
|
||||
{
|
||||
return (m_msg.decode(s,m_ret,m_id) == -2);
|
||||
}
|
||||
|
||||
ExtModReceiver::ExtModReceiver(const char *script, const char *args, int ain, int aout, ExtModChan *chan)
|
||||
: m_pid(-1), m_in(-1), m_out(-1), m_ain(ain), m_aout(aout),
|
||||
m_chan(chan), m_script(script), m_args(args)
|
||||
{
|
||||
Debug(DebugAll,"ExtModReceiver::ExtModReceiver(\"%s\",\"%s\") [%p]",script,args,this);
|
||||
new ExtThread(this);
|
||||
}
|
||||
|
||||
ExtModReceiver::~ExtModReceiver()
|
||||
{
|
||||
Debug(DebugAll,"ExtModReceiver::~ExtModReceiver() [%p] pid=%d",this,m_pid);
|
||||
/* Give the external script a chance to die gracefully */
|
||||
::kill(m_pid,SIGTERM);
|
||||
::close(m_in);
|
||||
::close(m_out);
|
||||
if (m_chan)
|
||||
m_chan->disconnect();
|
||||
int w = ::waitpid(m_pid, 0, WNOHANG);
|
||||
if (w == 0)
|
||||
Debug(DebugWarn, "Process %d has not exited yet?",m_pid);
|
||||
else if (w < 0)
|
||||
Debug(DebugWarn, "Failed waitpid on %d: %s",m_pid,strerror(errno));
|
||||
}
|
||||
|
||||
bool ExtModReceiver::received(Message &msg, int id)
|
||||
{
|
||||
MsgHolder h(msg);
|
||||
m_waiting.append(&h);
|
||||
Debug(DebugAll,"ExtMod [%p] queued message '%s' [%p]",this,msg.c_str(),&msg);
|
||||
outputLine(msg.encode(h.m_id));
|
||||
while (m_waiting.find(&h))
|
||||
Thread::yield();
|
||||
Debug(DebugAll,"ExtMod [%p] message '%s' [%p] returning %s",this,msg.c_str(),&msg, h.m_ret ? "true" : "false");
|
||||
return h.m_ret;
|
||||
}
|
||||
|
||||
bool ExtModReceiver::create(const char *script, const char *args)
|
||||
{
|
||||
String tmp(script);
|
||||
int pid;
|
||||
int ext2yate[2];
|
||||
int yate2ext[2];
|
||||
int x;
|
||||
if (script[0] != '/') {
|
||||
tmp = s_cfg.getValue("general","scripts_dir","scripts/") + tmp;
|
||||
}
|
||||
script = tmp.c_str();
|
||||
if (::pipe(ext2yate)) {
|
||||
Debug(DebugWarn, "Unable to create ext->yate pipe: %s",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (pipe(yate2ext)) {
|
||||
Debug(DebugWarn, "unable to create yate->ext pipe: %s", strerror(errno));
|
||||
::close(ext2yate[0]);
|
||||
::close(ext2yate[1]);
|
||||
return false;
|
||||
}
|
||||
pid = Thread::fork();
|
||||
if (pid < 0) {
|
||||
Debug(DebugWarn, "Failed to fork(): %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (!pid) {
|
||||
/* Terminate all other threads if needed */
|
||||
Thread::preExec();
|
||||
/* Redirect stdin and out */
|
||||
::dup2(yate2ext[0], STDIN_FILENO);
|
||||
::dup2(ext2yate[1], STDOUT_FILENO);
|
||||
/* Set audio in/out handlers */
|
||||
if (m_ain != -1)
|
||||
::dup2(m_ain, STDERR_FILENO+1);
|
||||
else
|
||||
::close(STDERR_FILENO+1);
|
||||
if (m_aout != -1)
|
||||
::dup2(m_aout, STDERR_FILENO+2);
|
||||
else
|
||||
::close(STDERR_FILENO+2);
|
||||
/* Close everything but stdin/out/error/audio */
|
||||
for (x=STDERR_FILENO+3;x<1024;x++)
|
||||
::close(x);
|
||||
/* Execute script */
|
||||
::fprintf(stderr, "Execing '%s' '%s'\n", script, args);
|
||||
::execl(script, script, args, (char *)NULL);
|
||||
::fprintf(stderr, "Failed to execute '%s': %s\n", script, strerror(errno));
|
||||
::exit(1);
|
||||
}
|
||||
Debug(DebugInfo,"Launched External Script %s", script);
|
||||
m_in = ext2yate[0];
|
||||
m_out = yate2ext[1];
|
||||
|
||||
/* close what we're not using in the parent */
|
||||
close(ext2yate[1]);
|
||||
close(yate2ext[0]);
|
||||
m_pid = pid;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExtModReceiver::run()
|
||||
{
|
||||
if (!create(m_script.safe(),m_args.safe())) {
|
||||
destruct();
|
||||
return;
|
||||
}
|
||||
char buffer[1024];
|
||||
int posinbuf = 0;
|
||||
for (;;) {
|
||||
int readsize = ::read(m_in,buffer+posinbuf,sizeof(buffer)-posinbuf-1);
|
||||
if (!readsize) {
|
||||
destruct();
|
||||
return;
|
||||
}
|
||||
else if (readsize < 0) {
|
||||
Debug("ExtModule",DebugWarn,"Read error %d on %d",errno,m_in);
|
||||
destruct();
|
||||
return;
|
||||
}
|
||||
int totalsize = readsize + posinbuf;
|
||||
buffer[totalsize]=0;
|
||||
for (;;) {
|
||||
char *eoline = ::strchr(buffer,'\n');
|
||||
if (!eoline && ((int)::strlen(buffer) < totalsize))
|
||||
eoline=buffer+::strlen(buffer);
|
||||
if (!eoline)
|
||||
break;
|
||||
*eoline=0;
|
||||
if (buffer[0])
|
||||
processLine(buffer);
|
||||
totalsize -= eoline-buffer+1;
|
||||
::memmove(buffer,eoline+1,totalsize+1);
|
||||
}
|
||||
posinbuf = totalsize;
|
||||
}
|
||||
}
|
||||
|
||||
void ExtModReceiver::outputLine(const char *line)
|
||||
{
|
||||
Debug("ExtModReceiver",DebugInfo,"outputLine '%s'", line);
|
||||
::write(m_out,line,::strlen(line));
|
||||
char nl = '\n';
|
||||
::write(m_out,&nl,sizeof(nl));
|
||||
}
|
||||
|
||||
void ExtModReceiver::processLine(const char *line)
|
||||
{
|
||||
Debug("ExtModReceiver",DebugInfo,"processLine '%s'", line);
|
||||
ObjList *p = &m_waiting;
|
||||
for (; p; p=p->next()) {
|
||||
MsgHolder *msg = static_cast<MsgHolder *>(p->get());
|
||||
if (msg && msg->decode(line)) {
|
||||
Debug("ExtModReceiver",DebugInfo,"Matched message");
|
||||
p->remove(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Message m("");
|
||||
String id;
|
||||
if (m.decode(line,id) == -2) {
|
||||
Debug("ExtModReceiver",DebugInfo,"Created message [%p]",this);
|
||||
outputLine(m.encode(Engine::dispatch(m),id));
|
||||
Debug("ExtModReceiver",DebugInfo,"Dispatched message [%p]",this);
|
||||
return;
|
||||
}
|
||||
Debug("ExtModReceiver",DebugWarn,"Error: '%s'", line);
|
||||
}
|
||||
|
||||
bool ExtModHandler::received(Message &msg)
|
||||
{
|
||||
String dest(msg.getValue("callto"));
|
||||
if (dest.null())
|
||||
return false;
|
||||
Regexp r("^external/\\([^/]*\\)/\\(.*\\)$");
|
||||
if (!dest.matches(r))
|
||||
return false;
|
||||
DataEndpoint *dd = static_cast<DataEndpoint *>(msg.userData());
|
||||
String t = dest.matchString(1);
|
||||
int typ = 0;
|
||||
if (t == "none")
|
||||
typ = ExtModChan::DataNone;
|
||||
else if (t == "read")
|
||||
typ = ExtModChan::DataRead;
|
||||
else if (t == "write")
|
||||
typ = ExtModChan::DataWrite;
|
||||
else if (t == "both")
|
||||
typ = ExtModChan::DataBoth;
|
||||
else {
|
||||
Debug(DebugFail,"Invalid ExtModule method '%s', use 'none', 'read', 'write' or 'both'",
|
||||
t.c_str());
|
||||
return false;
|
||||
}
|
||||
if (typ != ExtModChan::DataNone && !dd) {
|
||||
Debug(DebugFail,"ExtMod '%s' call found but no data channel!",t.c_str());
|
||||
return false;
|
||||
}
|
||||
ExtModChan *em = new ExtModChan(dest.matchString(2).c_str(),typ);
|
||||
if (!em) {
|
||||
Debug(DebugFail,"Failed to create ExtMod for '%s'",dest.matchString(2).c_str());
|
||||
return false;
|
||||
}
|
||||
if (dd && dd->connect(em))
|
||||
em->deref();
|
||||
return true;
|
||||
}
|
||||
|
||||
ExtModulePlugin::ExtModulePlugin()
|
||||
: m_handler(0)
|
||||
{
|
||||
Output("Loaded module ExtModule");
|
||||
}
|
||||
|
||||
ExtModulePlugin::~ExtModulePlugin()
|
||||
{
|
||||
Output("Unloading module ExtModule");
|
||||
}
|
||||
|
||||
void ExtModulePlugin::initialize()
|
||||
{
|
||||
Output("Initializing module ExtModule");
|
||||
s_cfg = Engine::configFile("extmodule");
|
||||
s_cfg.load();
|
||||
if (!m_handler) {
|
||||
m_handler = new ExtModHandler("call");
|
||||
Engine::install(m_handler);
|
||||
NamedList *list = s_cfg.getSection("scripts");
|
||||
if (list)
|
||||
{
|
||||
unsigned int len = list->length();
|
||||
for (unsigned int i=0; i<len; i++) {
|
||||
NamedString *n = list->getParam(i);
|
||||
if (n) {
|
||||
new ExtModReceiver(n->name(),*n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INIT_PLUGIN(ExtModulePlugin);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,124 @@
|
|||
/**
|
||||
* gsmcodec.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* GSM 6.10 codec using libgsm
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
#include <telephony.h>
|
||||
|
||||
extern "C" {
|
||||
#include <gsm.h>
|
||||
|
||||
typedef gsm_signal gsm_block[160];
|
||||
}
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
int count = 0;
|
||||
|
||||
class GsmPlugin : public Plugin, public TranslatorFactory
|
||||
{
|
||||
public:
|
||||
GsmPlugin();
|
||||
~GsmPlugin();
|
||||
virtual void initialize() { }
|
||||
virtual DataTranslator *create(const String &sFormat, const String &dFormat);
|
||||
};
|
||||
|
||||
class GsmCodec : public DataTranslator
|
||||
{
|
||||
public:
|
||||
GsmCodec(const char *sFormat, const char *dFormat, bool encoding);
|
||||
~GsmCodec();
|
||||
virtual void Consume(const DataBlock &data);
|
||||
private:
|
||||
bool m_encoding;
|
||||
gsm m_gsm;
|
||||
DataBlock m_data;
|
||||
};
|
||||
|
||||
GsmCodec::GsmCodec(const char *sFormat, const char *dFormat, bool encoding)
|
||||
: DataTranslator(sFormat,dFormat), m_encoding(encoding), m_gsm(0)
|
||||
{
|
||||
Debug(DebugAll,"GsmCodec::GsmCodec(\"%s\",\"%s\",%scoding) [%p]",
|
||||
sFormat,dFormat, m_encoding ? "en" : "de",this);
|
||||
count++;
|
||||
m_gsm = ::gsm_create();
|
||||
}
|
||||
|
||||
GsmCodec::~GsmCodec()
|
||||
{
|
||||
Debug(DebugAll,"GsmCodec::~GsmCodec() [%p]",this);
|
||||
count--;
|
||||
if (m_gsm) {
|
||||
gsm temp = m_gsm;
|
||||
m_gsm = 0;
|
||||
::gsm_destroy(temp);
|
||||
}
|
||||
}
|
||||
|
||||
void GsmCodec::Consume(const DataBlock &data)
|
||||
{
|
||||
if (!(m_gsm && getTransSource()))
|
||||
return;
|
||||
ref();
|
||||
m_data += data;
|
||||
DataBlock outdata;
|
||||
int frames,consumed;
|
||||
if (m_encoding) {
|
||||
frames = m_data.length() / sizeof(gsm_block);
|
||||
consumed = frames * sizeof(gsm_block);
|
||||
if (frames) {
|
||||
outdata.assign(0,frames*sizeof(gsm_frame));
|
||||
for (int i=0; i<frames; i++)
|
||||
::gsm_encode(m_gsm,
|
||||
(gsm_signal*)(((gsm_block *)m_data.data())+i),
|
||||
(gsm_byte*)(((gsm_frame *)outdata.data())+i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
frames = m_data.length() / sizeof(gsm_frame);
|
||||
consumed = frames * sizeof(gsm_frame);
|
||||
if (frames) {
|
||||
outdata.assign(0,frames*sizeof(gsm_block));
|
||||
for (int i=0; i<frames; i++)
|
||||
::gsm_decode(m_gsm,
|
||||
(gsm_byte*)(((gsm_frame *)m_data.data())+i),
|
||||
(gsm_signal*)(((gsm_block *)outdata.data())+i));
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
Debug("GsmCodec",DebugAll,"%scoding %d frames of %d input bytes (consumed %d) in %d output bytes",
|
||||
m_encoding ? "en" : "de",frames,m_data.length(),consumed,outdata.length());
|
||||
#endif
|
||||
if (frames) {
|
||||
m_data.cut(-consumed);
|
||||
getTransSource()->Forward(outdata);
|
||||
}
|
||||
deref();
|
||||
}
|
||||
|
||||
GsmPlugin::GsmPlugin()
|
||||
{
|
||||
Output("Loaded module GSM - based on libgsm-%d.%d.%d",GSM_MAJOR,GSM_MINOR,GSM_PATCHLEVEL);
|
||||
}
|
||||
|
||||
GsmPlugin::~GsmPlugin()
|
||||
{
|
||||
Output("Unloading module GSM with %d codecs still in use",count);
|
||||
}
|
||||
|
||||
DataTranslator *GsmPlugin::create(const String &sFormat, const String &dFormat)
|
||||
{
|
||||
if (sFormat == "slin" && dFormat == "gsm")
|
||||
return new GsmCodec(sFormat,dFormat,true);
|
||||
else if (sFormat == "gsm" && dFormat == "slin")
|
||||
return new GsmCodec(sFormat,dFormat,false);
|
||||
else return 0;
|
||||
}
|
||||
|
||||
INIT_PLUGIN(GsmPlugin);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,944 @@
|
|||
/**
|
||||
* h323chan.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* As a special exception to the GNU General Public License, permission is
|
||||
* granted for additional uses of the text contained in this release of Yate
|
||||
* as noted here.
|
||||
* This exception is that permission is hereby granted to link Yate with the
|
||||
* OpenH323 and PWLIB runtime libraries to produce an executable image.
|
||||
*
|
||||
* H.323 channel
|
||||
*/
|
||||
|
||||
#include <ptlib.h>
|
||||
#include <h323.h>
|
||||
#include <h323pdu.h>
|
||||
#include <ptclib/delaychan.h>
|
||||
#include <gkserver.h>
|
||||
|
||||
|
||||
/* Guess if codecs are dynamically loaded or linked in */
|
||||
#if (OPENH323_MAJOR <= 1)
|
||||
#if (OPENH323_MINOR < 13)
|
||||
#define OLD_STYLE_CODECS 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <telengine.h>
|
||||
#include <telephony.h>
|
||||
#include <yateversn.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static Configuration s_cfg;
|
||||
static ObjList translate;
|
||||
|
||||
static TokenDict dict_str2code[] = {
|
||||
{ "alpha" , PProcess::AlphaCode },
|
||||
{ "beta" , PProcess::BetaCode },
|
||||
{ "release" , PProcess::ReleaseCode },
|
||||
{ 0 , 0 },
|
||||
};
|
||||
|
||||
class H323Process : public PProcess
|
||||
{
|
||||
PCLASSINFO(H323Process, PProcess)
|
||||
H323Process()
|
||||
: PProcess(
|
||||
s_cfg.getValue("general","vendor","Null Team"),
|
||||
s_cfg.getValue("general","product","YATE"),
|
||||
(unsigned short)s_cfg.getIntValue("general","major",YATE_MAJOR),
|
||||
(unsigned short)s_cfg.getIntValue("general","minor",YATE_MINOR),
|
||||
(PProcess::CodeStatus)s_cfg.getIntValue("general","status",dict_str2code,PProcess::AlphaCode),
|
||||
(unsigned short)s_cfg.getIntValue("general","build",YATE_BUILD)
|
||||
)
|
||||
{ Resume(); }
|
||||
public:
|
||||
void Main()
|
||||
{ }
|
||||
};
|
||||
|
||||
class YateH323EndPoint;
|
||||
class YateGatekeeperServer;
|
||||
|
||||
class YateGatekeeperCall : public H323GatekeeperCall
|
||||
{
|
||||
PCLASSINFO(YateGatekeeperCall, H323GatekeeperCall);
|
||||
public:
|
||||
YateGatekeeperCall(
|
||||
YateGatekeeperServer & server,
|
||||
const OpalGloballyUniqueID & callIdentifier, /// Unique call identifier
|
||||
Direction direction
|
||||
);
|
||||
|
||||
virtual H323GatekeeperRequest::Response OnAdmission(
|
||||
H323GatekeeperARQ & request
|
||||
);
|
||||
};
|
||||
|
||||
class TranslateObj : public GenObject
|
||||
{
|
||||
public:
|
||||
H225_TransportAddress_ipAddress ip;
|
||||
PString alias;
|
||||
String e164;
|
||||
};
|
||||
|
||||
class YateGatekeeperServer : public H323GatekeeperServer
|
||||
{
|
||||
PCLASSINFO(YateGatekeeperServer, H323GatekeeperServer);
|
||||
public:
|
||||
YateGatekeeperServer(YateH323EndPoint & ep);
|
||||
BOOL Init();
|
||||
H323GatekeeperRequest::Response OnRegistration(
|
||||
H323GatekeeperRRQ & request);
|
||||
H323GatekeeperRequest::Response OnUnregistration(
|
||||
H323GatekeeperURQ & request );
|
||||
H323GatekeeperCall * CreateCall(const OpalGloballyUniqueID & id,H323GatekeeperCall::Direction dir);
|
||||
BOOL TranslateAliasAddressToSignalAddress(const H225_AliasAddress & alias,H323TransportAddress & address);
|
||||
TranslateObj * findAlias(const PString & alias);
|
||||
virtual BOOL GetUsersPassword(const PString & alias,PString & password) const;
|
||||
|
||||
private:
|
||||
YateH323EndPoint & endpoint;
|
||||
};
|
||||
|
||||
class YateH323AudioSource : public DataSource, public PIndirectChannel
|
||||
{
|
||||
PCLASSINFO(YateH323AudioSource, PIndirectChannel)
|
||||
public:
|
||||
YateH323AudioSource() { }
|
||||
// ~YateH323AudioSource() { Debug(DebugAll,"h.323 source [%p] deleted",this); }
|
||||
virtual BOOL Close();
|
||||
virtual BOOL IsOpen() const;
|
||||
virtual BOOL Write(const void *buf, PINDEX len);
|
||||
private:
|
||||
PAdaptiveDelay writeDelay;
|
||||
};
|
||||
|
||||
class YateH323AudioConsumer : public DataConsumer, public PIndirectChannel
|
||||
{
|
||||
PCLASSINFO(YateH323AudioConsumer, PIndirectChannel)
|
||||
public:
|
||||
YateH323AudioConsumer() : m_exit(false) { }
|
||||
// ~YateH323AudioConsumer() { Debug(DebugAll,"h.323 consumer [%p] deleted",this); }
|
||||
virtual BOOL Close();
|
||||
virtual BOOL IsOpen() const;
|
||||
virtual BOOL Read(void *buf, PINDEX len);
|
||||
virtual void Consume(const DataBlock &data);
|
||||
private:
|
||||
DataBlock m_buffer;
|
||||
bool m_exit;
|
||||
Mutex m_mutex;
|
||||
};
|
||||
|
||||
class YateH323EndPoint : public H323EndPoint
|
||||
{
|
||||
PCLASSINFO(YateH323EndPoint, H323EndPoint)
|
||||
public:
|
||||
YateH323EndPoint();
|
||||
~YateH323EndPoint();
|
||||
virtual H323Connection *CreateConnection(unsigned callReference, void *userData,
|
||||
H323Transport *transport, H323SignalPDU *setupPDU);
|
||||
bool Init(void);
|
||||
YateGatekeeperServer *gkServer;
|
||||
};
|
||||
|
||||
class YateH323Connection : public H323Connection, public DataEndpoint
|
||||
{
|
||||
PCLASSINFO(YateH323Connection, H323Connection)
|
||||
public:
|
||||
YateH323Connection(YateH323EndPoint &endpoint, unsigned callReference, void *userdata);
|
||||
~YateH323Connection();
|
||||
virtual H323Connection::AnswerCallResponse OnAnswerCall(const PString &caller,
|
||||
const H323SignalPDU &signalPDU, H323SignalPDU &connectPDU);
|
||||
virtual void OnEstablished();
|
||||
virtual void OnCleared();
|
||||
virtual BOOL OnAlerting(const H323SignalPDU &alertingPDU, const PString &user);
|
||||
virtual void OnUserInputTone(char tone, unsigned duration, unsigned logicalChannel, unsigned rtpTimestamp);
|
||||
virtual void OnUserInputString(const PString &value);
|
||||
virtual BOOL OpenAudioChannel(BOOL isEncoding, unsigned bufferSize,
|
||||
H323AudioCodec &codec);
|
||||
virtual void disconnected();
|
||||
inline const String &id() const
|
||||
{ return m_id; }
|
||||
private:
|
||||
String m_id;
|
||||
};
|
||||
|
||||
class H323Handler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
H323Handler(const char *name) : MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class H323Dropper : public MessageHandler
|
||||
{
|
||||
public:
|
||||
H323Dropper(const char *name) : MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class H323Stopper : public MessageHandler
|
||||
{
|
||||
public:
|
||||
H323Stopper(const char *name) : MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class H323MsgThread : public Thread
|
||||
{
|
||||
public:
|
||||
H323MsgThread(Message *msg, YateH323Connection *conn)
|
||||
: Thread("H323MsgThread"), m_msg(msg), m_conn(conn) { }
|
||||
virtual void run();
|
||||
private:
|
||||
Message *m_msg;
|
||||
YateH323Connection *m_conn;
|
||||
};
|
||||
|
||||
class StatusHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
StatusHandler() : MessageHandler("status") { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class H323Plugin : public Plugin
|
||||
{
|
||||
public:
|
||||
H323Plugin();
|
||||
virtual ~H323Plugin();
|
||||
virtual void initialize();
|
||||
void cleanup();
|
||||
YateH323Connection *findConnectionLock(const char *id);
|
||||
inline YateH323EndPoint *ep()
|
||||
{ return m_endpoint; }
|
||||
inline ObjList &calls()
|
||||
{ return m_calls; }
|
||||
private:
|
||||
bool m_first;
|
||||
ObjList m_calls;
|
||||
YateH323EndPoint *m_endpoint;
|
||||
static H323Process *m_process;
|
||||
};
|
||||
|
||||
H323Process *H323Plugin::m_process = 0;
|
||||
static H323Plugin hplugin;
|
||||
|
||||
void H323MsgThread::run()
|
||||
{
|
||||
Engine::dispatch(m_msg);
|
||||
*m_msg = "preroute";
|
||||
Engine::dispatch(m_msg);
|
||||
*m_msg = "route";
|
||||
if (Engine::dispatch(m_msg) && !m_msg->retValue().null()) {
|
||||
*m_msg = "call";
|
||||
m_msg->addParam("callto",m_msg->retValue());
|
||||
m_msg->retValue() = 0;
|
||||
m_msg->userData(static_cast<DataEndpoint *>(m_conn));
|
||||
if (Engine::dispatch(m_msg)) {
|
||||
Debug(DebugInfo,"Routing H.323 [%p] call to '%s'",m_conn,m_msg->getValue("callto"));
|
||||
m_conn->deref();
|
||||
m_conn->AnsweringCall(H323Connection::AnswerCallNow);
|
||||
}
|
||||
else {
|
||||
Debug(DebugInfo,"Rejecting unconnected H.323 [%p] call",m_conn);
|
||||
m_conn->AnsweringCall(H323Connection::AnswerCallDenied);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug(DebugInfo,"Rejecting unrouted H.323 [%p] call",m_conn);
|
||||
m_conn->AnsweringCall(H323Connection::AnswerCallDenied);
|
||||
}
|
||||
delete m_msg;
|
||||
}
|
||||
|
||||
YateGatekeeperServer::YateGatekeeperServer(YateH323EndPoint & ep)
|
||||
: H323GatekeeperServer(ep),
|
||||
endpoint(ep)
|
||||
{
|
||||
Debug(DebugAll,"YateGatekeeperServer::YateGatekeeperServer() [%p]",this);
|
||||
}
|
||||
|
||||
BOOL YateGatekeeperServer::Init ()
|
||||
{
|
||||
|
||||
SetGatekeeperIdentifier("YATE gatekeeper");
|
||||
H323TransportAddressArray interfaces;
|
||||
const char *addr = 0;
|
||||
int i;
|
||||
for (i = 1; (addr = s_cfg.getValue("gk",("interface"+String(i)).c_str())); i++){
|
||||
if (!AddListener(new H323GatekeeperListener(endpoint, *this,s_cfg.getValue("gk","name","YateGatekeeper"),new H323TransportUDP(endpoint,PIPSocket::Address(addr),s_cfg.getIntValue("gk","port",1719),0))))
|
||||
Debug(DebugFail,"I can't start the listener for address: %s",addr);
|
||||
}
|
||||
Debug(DebugInfo,"i = %d",i);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
YateH323EndPoint::YateH323EndPoint()
|
||||
: gkServer(0)
|
||||
{
|
||||
Debug(DebugAll,"YateH323EndPoint::YateH323EndPoint() [%p]",this);
|
||||
}
|
||||
|
||||
YateH323EndPoint::~YateH323EndPoint()
|
||||
{
|
||||
Debug(DebugAll,"YateH323EndPoint::~YateH323EndPoint() [%p]",this);
|
||||
RemoveListener(0);
|
||||
ClearAllCalls(H323Connection::EndedByLocalUser, true);
|
||||
if (gkServer)
|
||||
delete gkServer;
|
||||
}
|
||||
|
||||
H323Connection *YateH323EndPoint::CreateConnection(unsigned callReference,
|
||||
void *userData, H323Transport *transport, H323SignalPDU *setupPDU)
|
||||
{
|
||||
return new YateH323Connection(*this,callReference,userData);
|
||||
}
|
||||
|
||||
bool YateH323EndPoint::Init(void)
|
||||
{
|
||||
if (s_cfg.getBoolValue("codecs","g711u",true))
|
||||
#ifdef OLD_STYLE_CODECS
|
||||
SetCapability(0,0,new H323_G711Capability(H323_G711Capability::muLaw));
|
||||
#else
|
||||
AddAllCapabilities(0, 0, "G.711-u*{sw}");
|
||||
#endif
|
||||
|
||||
if (s_cfg.getBoolValue("codecs","g711a",true))
|
||||
#ifdef OLD_STYLE_CODECS
|
||||
SetCapability(0,0,new H323_G711Capability(H323_G711Capability::ALaw));
|
||||
#else
|
||||
AddAllCapabilities(0, 0, "G.711-A*{sw}");
|
||||
#endif
|
||||
|
||||
if (s_cfg.getBoolValue("codecs","gsm0610",true)) {
|
||||
#ifdef OLD_STYLE_CODECS
|
||||
H323_GSM0610Capability *gsmCap = new H323_GSM0610Capability;
|
||||
SetCapability(0, 0, gsmCap);
|
||||
gsmCap->SetTxFramesInPacket(4);
|
||||
#else
|
||||
AddAllCapabilities(0, 0, "GSM*{sw}");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (s_cfg.getBoolValue("codecs","speexnarrow",true)) {
|
||||
#ifdef OLD_STYLE_CODECS
|
||||
SpeexNarrow3AudioCapability *speex3Cap = new SpeexNarrow3AudioCapability();
|
||||
SetCapability(0, 0, speex3Cap);
|
||||
speex3Cap->SetTxFramesInPacket(5);
|
||||
#else
|
||||
AddAllCapabilities(0, 0, "Speex*{sw}");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (s_cfg.getBoolValue("codecs","lpc10",true))
|
||||
#ifdef OLD_STYLE_CODECS
|
||||
SetCapability(0, 0, new H323_LPC10Capability(*this));
|
||||
#else
|
||||
AddAllCapabilities(0, 0, "LPC*{sw}");
|
||||
#endif
|
||||
|
||||
AddAllUserInputCapabilities(0,1);
|
||||
|
||||
PIPSocket::Address addr = INADDR_ANY;
|
||||
int port = s_cfg.getIntValue("ep","port",1720);
|
||||
if (s_cfg.getBoolValue("ep","ep",true)) {
|
||||
H323ListenerTCP *listener = new H323ListenerTCP(*this,addr,port);
|
||||
if (!(listener && StartListener(listener))) {
|
||||
Debug(DebugFail,"Unable to start H323 Listener at port %d",port);
|
||||
if (listener)
|
||||
delete listener;
|
||||
return false;
|
||||
}
|
||||
const char *ali = s_cfg.getValue("ep","alias","yate");
|
||||
SetLocalUserName(ali);
|
||||
if (s_cfg.getBoolValue("ep","gkclient",false)){
|
||||
const char *p = s_cfg.getValue("ep","password");
|
||||
if (p) {
|
||||
SetGatekeeperPassword(p);
|
||||
Debug(DebugInfo,"Enabling H.235 security access to gatekeeper %s",p);
|
||||
}
|
||||
const char *d = s_cfg.getValue("ep","gkip");
|
||||
const char *a = s_cfg.getValue("ep","gkname");
|
||||
if (d) {
|
||||
PString gkName = d;
|
||||
H323TransportUDP * rasChannel = new H323TransportUDP(*this);
|
||||
if (SetGatekeeper(gkName, rasChannel))
|
||||
Debug(DebugInfo,"Connect to gatekeeper ip = %s",d);
|
||||
else {
|
||||
Debug(DebugFail,"Unable to connect to gatekeeper ip = %s",d);
|
||||
if (listener)
|
||||
listener->Close();
|
||||
}
|
||||
} else if (a) {
|
||||
PString gkIdentifier = a;
|
||||
if (LocateGatekeeper(gkIdentifier))
|
||||
Debug(DebugInfo,"Connect to gatekeeper name = %s",a);
|
||||
else {
|
||||
Debug(DebugFail,"Unable to connect to gatekeeper name = %s",a);
|
||||
if (listener)
|
||||
listener->Close();
|
||||
}
|
||||
} else {
|
||||
if (DiscoverGatekeeper(new H323TransportUDP(*this)))
|
||||
Debug(DebugInfo,"Find a gatekeeper");
|
||||
else {
|
||||
Debug(DebugFail,"Unable to connect to any gatekeeper");
|
||||
if (listener)
|
||||
listener->Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* if (s_cfg.getBoolValue("gk","server",true))
|
||||
{
|
||||
gkServer = new YateGatekeeperServer(*this);
|
||||
gkServer->Init();
|
||||
}
|
||||
*/
|
||||
|
||||
// bool useGk = s_cfg.getBoolean("general","use_gatekeeper");
|
||||
return true;
|
||||
}
|
||||
|
||||
YateH323Connection::YateH323Connection(YateH323EndPoint &endpoint,
|
||||
unsigned callReference, void *userdata)
|
||||
: H323Connection(endpoint,callReference), DataEndpoint("h323")
|
||||
{
|
||||
Debug(DebugAll,"YateH323Connection::YateH323Connection(%p,%u,%p) [%p]",
|
||||
&endpoint,callReference,userdata,this);
|
||||
m_id = "h323/";
|
||||
m_id << callReference;
|
||||
setSource(new YateH323AudioSource);
|
||||
getSource()->deref();
|
||||
setConsumer(new YateH323AudioConsumer);
|
||||
getConsumer()->deref();
|
||||
DataEndpoint *dd = static_cast<DataEndpoint *>(userdata);
|
||||
if (dd && connect(dd))
|
||||
deref();
|
||||
hplugin.calls().append(this)->setDelete(false);
|
||||
}
|
||||
|
||||
YateH323Connection::~YateH323Connection()
|
||||
{
|
||||
Debug(DebugAll,"YateH323Connection::~YateH323Connection() [%p]",this);
|
||||
hplugin.calls().remove(this,false);
|
||||
CloseAllLogicalChannels(true);
|
||||
CloseAllLogicalChannels(false);
|
||||
}
|
||||
|
||||
H323Connection::AnswerCallResponse YateH323Connection::OnAnswerCall(const PString &caller,
|
||||
const H323SignalPDU &setupPDU, H323SignalPDU &connectPDU)
|
||||
{
|
||||
Debug(DebugInfo,"YateH323Connection::OnAnswerCall caller='%s'",(const char *)caller);
|
||||
|
||||
Message *m = new Message("ring");
|
||||
m->addParam("driver","h323");
|
||||
m->addParam("id",m_id);
|
||||
const char *s = s_cfg.getValue("incoming","context");
|
||||
if (s)
|
||||
m->addParam("context",s);
|
||||
|
||||
m->addParam("callername",caller);
|
||||
s = GetRemotePartyNumber();
|
||||
Debug(DebugInfo,"GetRemotePartyNumber()='%s'",s);
|
||||
m->addParam("caller",s ? s : (const char *)("h323/"+caller));
|
||||
|
||||
const H225_Setup_UUIE &setup = setupPDU.m_h323_uu_pdu.m_h323_message_body;
|
||||
const H225_ArrayOf_AliasAddress &adr = setup.m_destinationAddress;
|
||||
s = adr.GetSize() ? (const char *)H323GetAliasAddressString(adr[0]) : 0;
|
||||
if (!(s && *s))
|
||||
s = s_cfg.getValue("incoming","called");
|
||||
if (s)
|
||||
m->addParam("called",s);
|
||||
#if 0
|
||||
s = GetRemotePartyAddress();
|
||||
Debug(DebugInfo,"GetRemotePartyAddress()='%s'",s);
|
||||
if (s)
|
||||
m->addParam("calledname",s);
|
||||
#endif
|
||||
new H323MsgThread(m,this);
|
||||
return H323Connection::AnswerCallPending;
|
||||
}
|
||||
|
||||
void YateH323Connection::OnEstablished()
|
||||
{
|
||||
Debug(DebugInfo,"YateH323Connection::OnEstablished");
|
||||
if (!HadAnsweredCall())
|
||||
return;
|
||||
Message *m = new Message("answer");
|
||||
m->addParam("driver","h323");
|
||||
m->addParam("id",m_id);
|
||||
m->addParam("status","answered");
|
||||
Engine::enqueue(m);
|
||||
}
|
||||
|
||||
void YateH323Connection::OnCleared()
|
||||
{
|
||||
Debug(DebugInfo,"YateH323Connection::OnCleared");
|
||||
bool ans = HadAnsweredCall();
|
||||
disconnect();
|
||||
if (!ans)
|
||||
return;
|
||||
Message *m = new Message("hangup");
|
||||
m->addParam("driver","h323");
|
||||
m->addParam("id",m_id);
|
||||
Engine::enqueue(m);
|
||||
}
|
||||
|
||||
BOOL YateH323Connection::OnAlerting(const H323SignalPDU &alertingPDU, const PString &user)
|
||||
{
|
||||
Debug(DebugInfo,"YateH323Connection::OnAlerting '%s'",(const char *)user);
|
||||
Message *m = new Message("ringing");
|
||||
m->addParam("driver","h323");
|
||||
m->addParam("id",m_id);
|
||||
Engine::enqueue(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
void YateH323Connection::OnUserInputTone(char tone, unsigned duration, unsigned logicalChannel, unsigned rtpTimestamp)
|
||||
{
|
||||
Debug(DebugInfo,"YateH323Connection::OnUserInputTone '%c' duration=%u",tone,duration);
|
||||
}
|
||||
|
||||
void YateH323Connection::OnUserInputString(const PString &value)
|
||||
{
|
||||
Debug(DebugInfo,"YateH323Connection::OnUserInputString '%s'",(const char *)value);
|
||||
}
|
||||
|
||||
BOOL YateH323Connection::OpenAudioChannel(BOOL isEncoding, unsigned bufferSize,
|
||||
H323AudioCodec &codec)
|
||||
{
|
||||
if (isEncoding) {
|
||||
// data going TO h.323
|
||||
if (getConsumer())
|
||||
return codec.AttachChannel(static_cast<YateH323AudioConsumer *>(getConsumer()),false);
|
||||
}
|
||||
else {
|
||||
// data coming FROM h.323
|
||||
if (getSource())
|
||||
return codec.AttachChannel(static_cast<YateH323AudioSource *>(getSource()),false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void YateH323Connection::disconnected()
|
||||
{
|
||||
Debugger debug("YateH323Connection::disconnected()");
|
||||
// we must bypass the normal Yate refcounted destruction as OpenH323 will destroy the object
|
||||
ref();
|
||||
if (getSource())
|
||||
static_cast<YateH323AudioSource *>(getSource())->Close();
|
||||
if (getConsumer())
|
||||
static_cast<YateH323AudioConsumer *>(getConsumer())->Close();
|
||||
ClearCall();
|
||||
}
|
||||
|
||||
BOOL YateH323AudioConsumer::Close()
|
||||
{
|
||||
m_exit = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL YateH323AudioConsumer::IsOpen() const
|
||||
{
|
||||
return !m_exit;
|
||||
}
|
||||
|
||||
void YateH323AudioConsumer::Consume(const DataBlock &data)
|
||||
{
|
||||
Lock lock(m_mutex);
|
||||
if ((m_buffer.length() + data.length()) <= (480*5))
|
||||
m_buffer += data;
|
||||
#ifdef DEBUG
|
||||
else
|
||||
Debug("YateH323AudioConsumer",DebugAll,"Skipped %u bytes, buffer is full",data.length());
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL YateH323AudioConsumer::Read(void *buf, PINDEX len)
|
||||
{
|
||||
for (;;) {
|
||||
Lock lock(m_mutex);
|
||||
if (len >= (int)m_buffer.length()) {
|
||||
ref();
|
||||
Thread::yield();
|
||||
if (deref() || m_exit || Engine::exiting())
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
if (len > 0) {
|
||||
::memcpy(buf,m_buffer.data(),len);
|
||||
m_buffer.assign(len+(char *)m_buffer.data(),m_buffer.length()-len);
|
||||
#ifdef DEBUG
|
||||
Debug("YateH323AudioConsumer",DebugAll,"Pulled %d bytes from buffer",len);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
else
|
||||
len = 0;
|
||||
}
|
||||
lastReadCount = len;
|
||||
return (len != 0);
|
||||
}
|
||||
|
||||
BOOL YateH323AudioSource::Close()
|
||||
{
|
||||
DataSource::clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL YateH323AudioSource::IsOpen() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
BOOL YateH323AudioSource::Write(const void *buf, PINDEX len)
|
||||
{
|
||||
DataBlock data((void *)buf,len,false);
|
||||
Forward(data);
|
||||
data.clear(false);
|
||||
lastWriteCount = len;
|
||||
writeDelay.Delay(len/16);
|
||||
return true;
|
||||
}
|
||||
|
||||
TranslateObj * YateGatekeeperServer::findAlias(const PString &alias)
|
||||
{
|
||||
ObjList *p = &translate;
|
||||
for (; p; p=p->next()) {
|
||||
TranslateObj *t =
|
||||
static_cast<TranslateObj *>(p->get());
|
||||
if (t && t->alias == alias)
|
||||
return t;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL YateGatekeeperServer::GetUsersPassword(const PString & alias,PString & password) const
|
||||
{
|
||||
Message *m = new Message("auth");
|
||||
m->addParam("username",alias);
|
||||
Engine::dispatch(m);
|
||||
if (m->retValue() != NULL)
|
||||
{
|
||||
password = m->retValue();
|
||||
return true;
|
||||
} else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
H323GatekeeperCall * YateGatekeeperServer::CreateCall(const OpalGloballyUniqueID & id,
|
||||
H323GatekeeperCall::Direction dir)
|
||||
{
|
||||
return new YateGatekeeperCall(*this, id, dir);
|
||||
}
|
||||
|
||||
H323GatekeeperRequest::Response YateGatekeeperServer::OnRegistration(H323GatekeeperRRQ & request)
|
||||
{
|
||||
// PString request_s = request.GetGatekeeperIdentifier();
|
||||
// request.rrq.HasOptionalField(H225_RegistrationRequest::e_endpointIdentifier);
|
||||
PString alias;
|
||||
PString ips;
|
||||
PString r ;
|
||||
for (int j = 0; j < request.rrq.m_terminalAlias.GetSize(); j++) {
|
||||
alias = H323GetAliasAddressString(request.rrq.m_terminalAlias[j]);
|
||||
r = H323GetAliasAddressE164(request.rrq.m_terminalAlias[j]);
|
||||
// PString c = request.GetEndpointIdentifier();
|
||||
Debug(DebugInfo,"marimea matrici este %d : ",request.rrq.m_callSignalAddress.GetSize());
|
||||
// H225_TransportAddress_ipAddress ip=request.rrq.m_callSignalAddress[0];
|
||||
for (int k=0; k<request.rrq.m_callSignalAddress.GetSize();k++)
|
||||
{
|
||||
H225_TransportAddress_ipAddress ip=request.rrq.m_callSignalAddress[k];
|
||||
ips = String(ip.m_ip[0]) + "." + String(ip.m_ip[1]) + "." + String(ip.m_ip[2]) + "." + String(ip.m_ip[3]) + ":" + String((int)ip.m_port) ;
|
||||
Debug(DebugInfo,"Stringul initial este %s",(const char *)ips);
|
||||
}
|
||||
// Debug(DebugInfo,"ip %d.%d.%d.%d:%u",ip.m_ip[0], ip.m_ip[1], ip.m_ip[2], ip.m_ip[3],ip.m_port.GetValue());
|
||||
|
||||
// Debug(DebugInfo,"end point user id %s and e164 %s",(const char *)s,(const char *)r); //(const char *)request.m_endpointIdentifier); //request_s.GetLength());
|
||||
H225_TransportAddress_ipAddress ip=request.rrq.m_callSignalAddress[0];
|
||||
Message *m = new Message("regist");
|
||||
m->addParam("username",alias);
|
||||
m->addParam("techno","h323");
|
||||
m->addParam("data",ips);
|
||||
Engine::dispatch(m);
|
||||
Debug(DebugInfo,"prefix boo registering %s",m->retValue().c_str());
|
||||
|
||||
TranslateObj *t = new TranslateObj;
|
||||
t->ip = ip;
|
||||
t->alias = alias;
|
||||
t->e164 = m->retValue();
|
||||
translate.append(t);
|
||||
}
|
||||
/* for (int i = 0; i < request.rrq.m_callSignalAddress.GetSize(); i++) {
|
||||
Debug(DebugInfo,"end point identifier %d",request.rrq.m_callSignalAddress[i]); //(const char *)request.m_endpointIdentifier); //request_s.GetLength());
|
||||
}*/
|
||||
|
||||
return H323GatekeeperServer::OnRegistration(request);
|
||||
}
|
||||
H323GatekeeperRequest::Response YateGatekeeperServer::OnUnregistration(H323GatekeeperURQ & request )
|
||||
{
|
||||
/* for (int j = 0; j < request.urq.m_terminalAlias.GetSize(); j++) {
|
||||
PString s = H323GetAliasAddressString(request.urq.m_terminalAlias[j]);
|
||||
}*/
|
||||
// PString s = H323GetAliasAddressString(request.urq.m_callSignalAddress[0]);
|
||||
PString s = H323GetAliasAddressString(request.urq.m_endpointAlias[0]);
|
||||
TranslateObj * c = findAlias(s);
|
||||
|
||||
Message *m = new Message("unregist");
|
||||
m->addParam("prefix",c->e164);
|
||||
Debug(DebugInfo,"prefixh323 %s",c->e164.c_str());
|
||||
Engine::dispatch(m);
|
||||
|
||||
//Debug(DebugInfo,"aliasul descarcat este %s",(const char *)c->alias);
|
||||
translate.remove(c);
|
||||
|
||||
return H323GatekeeperServer::OnUnregistration(request);
|
||||
}
|
||||
|
||||
BOOL YateGatekeeperServer::TranslateAliasAddressToSignalAddress(const H225_AliasAddress & alias,H323TransportAddress & address)
|
||||
{
|
||||
PString aliasString = H323GetAliasAddressString(alias);
|
||||
// TranslateObj *f = new TranslateObj;
|
||||
//f->alias = aliasString;
|
||||
//translate.find()
|
||||
TranslateObj * c = findAlias(aliasString);
|
||||
// c->alias = aliasString;
|
||||
//Debug(DebugInfo,"ip-ul corespondent este %d.%d.%d.%d:%u",c->ip.m_ip[0],c->ip.m_ip[1],c->ip.m_ip[2],c->ip.m_ip[3],c->ip.m_port.GetValue());
|
||||
if (c)
|
||||
{
|
||||
Debug(DebugInfo,"alias-ul este %s si cel gasit este %s",(const char *)aliasString,(const char *)c->alias);
|
||||
// Debug(DebugInfo,"ip-ul corespondent este %d.%d.%d.%d:%u",c->ip.m_ip[0],c->ip.m_ip[1],c->ip.m_ip[2],c->ip.m_ip[3],c->ip.m_port.GetValue());
|
||||
String s = "ip$" + String(c->ip.m_ip[0]) + "." + String(c->ip.m_ip[1]) + "." + String(c->ip.m_ip[2]) + "." + String(c->ip.m_ip[3]) + ":" + String((int)c->ip.m_port) ;
|
||||
Debug(DebugInfo,"Stringul este %s",(const char *)s);
|
||||
/* H323TransportAddress aliasAsTransport = c->aliasaddres;
|
||||
PIPSocket::Address ip;
|
||||
WORD port = H323EndPoint::DefaultTcpPort;
|
||||
if (!aliasAsTransport.GetIpAndPort(ip, port)) {
|
||||
Debug(DebugInfo,"RAS\tCould not translate %s as host name.",(const char *)aliasString);
|
||||
return FALSE;
|
||||
}*/
|
||||
// H225_TransportAddress ceva = c->aliasaddres;
|
||||
// address = H323TransportAddress(ceva);
|
||||
//Debug(DebugInfo,"RAS\tTranslating alias %s to %s, host name",(const char *)aliasString,(const char *)address);
|
||||
// return TRUE;
|
||||
address = s.c_str();
|
||||
return TRUE;
|
||||
|
||||
//if (H323GatekeeperServer::TranslateAliasAddressToSignalAddress(alias, address))
|
||||
}
|
||||
// if (H323GatekeeperServer::TranslateAliasAddressToSignalAddress(alias, address))
|
||||
// return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
YateGatekeeperCall::YateGatekeeperCall(YateGatekeeperServer & gk,
|
||||
const OpalGloballyUniqueID & id,
|
||||
Direction dir)
|
||||
: H323GatekeeperCall(gk, id, dir)
|
||||
{
|
||||
}
|
||||
|
||||
H323GatekeeperRequest::Response YateGatekeeperCall::OnAdmission(H323GatekeeperARQ & info)
|
||||
{
|
||||
/* for (int i = 0; i < info.arq.m_srcInfo.GetSize(); i++) {
|
||||
PString alias = H323GetAliasAddressString(info.arq.m_srcInfo[i]);
|
||||
PString d = H323GetAliasAddressString(info.arq.m_destinationInfo[0]);
|
||||
Debug(DebugInfo,"aliasul in m_srcInfo %s si m_destinationInfo %s",(const char *)alias,(const char *)d);
|
||||
|
||||
}
|
||||
|
||||
return H323GatekeeperCall::OnAdmission(info);*/
|
||||
|
||||
#ifdef TEST_TOKEN
|
||||
info.acf.IncludeOptionalField(H225_AdmissionConfirm::e_tokens);
|
||||
info.acf.m_tokens.SetSize(1);
|
||||
info.acf.m_tokens[0].m_tokenOID = "1.2.36.76840296.1";
|
||||
info.acf.m_tokens[0].IncludeOptionalField(H235_ClearToken::e_nonStandard);
|
||||
info.acf.m_tokens[0].m_nonStandard.m_nonStandardIdentifier = "1.2.36.76840296.1.1";
|
||||
info.acf.m_tokens[0].m_nonStandard.m_data = "SnfYt0jUuZ4lVQv8umRYaH2JltXDRW6IuYcnASVU";
|
||||
#endif
|
||||
|
||||
#ifdef TEST_SLOW_ARQ
|
||||
if (info.IsFastResponseRequired()) {
|
||||
if (YateH323GatekeeperCall::OnAdmission(info) == H323GatekeeperRequest::Reject)
|
||||
return H323GatekeeperRequest::Reject;
|
||||
|
||||
return H323GatekeeperRequest::InProgress(5000); // 5 seconds maximum
|
||||
}
|
||||
|
||||
PTimeInterval delay = 500+PRandom::Number()%3500; // Take from 0.5 to 4 seconds
|
||||
PTRACE(3, "RAS\tTest ARQ delay " << delay);
|
||||
PThread::Sleep(delay);
|
||||
return H323GatekeeperRequest::Confirm;
|
||||
#else
|
||||
return H323GatekeeperCall::OnAdmission(info);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool H323Handler::received(Message &msg)
|
||||
{
|
||||
String dest(msg.getValue("callto"));
|
||||
if (dest.null())
|
||||
return false;
|
||||
Regexp r("^h323/\\(.*\\)$");
|
||||
if (!dest.matches(r))
|
||||
return false;
|
||||
if (!msg.userData()) {
|
||||
Debug(DebugFail,"H.323 call found but no data channel!");
|
||||
return false;
|
||||
}
|
||||
Debug(DebugInfo,"Found call to H.323 target='%s'",
|
||||
dest.matchString(1).c_str());
|
||||
PString p;
|
||||
H323Connection *conn = hplugin.ep()->MakeCallLocked(dest.matchString(1).c_str(),p,msg.userData());
|
||||
if (conn) {
|
||||
String caller(msg.getValue("caller"));
|
||||
if (caller.null())
|
||||
caller = msg.getValue("callername");
|
||||
else
|
||||
caller << " [" << s_cfg.getValue("ep","ident","yate") << "]";
|
||||
if (!caller.null()) {
|
||||
Debug(DebugInfo,"Setting H.323 caller name to '%s'",caller.c_str());
|
||||
conn->SetLocalPartyName(caller.c_str());
|
||||
}
|
||||
conn->Unlock();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool H323Dropper::received(Message &msg)
|
||||
{
|
||||
String id(msg.getValue("id"));
|
||||
if (id.null()) {
|
||||
Debug("H323Dropper",DebugInfo,"Dropping all calls");
|
||||
ObjList *l = &hplugin.calls();
|
||||
for (; l; l=l->next()) {
|
||||
YateH323Connection *c = static_cast<YateH323Connection *>(l->get());
|
||||
if (c && c->Lock()) {
|
||||
c->ClearCall(H323Connection::EndedByGatekeeper);
|
||||
c->Unlock();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!id.startsWith("h323"))
|
||||
return false;
|
||||
YateH323Connection *conn = hplugin.findConnectionLock(id);
|
||||
if (conn) {
|
||||
Debug("H323Dropper",DebugInfo,"Dropping call '%s' [%p]",conn->id().c_str(),conn);
|
||||
conn->ClearCall(H323Connection::EndedByGatekeeper);
|
||||
conn->Unlock();
|
||||
return true;
|
||||
}
|
||||
Debug("H323Dropper",DebugInfo,"Could not find call '%s'",id.c_str());
|
||||
return false;
|
||||
};
|
||||
|
||||
bool StatusHandler::received(Message &msg)
|
||||
{
|
||||
const char *sel = msg.getValue("module");
|
||||
if (sel && ::strcmp(sel,"h323chan") && ::strcmp(sel,"varchans"))
|
||||
return false;
|
||||
String st("h323chan,type=varchans");
|
||||
st << ",chans=" << hplugin.calls().count() << ",[LIST]";
|
||||
ObjList *l = &hplugin.calls();
|
||||
for (; l; l=l->next()) {
|
||||
YateH323Connection *c = static_cast<YateH323Connection *>(l->get());
|
||||
if (c && c->Lock()) {
|
||||
// HACK: we assume transport$address/callref format
|
||||
String s((const char *)c->GetCallToken());
|
||||
st << "," << c->id() << "=" << s.substr(0,s.rfind('/'));
|
||||
c->Unlock();
|
||||
}
|
||||
}
|
||||
msg.retValue() << st << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool H323Stopper::received(Message &msg)
|
||||
{
|
||||
hplugin.cleanup();
|
||||
return false;
|
||||
};
|
||||
|
||||
H323Plugin::H323Plugin()
|
||||
: m_first(true), m_endpoint(0)
|
||||
{
|
||||
Output("Loaded module H.323");
|
||||
}
|
||||
|
||||
void H323Plugin::cleanup()
|
||||
{
|
||||
if (m_endpoint) {
|
||||
delete m_endpoint;
|
||||
m_endpoint = 0;
|
||||
PSyncPoint terminationSync;
|
||||
terminationSync.Signal();
|
||||
Output("Waiting for OpenH323 to die");
|
||||
terminationSync.Wait();
|
||||
}
|
||||
m_calls.clear();
|
||||
}
|
||||
|
||||
H323Plugin::~H323Plugin()
|
||||
{
|
||||
cleanup();
|
||||
if (m_process) {
|
||||
delete m_process;
|
||||
m_process = 0;
|
||||
}
|
||||
}
|
||||
|
||||
YateH323Connection *H323Plugin::findConnectionLock(const char *id)
|
||||
{
|
||||
ObjList *l = &m_calls;
|
||||
for (; l; l=l->next()) {
|
||||
YateH323Connection *c = static_cast<YateH323Connection *>(l->get());
|
||||
if (c && c->Lock()) {
|
||||
if (c->id() == id)
|
||||
return c;
|
||||
c->Unlock();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void H323Plugin::initialize()
|
||||
{
|
||||
Output("Initializing module H.323");
|
||||
s_cfg = Engine::configFile("h323chan");
|
||||
s_cfg.load();
|
||||
if (!m_process)
|
||||
m_process = new H323Process;
|
||||
int dbg=s_cfg.getIntValue("general","debug");
|
||||
if (dbg)
|
||||
PTrace::Initialise(dbg,0,PTrace::Blocks | PTrace::Timestamp
|
||||
| PTrace::Thread | PTrace::FileAndLine);
|
||||
if (!m_endpoint) {
|
||||
m_endpoint = new YateH323EndPoint;
|
||||
m_endpoint->Init();
|
||||
}
|
||||
if (m_first) {
|
||||
m_first = false;
|
||||
Engine::install(new H323Handler("call"));
|
||||
Engine::install(new H323Dropper("drop"));
|
||||
Engine::install(new H323Stopper("engine.halt"));
|
||||
Engine::install(new StatusHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
tests="randcall msgsniff"
|
||||
if [ "$1" = "-d" ]; then
|
||||
for f in $tests; do rm $f.yate; done
|
||||
else
|
||||
for f in $tests; do ln -s ../test/$f.yate $f.yate; done
|
||||
fi
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
test.c
|
||||
This file holds the entry point of the Telephony Engine
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
class MsgSniff : public Plugin
|
||||
{
|
||||
public:
|
||||
MsgSniff();
|
||||
virtual void initialize();
|
||||
private:
|
||||
bool m_first;
|
||||
};
|
||||
|
||||
class SniffHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
SniffHandler() : MessageHandler(0,0) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
bool SniffHandler::received(Message &msg)
|
||||
{
|
||||
Output("Sniffed message '%s' time=%llu thread=%p",
|
||||
msg.c_str(),msg.msgTime().usec(),Thread::current());
|
||||
unsigned n = msg.length();
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
NamedString *s = msg.getParam(i);
|
||||
if (s)
|
||||
Output(" param['%s']='%s'",s->name().c_str(),s->c_str());
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
MsgSniff::MsgSniff()
|
||||
: m_first(true)
|
||||
{
|
||||
Output("Loaded module MsgSniffer");
|
||||
}
|
||||
|
||||
void MsgSniff::initialize()
|
||||
{
|
||||
Output("Initializing module MsgSniffer");
|
||||
if (m_first) {
|
||||
m_first = false;
|
||||
Engine::install(new SniffHandler);
|
||||
}
|
||||
}
|
||||
|
||||
INIT_PLUGIN(MsgSniff);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,216 @@
|
|||
/**
|
||||
* pgsqlroute.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Postgres SQL based routing
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
#include <telephony.h>
|
||||
|
||||
#include <libpq-fe.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static PGconn *conn=0;
|
||||
static Mutex dbmutex;
|
||||
|
||||
static unsigned s_route_rq = 0;
|
||||
static unsigned s_route_err = 0;
|
||||
static unsigned s_route_yes = 0;
|
||||
static unsigned s_route_no = 0;
|
||||
|
||||
class RouteHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
RouteHandler(const char *name, unsigned prio = 1)
|
||||
: MessageHandler(name,prio) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
bool RouteHandler::received(Message &msg)
|
||||
{
|
||||
char buffer[2048];
|
||||
unsigned long long tmr = Time::now();
|
||||
String called(msg.getValue("called"));
|
||||
if (called.null())
|
||||
return false;
|
||||
Lock lock(dbmutex);
|
||||
if (!conn)
|
||||
return false;
|
||||
s_route_rq++;
|
||||
const char *context = c_safe(msg.getValue("context","default"));
|
||||
snprintf(buffer,sizeof(buffer),"SELECT tehno,data,length (prefix) as lll"
|
||||
" from route where prefix= substring('%s',1,length(prefix))"
|
||||
" and context='%s' order by lll desc LIMIT 1",called.c_str(),context);
|
||||
PGresult *respgsql = PQexec(conn,buffer);
|
||||
if (!respgsql || PQresultStatus(respgsql) != PGRES_TUPLES_OK)
|
||||
{
|
||||
Debug(DebugFail,"Failed to query from database: %s",
|
||||
PQerrorMessage(conn));
|
||||
s_route_err++;
|
||||
return false;
|
||||
}
|
||||
if (PQntuples(respgsql) == 0) {
|
||||
Debug(DebugFail,"No route.");
|
||||
s_route_no++;
|
||||
return false;
|
||||
}
|
||||
msg.retValue() = String(PQgetvalue(respgsql,0,0))+"/" + String(PQgetvalue(respgsql,0,1));
|
||||
Debug(DebugInfo,"Routing call to '%s' in context '%s' using '%s' tehnology and data in %llu usec",
|
||||
called.c_str(),context,msg.retValue().c_str(),Time::now()-tmr);
|
||||
s_route_yes++;
|
||||
return true;
|
||||
};
|
||||
|
||||
class PrerouteHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
PrerouteHandler(const char *name, unsigned prio = 1)
|
||||
: MessageHandler(name,prio) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
bool PrerouteHandler::received(Message &msg)
|
||||
{
|
||||
char buffer[2048];
|
||||
// char select_called[200];
|
||||
// char select_channel[200];
|
||||
unsigned long long tmr = Time::now();
|
||||
// return immediately if there is already a context
|
||||
if (msg.getValue("context"))
|
||||
return false;
|
||||
String caller(msg.getValue("caller"));
|
||||
if (caller.null())
|
||||
return false;
|
||||
Lock lock(dbmutex);
|
||||
if (!conn)
|
||||
return false;
|
||||
String called(msg.getValue("called"));
|
||||
if (!caller.null())
|
||||
// snprintf(select_called,sizeof(select_called),"and called='%s'",called.c_str());
|
||||
snprintf(buffer,sizeof(buffer),"SELECT context,length (caller) as lll from preroute where caller= substring('%s',1,length(caller)) order by lll desc limit 1;",caller.c_str());
|
||||
PGresult *respgsql = PQexec(conn,buffer);
|
||||
if (!respgsql || PQresultStatus(respgsql) != PGRES_TUPLES_OK)
|
||||
{
|
||||
Debug(DebugFail,"Failed to query from database: %s",
|
||||
PQerrorMessage(conn));
|
||||
return false;
|
||||
}
|
||||
if (PQntuples(respgsql) == 0) {
|
||||
Debug(DebugFail,"No preroute.");
|
||||
return false;
|
||||
}
|
||||
msg.addParam("context",PQgetvalue(respgsql,0,0));
|
||||
Debug(DebugInfo,"Classifying caller '%s' in context '%s' in %llu usec",
|
||||
caller.c_str(),msg.getValue("context"),Time::now()-tmr);
|
||||
return true;
|
||||
|
||||
#if 0
|
||||
NamedList *l = s_cfg.getSection("contexts");
|
||||
if (l) {
|
||||
unsigned int len = l->length();
|
||||
for (unsigned int i=0; i<len; i++) {
|
||||
NamedString *n = l->getParam(i);
|
||||
if (n) {
|
||||
Regexp r(n->name());
|
||||
if (s.matches(r)) {
|
||||
msg.addParam("context",s.replaceMatches(*n));
|
||||
Debug(DebugInfo,"Classifying caller '%s' in context '%s' by rule #%u '%s' in %llu usec",
|
||||
s.c_str(),msg.getValue("context"),i+1,r.c_str(),Time::now()-tmr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug(DebugInfo,"Could not classify call from '%s', wasted %llu usec",
|
||||
s.c_str(),Time::now()-tmr);
|
||||
return false;
|
||||
#endif
|
||||
};
|
||||
|
||||
class StatusHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
StatusHandler(const char *name, unsigned prio = 1)
|
||||
: MessageHandler(name,prio) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
bool StatusHandler::received(Message &msg)
|
||||
{
|
||||
const char *sel = msg.getValue("module");
|
||||
if (sel && ::strcmp(sel,"pgsqlroute"))
|
||||
return false;
|
||||
|
||||
msg.retValue() << "PgSQLroute,conn=" << (conn != 0);
|
||||
msg.retValue() << ",total=" << s_route_rq << ",errors=" << s_route_err;
|
||||
msg.retValue() << ",routed=" << s_route_yes << ",noroute=" << s_route_no;
|
||||
msg.retValue() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class PGSQLRoutePlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
PGSQLRoutePlugin();
|
||||
~PGSQLRoutePlugin();
|
||||
virtual void initialize();
|
||||
private:
|
||||
bool m_first;
|
||||
};
|
||||
|
||||
PGSQLRoutePlugin::PGSQLRoutePlugin()
|
||||
: m_first(true)
|
||||
{
|
||||
Output("Loaded module PGSQLRoute");
|
||||
}
|
||||
|
||||
PGSQLRoutePlugin::~PGSQLRoutePlugin()
|
||||
{
|
||||
if (conn) {
|
||||
PQfinish(conn);
|
||||
conn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PGSQLRoutePlugin::initialize()
|
||||
{
|
||||
char *pgoptions=NULL,
|
||||
*pgtty=NULL;
|
||||
|
||||
Output("Initializing module PGSQLRoute");
|
||||
Configuration cfg(Engine::configFile("pgsqlroute"));
|
||||
const char *pghost = c_safe(cfg.getValue("general","host","localhost"));
|
||||
const char *pgport = c_safe(cfg.getValue("general","port","5432"));
|
||||
const char *dbName = c_safe(cfg.getValue("general","database","yate"));
|
||||
const char *dbUser = c_safe(cfg.getValue("general","user","postgres"));
|
||||
const char *dbPass = c_safe(cfg.getValue("general","password"));
|
||||
|
||||
Lock lock(dbmutex);
|
||||
if (conn)
|
||||
PQfinish(conn);
|
||||
conn = PQsetdbLogin(pghost,pgport,pgoptions,pgtty,dbName,dbUser,dbPass);
|
||||
if (PQstatus(conn) == CONNECTION_BAD) {
|
||||
Debug(DebugFail, "Connection to database '%s' failed.", dbName);
|
||||
Debug(DebugFail, "%s", PQerrorMessage(conn));
|
||||
PQfinish(conn);
|
||||
conn = 0;
|
||||
return;
|
||||
}
|
||||
// don't bother to install handlers until we are connected
|
||||
if (m_first && conn) {
|
||||
m_first = false;
|
||||
unsigned prio = cfg.getIntValue("general","priority",100);
|
||||
Engine::install(new PrerouteHandler("preroute",prio));
|
||||
Engine::install(new RouteHandler("route",prio));
|
||||
Engine::install(new StatusHandler("status"));
|
||||
}
|
||||
}
|
||||
|
||||
INIT_PLUGIN(PGSQLRoutePlugin);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,139 @@
|
|||
/**
|
||||
* regexroute.cpp
|
||||
*
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
* Regexp based routing
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
#include <telephony.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static Configuration s_cfg;
|
||||
|
||||
class RouteHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
RouteHandler(int prio)
|
||||
: MessageHandler("route",prio) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
bool RouteHandler::received(Message &msg)
|
||||
{
|
||||
unsigned long long tmr = Time::now();
|
||||
String s(msg.getValue("called"));
|
||||
if (s.null())
|
||||
return false;
|
||||
const char *context = msg.getValue("context","default");
|
||||
NamedList *l = s_cfg.getSection(context);
|
||||
if (l) {
|
||||
unsigned int len = l->length();
|
||||
for (unsigned int i=0; i<len; i++) {
|
||||
NamedString *n = l->getParam(i);
|
||||
if (n) {
|
||||
Regexp r(n->name());
|
||||
if (s.matches(r)) {
|
||||
msg.retValue() = s.replaceMatches(*n);
|
||||
Debug(DebugInfo,"Routing call to '%s' in context '%s' via `%s' by rule #%u '%s' in %llu usec",
|
||||
s.c_str(),context,msg.retValue().c_str(),i+1,r.c_str(),Time::now()-tmr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug(DebugInfo,"Could not route call to '%s' in context '%s', wasted %llu usec",
|
||||
s.c_str(),context,Time::now()-tmr);
|
||||
return false;
|
||||
};
|
||||
|
||||
class PrerouteHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
PrerouteHandler(int prio)
|
||||
: MessageHandler("preroute",prio) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
bool PrerouteHandler::received(Message &msg)
|
||||
{
|
||||
unsigned long long tmr = Time::now();
|
||||
// return immediately if there is already a context
|
||||
if (msg.getValue("context"))
|
||||
return false;
|
||||
// String s(msg.getValue("caller"));
|
||||
String s(msg.getValue("driver")); s+="/";
|
||||
s+=msg.getValue("span"); s+="/";
|
||||
s+=msg.getValue("channel"); s+="/";
|
||||
s+=msg.getValue("caller");
|
||||
|
||||
if (s.null())
|
||||
return false;
|
||||
NamedList *l = s_cfg.getSection("contexts");
|
||||
if (l) {
|
||||
unsigned int len = l->length();
|
||||
for (unsigned int i=0; i<len; i++) {
|
||||
NamedString *n = l->getParam(i);
|
||||
if (n) {
|
||||
Regexp r(n->name());
|
||||
if (s.matches(r)) {
|
||||
msg.addParam("context",s.replaceMatches(*n));
|
||||
Debug(DebugInfo,"Classifying caller '%s' in context '%s' by rule #%u '%s' in %llu usec",
|
||||
s.c_str(),msg.getValue("context"),i+1,r.c_str(),Time::now()-tmr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug(DebugInfo,"Could not classify call from '%s', wasted %llu usec",
|
||||
s.c_str(),Time::now()-tmr);
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
class RegexRoutePlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
RegexRoutePlugin();
|
||||
virtual void initialize();
|
||||
private:
|
||||
MessageHandler *m_preroute, *m_route;
|
||||
};
|
||||
|
||||
RegexRoutePlugin::RegexRoutePlugin()
|
||||
: m_preroute(0), m_route(0)
|
||||
{
|
||||
Output("Loaded module RegexRoute");
|
||||
}
|
||||
|
||||
void RegexRoutePlugin::initialize()
|
||||
{
|
||||
Output("Initializing module RegexRoute");
|
||||
s_cfg = Engine::configFile("regexroute");
|
||||
s_cfg.load();
|
||||
if (m_preroute) {
|
||||
delete m_preroute;
|
||||
m_preroute = 0;
|
||||
}
|
||||
if (m_route) {
|
||||
delete m_route;
|
||||
m_route = 0;
|
||||
}
|
||||
unsigned priority = s_cfg.getIntValue("priorities","preroute",100);
|
||||
if (priority) {
|
||||
m_preroute = new PrerouteHandler(priority);
|
||||
Engine::install(m_preroute);
|
||||
}
|
||||
priority = s_cfg.getIntValue("priorities","route",100);
|
||||
if (priority) {
|
||||
m_route = new RouteHandler(priority);
|
||||
Engine::install(m_route);
|
||||
}
|
||||
}
|
||||
|
||||
INIT_PLUGIN(RegexRoutePlugin);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,279 @@
|
|||
/* register.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Ask for a registration from this module.
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <libpq-fe.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static PGconn *conn=0;
|
||||
Mutex dbmutex;
|
||||
|
||||
static unsigned s_route_rq = 0;
|
||||
static unsigned s_route_err = 0;
|
||||
static unsigned s_route_yes = 0;
|
||||
static unsigned s_route_no = 0;
|
||||
|
||||
class AuthHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
AuthHandler(const char *name)
|
||||
: MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class RegistHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
RegistHandler(const char *name)
|
||||
: MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class UnRegistHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
UnRegistHandler(const char *name)
|
||||
: MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class RouteHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
RouteHandler(const char *name)
|
||||
: MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class StatusHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
StatusHandler(const char *name, unsigned prio = 1)
|
||||
: MessageHandler(name,prio) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class RegistThread : public Thread
|
||||
{
|
||||
public:
|
||||
RegistThread();
|
||||
~RegistThread();
|
||||
void run(void);
|
||||
};
|
||||
|
||||
class RegistPlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
RegistPlugin();
|
||||
~RegistPlugin();
|
||||
virtual void initialize();
|
||||
private:
|
||||
AuthHandler *m_authhandler;
|
||||
RegistHandler *m_registhandler;
|
||||
UnRegistHandler *m_unregisthandler;
|
||||
RouteHandler *m_routehandler;
|
||||
StatusHandler *m_statushandler;
|
||||
};
|
||||
|
||||
bool AuthHandler::received(Message &msg)
|
||||
{
|
||||
// const char *calltime = c_safe(msg.getValue("time"));
|
||||
const char *username = c_safe(msg.getValue("username"));
|
||||
|
||||
Lock lock(dbmutex);
|
||||
if (!conn)
|
||||
return false;
|
||||
|
||||
char buffer[2048];
|
||||
snprintf(buffer,sizeof(buffer),"SELECT password FROM register WHERE username='%s'",username);
|
||||
PGresult *respgsql = PQexec(conn,buffer);
|
||||
if (!respgsql || PQresultStatus(respgsql) != PGRES_TUPLES_OK)
|
||||
{
|
||||
Debug(DebugFail,"Failed to query from database: %s",
|
||||
PQerrorMessage(conn));
|
||||
return false;
|
||||
}
|
||||
if (PQntuples(respgsql) == 0) {
|
||||
Debug(DebugFail,"No user.");
|
||||
return false;
|
||||
}
|
||||
msg.retValue() << PQgetvalue(respgsql,0,0);
|
||||
return true;
|
||||
};
|
||||
|
||||
bool RegistHandler::received(Message &msg)
|
||||
{
|
||||
// const char *calltime = c_safe(msg.getValue("time"));
|
||||
const char *username = c_safe(msg.getValue("username"));
|
||||
const char *techno = c_safe(msg.getValue("techno"));
|
||||
const char *data = c_safe(msg.getValue("data"));
|
||||
|
||||
Lock lock(dbmutex);
|
||||
if (!conn)
|
||||
return false;
|
||||
|
||||
char buffer[2048];
|
||||
snprintf(buffer,sizeof(buffer),"SELECT credit,price,e164,context FROM register WHERE username='%s'",username);
|
||||
PGresult *respgsql = PQexec(conn,buffer);
|
||||
if (!respgsql || PQresultStatus(respgsql) != PGRES_TUPLES_OK)
|
||||
{
|
||||
Debug(DebugFail,"Failed to query from database: %s",
|
||||
PQerrorMessage(conn));
|
||||
return false;
|
||||
}
|
||||
if (PQntuples(respgsql) == 0) {
|
||||
Debug(DebugFail,"No credit.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *credit = PQgetvalue(respgsql,0,0);
|
||||
const char *price = PQgetvalue(respgsql,0,1);
|
||||
const char *prefix = PQgetvalue(respgsql,0,2);
|
||||
const char *context = PQgetvalue(respgsql,0,3);
|
||||
|
||||
snprintf(buffer,sizeof(buffer),"INSERT INTO routepaid (context,prefix,tehno,data,price) VALUES ('%s','%s','%s','%s',%s);",context,prefix,techno,data,price);
|
||||
|
||||
PGresult *respgsql1 = PQexec(conn,buffer);
|
||||
if (!respgsql1 || PQresultStatus(respgsql1) != PGRES_COMMAND_OK)
|
||||
Debug(DebugFail,"Failed to insert in database: %s",
|
||||
PQerrorMessage(conn));
|
||||
msg.retValue() = prefix;
|
||||
Debug(DebugInfo,"prefix in register este %s",prefix);
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
bool UnRegistHandler::received(Message &msg)
|
||||
{
|
||||
const char *prefix = c_safe(msg.getValue("prefix"));
|
||||
Debug(DebugInfo,"prefix=%s",prefix);
|
||||
|
||||
Lock lock(dbmutex);
|
||||
if (!conn)
|
||||
return false;
|
||||
|
||||
char buffer[2048];
|
||||
snprintf(buffer,sizeof(buffer),"DELETE from routepaid WHERE prefix='%s'",prefix);
|
||||
PGresult *respgsql = PQexec(conn,buffer);
|
||||
if (!respgsql || PQresultStatus(respgsql) != PGRES_TUPLES_OK)
|
||||
{
|
||||
Debug(DebugFail,"Failed to query from database: %s",
|
||||
PQerrorMessage(conn));
|
||||
return false;
|
||||
}
|
||||
if (PQntuples(respgsql) == 0) {
|
||||
Debug(DebugFail,"No user.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
bool RouteHandler::received(Message &msg)
|
||||
{
|
||||
char buffer[2048];
|
||||
unsigned long long tmr = Time::now();
|
||||
String called(msg.getValue("called"));
|
||||
if (called.null())
|
||||
return false;
|
||||
Lock lock(dbmutex);
|
||||
if (!conn)
|
||||
return false;
|
||||
s_route_rq++;
|
||||
const char *context = c_safe(msg.getValue("context","default"));
|
||||
snprintf(buffer,sizeof(buffer),"SELECT tehno,data,length (prefix) as lll,price"
|
||||
" from routepaid where prefix= substring('%s',1,length(prefix))"
|
||||
" and context='%s' order by lll desc LIMIT 1",called.c_str(),context);
|
||||
PGresult *respgsql = PQexec(conn,buffer);
|
||||
if (!respgsql || PQresultStatus(respgsql) != PGRES_TUPLES_OK)
|
||||
{
|
||||
Debug(DebugFail,"Failed to query from database: %s",
|
||||
PQerrorMessage(conn));
|
||||
s_route_err++;
|
||||
return false;
|
||||
}
|
||||
if (PQntuples(respgsql) == 0) {
|
||||
Debug(DebugFail,"No route.");
|
||||
s_route_no++;
|
||||
return false;
|
||||
}
|
||||
msg.retValue() = String(PQgetvalue(respgsql,0,0))+"/" + String(PQgetvalue(respgsql,0,1));
|
||||
Debug(DebugInfo,"Routing call to '%s' in context '%s' using '%s' tehnology and data in %llu usec",
|
||||
called.c_str(),context,msg.retValue().c_str(),Time::now()-tmr);
|
||||
s_route_yes++;
|
||||
return true;
|
||||
};
|
||||
|
||||
bool StatusHandler::received(Message &msg)
|
||||
{
|
||||
msg.retValue() << "Register,conn=" << (conn != 0) <<"\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
RegistPlugin::RegistPlugin()
|
||||
: m_authhandler(0),m_registhandler(0),m_routehandler(0),m_statushandler(0)
|
||||
{
|
||||
Output("Loaded module Registration");
|
||||
}
|
||||
|
||||
RegistPlugin::~RegistPlugin()
|
||||
{
|
||||
if (conn) {
|
||||
PQfinish(conn);
|
||||
conn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void RegistPlugin::initialize()
|
||||
{
|
||||
char *pgoptions=NULL,
|
||||
*pgtty=NULL;
|
||||
Output("Initializing module Register for PostgreSQL");
|
||||
Configuration cfg(Engine::configFile("register"));
|
||||
const char *pghost = c_safe(cfg.getValue("general","host","localhost"));
|
||||
const char *pgport = c_safe(cfg.getValue("general","port","5432"));
|
||||
const char *dbName = c_safe(cfg.getValue("general","database","yate"));
|
||||
const char *dbUser = c_safe(cfg.getValue("general","user","postgres"));
|
||||
const char *dbPass = c_safe(cfg.getValue("general","password"));
|
||||
|
||||
Lock lock(dbmutex);
|
||||
if (conn)
|
||||
PQfinish(conn);
|
||||
conn = PQsetdbLogin(pghost,pgport,pgoptions,pgtty,dbName,dbUser,dbPass);
|
||||
if (PQstatus(conn) == CONNECTION_BAD) {
|
||||
Debug(DebugFail, "Connection to database '%s' failed.", dbName);
|
||||
Debug(DebugFail, "%s", PQerrorMessage(conn));
|
||||
PQfinish(conn);
|
||||
conn = 0;
|
||||
return;
|
||||
}
|
||||
if (!m_registhandler) {
|
||||
Output("Installing Registering handler");
|
||||
Engine::install(new RegistHandler("regist"));
|
||||
}
|
||||
if (!m_unregisthandler) {
|
||||
Output("Installing UnRegistering handler");
|
||||
Engine::install(new UnRegistHandler("unregist"));
|
||||
}
|
||||
if (!m_authhandler) {
|
||||
Output("Installing Authentification handler");
|
||||
Engine::install(new AuthHandler("auth"));
|
||||
}
|
||||
if (!m_routehandler) {
|
||||
Output("Installing Route handler");
|
||||
Engine::install(new RouteHandler("route"));
|
||||
}
|
||||
if (!m_statushandler) {
|
||||
Output("Installing Status handler");
|
||||
Engine::install(new StatusHandler("status"));
|
||||
}
|
||||
}
|
||||
|
||||
INIT_PLUGIN(RegistPlugin);
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,457 @@
|
|||
/**
|
||||
* rmanager.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* This module gets the messages from YATE out so anyone can use an
|
||||
* administrating interface.
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static const char s_helpmsg[] =
|
||||
"Available commands:\n"
|
||||
" debug [level|on|off]\n"
|
||||
" machine [on|off]\n"
|
||||
" status [module]\n"
|
||||
" drop {chan|*|all}\n"
|
||||
" call chan target\n"
|
||||
" reload\n"
|
||||
" quit\n"
|
||||
" stop [exitcode]\n";
|
||||
|
||||
static Configuration s_cfg;
|
||||
|
||||
/* I need this here because i'm gonna use it in both classes */
|
||||
int sock = -1;
|
||||
|
||||
//we gonna create here the list with all the new connections.
|
||||
static ObjList connectionlist;
|
||||
|
||||
class RManagerThread : public Thread
|
||||
{
|
||||
public:
|
||||
RManagerThread() : Thread("RManager Listener") { }
|
||||
virtual void run();
|
||||
private:
|
||||
};
|
||||
|
||||
class Connection : public GenObject, public Thread
|
||||
{
|
||||
public:
|
||||
Connection(int sock);
|
||||
~Connection();
|
||||
|
||||
virtual void run();
|
||||
void processLine(const char *line);
|
||||
void write(const char *str, int len = -1);
|
||||
void writeDebug(const char *str);
|
||||
void write(Message &msg,bool received);
|
||||
inline void write(const String &s)
|
||||
{ write(s.safe(),s.length()); }
|
||||
static Connection *checkCreate(int sock);
|
||||
private:
|
||||
int m_socket;
|
||||
bool m_debug;
|
||||
bool m_machine;
|
||||
};
|
||||
|
||||
class RManager : public Plugin
|
||||
{
|
||||
public:
|
||||
RManager();
|
||||
~RManager();
|
||||
virtual void initialize();
|
||||
private:
|
||||
bool m_first;
|
||||
};
|
||||
|
||||
static void dbg_remote_func(const char *buf)
|
||||
{
|
||||
ObjList *p = &connectionlist;
|
||||
for (; p; p=p->next()) {
|
||||
Connection *con = static_cast<Connection *>(p->get());
|
||||
if (con)
|
||||
con->writeDebug(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void RManagerThread::run()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
int sinlen = sizeof(sin);
|
||||
int as = ::accept(sock, (struct sockaddr *)&sin, (socklen_t *)&sinlen);
|
||||
if (as < 0) {
|
||||
Debug("RManager",DebugWarn, "Accept error: %s\n", strerror(errno));
|
||||
continue;
|
||||
} else {
|
||||
if (Connection::checkCreate(as))
|
||||
Debug("RManager",DebugInfo,"Connection established from %s:%u",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port));
|
||||
else {
|
||||
Debug("RManager",DebugWarn,"Connection rejected for %s:%u",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port));
|
||||
::close(as);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connection *Connection::checkCreate(int sock)
|
||||
{
|
||||
// should check IP address here
|
||||
return new Connection(sock);
|
||||
}
|
||||
|
||||
Connection::Connection(int sock)
|
||||
: Thread("RManager Connection"), m_socket(sock), m_debug(false), m_machine(false)
|
||||
{
|
||||
const char *hdr = s_cfg.getValue("general","header","YATE (http://YATE.null.ro) ready.");
|
||||
if (hdr) {
|
||||
write(hdr);
|
||||
write("\n");
|
||||
}
|
||||
connectionlist.append(this);
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
m_debug = false;
|
||||
connectionlist.remove(this,false);
|
||||
::close(m_socket);
|
||||
}
|
||||
|
||||
void Connection::run()
|
||||
{
|
||||
if (::fcntl(m_socket,F_SETFL,O_NONBLOCK)) {
|
||||
Debug("RManager",DebugFail, "Failed to set tcp socket to nonblocking mode: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
// For the sake of responsiveness try to turn off the tcp assembly timer
|
||||
int arg = 1;
|
||||
if (::setsockopt(m_socket, SOL_SOCKET, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0)
|
||||
Debug("RManager",DebugWarn, "Failed to set tcp socket to TCP_NODELAY mode: %s\n", strerror(errno));
|
||||
|
||||
struct timeval timer;
|
||||
char buffer[300];
|
||||
int posinbuf = 0;
|
||||
while (posinbuf < (int)sizeof(buffer)-1) {
|
||||
timer.tv_sec = 0;
|
||||
timer.tv_usec = 30000;
|
||||
fd_set readfd;
|
||||
FD_ZERO(&readfd);
|
||||
FD_SET(m_socket, &readfd);
|
||||
fd_set errorfd;
|
||||
FD_ZERO(&errorfd);
|
||||
FD_SET(m_socket, &errorfd);
|
||||
int c = ::select(m_socket+1,&readfd,0,&errorfd,&timer);
|
||||
// Debug(DebugInfo,"trec pasul 1");
|
||||
if (c > 0) {
|
||||
if (FD_ISSET(m_socket,&errorfd)) {
|
||||
Debug("RManager",DebugInfo,"Socket exception condition on %d",m_socket);
|
||||
return;
|
||||
}
|
||||
int readsize = ::read(m_socket,buffer+posinbuf,sizeof(buffer)-posinbuf-1);
|
||||
if (!readsize) {
|
||||
Debug("RManager",DebugInfo,"Socket condition EOF on %d",m_socket);
|
||||
return;
|
||||
}
|
||||
else if (readsize > 0) {
|
||||
int totalsize = readsize + posinbuf;
|
||||
buffer[totalsize]=0;
|
||||
#ifdef DEBUG
|
||||
Debug("RManager",DebugInfo,"read=%d pos=%d buffer='%s'",readsize,posinbuf,buffer);
|
||||
#endif
|
||||
for (;;) {
|
||||
// Try to accomodate various telnet modes
|
||||
char *eoline = ::strchr(buffer,'\r');
|
||||
if (!eoline)
|
||||
eoline = ::strchr(buffer,'\n');
|
||||
if (!eoline && ((int)::strlen(buffer) < totalsize))
|
||||
eoline=buffer+::strlen(buffer);
|
||||
if (!eoline)
|
||||
break;
|
||||
*eoline=0;
|
||||
if (buffer[0])
|
||||
processLine(buffer);
|
||||
totalsize -= eoline-buffer+1;
|
||||
::memmove(buffer,eoline+1,totalsize+1);
|
||||
}
|
||||
posinbuf = totalsize;
|
||||
}
|
||||
else if ((readsize < 0) && (errno != EINTR) && (errno != EAGAIN)) {
|
||||
Debug("RManager",DebugWarn,"Socket read error %d on %d",errno,m_socket);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((c < 0) && (errno != EINTR)) {
|
||||
Debug("RManager",DebugWarn,"socket select error %d on %d",errno,m_socket);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool startSkip(String &s, const char *keyword)
|
||||
{
|
||||
if (s.startsWith(keyword,true)) {
|
||||
s >> keyword;
|
||||
s.trimBlanks();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Connection::processLine(const char *line)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Debug("RManager",DebugInfo,"processLine = %s",line);
|
||||
#endif
|
||||
String str(line);
|
||||
str.trimBlanks();
|
||||
if (str.null())
|
||||
return;
|
||||
|
||||
if (startSkip(str,"status"))
|
||||
{
|
||||
Message m("status");
|
||||
if (!str.null()) {
|
||||
m.addParam("module",str);
|
||||
str = ":" + str;
|
||||
}
|
||||
Engine::dispatch(m);
|
||||
str = "%%+status" + str + "\n";
|
||||
str << m.retValue() << "%%-status\n";
|
||||
write(str);
|
||||
}
|
||||
else if (startSkip(str,"drop"))
|
||||
{
|
||||
if (str.null()) {
|
||||
write(m_machine ? "%%=drop:fail=noarg\n" : "You must specify what connection to drop!\n");
|
||||
return;
|
||||
}
|
||||
Message m("drop");
|
||||
bool all = false;
|
||||
if (str == "*" || str == "all") {
|
||||
all = true;
|
||||
str = "all calls";
|
||||
}
|
||||
else
|
||||
m.addParam("id",str);
|
||||
if (Engine::dispatch(m))
|
||||
str = (m_machine ? "%%=drop:success:" : "Dropped ") + str + "\n";
|
||||
else if (all)
|
||||
str = (m_machine ? "%%=drop:unknown:" : "Tried to drop ") + str + "\n";
|
||||
else
|
||||
str = (m_machine ? "%%=drop:fail:" : "Could not drop ") + str + "\n";
|
||||
write(str);
|
||||
}
|
||||
else if (startSkip(str,"call"))
|
||||
{
|
||||
int pos = str.find(' ');
|
||||
if (pos <= 0) {
|
||||
write(m_machine ? "%%=call:fail=noarg\n" : "You must specify source and target!\n");
|
||||
return;
|
||||
}
|
||||
Message m("call");
|
||||
m.addParam("callto",str.substr(0,pos));
|
||||
m.addParam("target",str.substr(pos+1));
|
||||
|
||||
if (Engine::dispatch(m))
|
||||
str = (m_machine ? "%%=call:success:" : "Called ") + str + "\n";
|
||||
else
|
||||
str = (m_machine ? "%%=call:fail:" : "Could not call ") + str + "\n";
|
||||
write(str);
|
||||
}
|
||||
else if (startSkip(str,"debug"))
|
||||
{
|
||||
if (startSkip(str,"level")) {
|
||||
int dbg = debugLevel();
|
||||
str >> dbg;
|
||||
dbg = debugLevel(dbg);
|
||||
}
|
||||
else
|
||||
str >> m_debug;
|
||||
if (m_machine) {
|
||||
str = "%%=debug:level=";
|
||||
str << debugLevel() << ":local=" << m_debug << "\n";
|
||||
}
|
||||
else {
|
||||
str = "Debug level: ";
|
||||
str << debugLevel() << " local: " << (m_debug ? "on\n" : "off\n");
|
||||
}
|
||||
write(str);
|
||||
}
|
||||
else if (startSkip(str,"machine"))
|
||||
{
|
||||
str >> m_machine;
|
||||
str = "Machine mode: ";
|
||||
str += (m_machine ? "on\n" : "off\n");
|
||||
write(str);
|
||||
}
|
||||
else if (startSkip(str,"reload"))
|
||||
{
|
||||
write(m_machine ? "%%=reload\n" : "Reinitializing...\n");
|
||||
Engine::init();
|
||||
}
|
||||
else if (startSkip(str,"quit"))
|
||||
{
|
||||
write(m_machine ? "%%=quit\n" : "Goodbye!\n");
|
||||
cancel();
|
||||
}
|
||||
else if (startSkip(str,"stop"))
|
||||
{
|
||||
unsigned code = 0;
|
||||
str >> code;
|
||||
write(m_machine ? "%%=shutdown\n" : "Engine shutting down - bye!\n");
|
||||
Engine::halt(code);
|
||||
}
|
||||
else if (startSkip(str,"help") || startSkip(str,"?"))
|
||||
{
|
||||
Message m("help");
|
||||
if (!str.null())
|
||||
{
|
||||
m.addParam("command",str);
|
||||
if (Engine::dispatch(m))
|
||||
write(m.retValue());
|
||||
else
|
||||
write("No help for '"+str+"'\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
m.retValue() = s_helpmsg;
|
||||
Engine::dispatch(m);
|
||||
write(m.retValue());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message m("command");
|
||||
m.addParam("line",str);
|
||||
if (Engine::dispatch(m))
|
||||
write(m.retValue());
|
||||
else
|
||||
write((m_machine ? "%%=syntax:" : "Cannot understand: ") + str + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::write(Message &msg,bool received)
|
||||
{
|
||||
if (!m_machine)
|
||||
return;
|
||||
String s = msg.encode(received,"");
|
||||
s << "\n";
|
||||
write(s.c_str());
|
||||
}
|
||||
|
||||
void Connection::writeDebug(const char *str)
|
||||
{
|
||||
if (m_debug && str && *str)
|
||||
write(str,::strlen(str));
|
||||
}
|
||||
|
||||
void Connection::write(const char *str, int len)
|
||||
{
|
||||
if (len < 0)
|
||||
len = ::strlen(str);
|
||||
if (int written = ::write(m_socket,str,len) != len) {
|
||||
Debug("RManager",DebugInfo,"Socket %d wrote only %d out of %d bytes",m_socket,written,len);
|
||||
// Destroy the thread, will kill the connection
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
|
||||
static void postHook(Message &msg, bool received)
|
||||
{
|
||||
ObjList *p = &connectionlist;
|
||||
for (; p; p=p->next()) {
|
||||
Connection *con = static_cast<Connection *>(p->get());
|
||||
if (con)
|
||||
con->write(msg,received);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
RManager::RManager()
|
||||
: m_first(true)
|
||||
{
|
||||
Output("Loaded module RManager");
|
||||
Debugger::setIntOut(dbg_remote_func);
|
||||
}
|
||||
|
||||
RManager::~RManager()
|
||||
{
|
||||
Output("Unloading module RManager");
|
||||
if (sock != -1) {
|
||||
::close(sock);
|
||||
sock = -1;
|
||||
}
|
||||
Engine::self()->setHook();
|
||||
Debugger::setIntOut(0);
|
||||
}
|
||||
|
||||
void RManager::initialize()
|
||||
{
|
||||
Output("Initializing module RManager");
|
||||
s_cfg = Engine::configFile("rmanager");
|
||||
s_cfg.load();
|
||||
|
||||
if (sock >= 0)
|
||||
return;
|
||||
|
||||
/* configuration */
|
||||
int port = s_cfg.getIntValue("general","port",5038);
|
||||
const char *host = c_safe(s_cfg.getValue("general","addr","127.0.0.1"));
|
||||
if (!(port && *host))
|
||||
return;
|
||||
|
||||
/* starting the socket */
|
||||
struct sockaddr_in bindaddr;
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
bindaddr.sin_family = AF_INET;
|
||||
bindaddr.sin_addr.s_addr = inet_addr(host);
|
||||
bindaddr.sin_port = htons(port);
|
||||
if (sock < 0) {
|
||||
Debug("RManager",DebugFail,"Unable to create the listening socket: %s",strerror(errno));
|
||||
return;
|
||||
}
|
||||
const int reuseFlag = 1;
|
||||
::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,(const char*)&reuseFlag,sizeof reuseFlag);
|
||||
if (::bind(sock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
|
||||
Debug("RManager",DebugFail,"Failed to bind to %s:%u : %s",inet_ntoa(bindaddr.sin_addr),ntohs(bindaddr.sin_port),strerror(errno));
|
||||
::close(sock);
|
||||
sock = -1;
|
||||
return;
|
||||
}
|
||||
if (listen(sock, 2)) {
|
||||
Debug("RManager",DebugFail,"Unable to listen on socket: %s\n", strerror(errno));
|
||||
::close(sock);
|
||||
sock = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// don't bother to install handlers until we are listening
|
||||
if (m_first) {
|
||||
m_first = false;
|
||||
Engine::self()->setHook(postHook);
|
||||
new RManagerThread;
|
||||
}
|
||||
}
|
||||
|
||||
INIT_PLUGIN(RManager);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,299 @@
|
|||
/**
|
||||
* tonegen.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Tones generator
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
#include <telephony.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static ObjList tones;
|
||||
static ObjList chans;
|
||||
|
||||
typedef struct {
|
||||
int nsamples;
|
||||
const short *data;
|
||||
} Tone;
|
||||
|
||||
class ToneSource : public ThreadedSource
|
||||
{
|
||||
public:
|
||||
~ToneSource();
|
||||
virtual void run();
|
||||
inline const String &name()
|
||||
{ return m_name; }
|
||||
static ToneSource *getTone(const String &tone);
|
||||
private:
|
||||
ToneSource(const String &tone);
|
||||
static Tone *getBlock(const String &tone);
|
||||
String m_name;
|
||||
Tone *m_tone;
|
||||
unsigned m_brate;
|
||||
unsigned m_total;
|
||||
unsigned long long m_time;
|
||||
};
|
||||
|
||||
class ToneChan : public DataEndpoint
|
||||
{
|
||||
public:
|
||||
ToneChan(const String &tone);
|
||||
~ToneChan();
|
||||
virtual void disconnected();
|
||||
};
|
||||
|
||||
class ToneHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
ToneHandler(const char *name) : MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class StatusHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
StatusHandler() : MessageHandler("status") { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class ToneGenPlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
ToneGenPlugin();
|
||||
~ToneGenPlugin();
|
||||
virtual void initialize();
|
||||
private:
|
||||
ToneHandler *m_handler;
|
||||
};
|
||||
|
||||
// 421.052Hz (19 samples @ 8kHz) sine wave, pretty close to standard 425Hz
|
||||
static short tone421hz[] = {
|
||||
19,
|
||||
3246,6142,8371,9694,9965,9157,7357,4759,1645,
|
||||
-1645,-4759,-7357,-9157,-9965,-9694,-8371,-6142,-3246,
|
||||
0 };
|
||||
|
||||
static short get_sample(const short *data, int index)
|
||||
{
|
||||
return data ? data[1+(index % data[0])] : 0;
|
||||
}
|
||||
|
||||
static short get_sample(const Tone *data, int index)
|
||||
{
|
||||
const Tone *d = data;
|
||||
while (index >= d->nsamples) {
|
||||
index -= d->nsamples;
|
||||
d++;
|
||||
if (!d->nsamples)
|
||||
d = data;
|
||||
}
|
||||
return get_sample(d->data,index);
|
||||
}
|
||||
|
||||
static int get_length(const Tone *data)
|
||||
{
|
||||
int len = 0;
|
||||
for (; data->nsamples; data++)
|
||||
len += data->nsamples;
|
||||
return len;
|
||||
}
|
||||
|
||||
static Tone t_dial[] = { { 8000, tone421hz }, { 0, 0 } };
|
||||
|
||||
static Tone t_busy[] = { { 4000, tone421hz }, { 4000, 0 }, { 0, 0 } };
|
||||
|
||||
static Tone t_specdial[] = { { 7600, tone421hz }, { 400, 0 }, { 0, 0 } };
|
||||
|
||||
static Tone t_ring[] = { { 8000, tone421hz }, { 32000, 0 }, { 0, 0 } };
|
||||
|
||||
ToneSource::ToneSource(const String &tone)
|
||||
: m_name(tone), m_tone(0), m_brate(16000), m_total(0), m_time(0)
|
||||
{
|
||||
Debug(DebugAll,"ToneSource::ToneSource(\"%s\") [%p]",tone.c_str(),this);
|
||||
m_tone = getBlock(tone);
|
||||
tones.append(this);
|
||||
if (m_tone)
|
||||
start("ToneSource");
|
||||
}
|
||||
|
||||
ToneSource::~ToneSource()
|
||||
{
|
||||
Debug(DebugAll,"ToneSource::~ToneSource() [%p] total=%u",this,m_total);
|
||||
if (m_time) {
|
||||
m_time = Time::now() - m_time;
|
||||
m_time = (m_total*1000000ULL + m_time/2) / m_time;
|
||||
Debug(DebugAll,"ToneSource rate=%llu b/s",m_time);
|
||||
}
|
||||
tones.remove(this,false);
|
||||
}
|
||||
|
||||
Tone *ToneSource::getBlock(const String &tone)
|
||||
{
|
||||
if (tone == "dial" || tone == "dt")
|
||||
return t_dial;
|
||||
else if (tone == "busy" || tone == "bs")
|
||||
return t_busy;
|
||||
else if (tone == "ring" || tone == "rt")
|
||||
return t_ring;
|
||||
else if (tone == "specdial" || tone == "sd")
|
||||
return t_specdial;
|
||||
Debug(DebugWarn,"No waveform is defined for tone '%s'",tone.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
ToneSource *ToneSource::getTone(const String &tone)
|
||||
{
|
||||
ObjList *l = &tones;
|
||||
for (; l; l = l->next()) {
|
||||
ToneSource *t = static_cast<ToneSource *>(l->get());
|
||||
if (t && (t->name() == tone)) {
|
||||
t->ref();
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return new ToneSource(tone);
|
||||
}
|
||||
|
||||
void ToneSource::run()
|
||||
{
|
||||
Debug(DebugAll,"ToneSource::run() [%p]",this);
|
||||
unsigned long long tpos = Time::now();
|
||||
m_time = tpos;
|
||||
DataBlock data(0,480);
|
||||
int pos = 0;
|
||||
while (m_tone) {
|
||||
short *d = (short *) data.data();
|
||||
for (unsigned int i = data.length()/2; i--; pos++)
|
||||
*d++ = get_sample(m_tone,pos);
|
||||
pos = pos % get_length(m_tone);
|
||||
long long dly = tpos - Time::now();
|
||||
if (dly > 0) {
|
||||
#ifdef DEBUG
|
||||
Debug("ToneSource",DebugAll,"Sleeping for %lld usec",dly);
|
||||
#endif
|
||||
::usleep((unsigned long)dly);
|
||||
}
|
||||
Forward(data);
|
||||
m_total += data.length();
|
||||
tpos += (data.length()*1000000ULL/m_brate);
|
||||
};
|
||||
m_time = Time::now() - m_time;
|
||||
m_time = (m_total*1000000ULL + m_time/2) / m_time;
|
||||
Debug(DebugAll,"ToneSource [%p] end, total=%u (%llu b/s)",this,m_total,m_time);
|
||||
m_time = 0;
|
||||
}
|
||||
|
||||
ToneChan::ToneChan(const String &tone)
|
||||
: DataEndpoint("tone")
|
||||
{
|
||||
Debug(DebugAll,"ToneChan::ToneChan(\"%s\") [%p]",tone.c_str(),this);
|
||||
chans.append(this);
|
||||
ToneSource *t = ToneSource::getTone(tone);
|
||||
if (t) {
|
||||
setSource(t);
|
||||
t->deref();
|
||||
}
|
||||
}
|
||||
|
||||
ToneChan::~ToneChan()
|
||||
{
|
||||
Debug(DebugAll,"ToneChan::~ToneChan() [%p]",this);
|
||||
chans.remove(this,false);
|
||||
}
|
||||
|
||||
void ToneChan::disconnected()
|
||||
{
|
||||
Debugger debug("ToneChan::disconnected()"," [%p]",this);
|
||||
destruct();
|
||||
}
|
||||
|
||||
bool ToneHandler::received(Message &msg)
|
||||
{
|
||||
String dest(msg.getValue("callto"));
|
||||
if (dest.null())
|
||||
return false;
|
||||
Regexp r("^tone/\\(.*\\)$");
|
||||
if (!dest.matches(r))
|
||||
return false;
|
||||
String tone = dest.matchString(1);
|
||||
DataEndpoint *dd = static_cast<DataEndpoint *>(msg.userData());
|
||||
if (dd)
|
||||
dd->connect(new ToneChan(tone));
|
||||
else {
|
||||
const char *targ = msg.getValue("target");
|
||||
if (!targ) {
|
||||
Debug(DebugWarn,"Tone outgoing call with no target!");
|
||||
return false;
|
||||
}
|
||||
Message m("preroute");
|
||||
m.addParam("id",dest);
|
||||
m.addParam("caller",dest);
|
||||
m.addParam("called",targ);
|
||||
Engine::dispatch(m);
|
||||
m = "route";
|
||||
if (Engine::dispatch(m)) {
|
||||
m = "call";
|
||||
m.addParam("callto",m.retValue());
|
||||
m.retValue() = 0;
|
||||
ToneChan *tc = new ToneChan(dest.matchString(1).c_str());
|
||||
m.userData(tc);
|
||||
if (Engine::dispatch(m))
|
||||
return true;
|
||||
Debug(DebugFail,"Tone outgoing call not accepted!");
|
||||
delete tc;
|
||||
}
|
||||
else
|
||||
Debug(DebugWarn,"Tone outgoing call but no route!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatusHandler::received(Message &msg)
|
||||
{
|
||||
const char *sel = msg.getValue("module");
|
||||
if (sel && ::strcmp(sel,"tonegen"))
|
||||
return false;
|
||||
msg.retValue() << "tonegen,tones=" << tones.count() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
ToneGenPlugin::ToneGenPlugin()
|
||||
: m_handler(0)
|
||||
{
|
||||
Output("Loaded module ToneGen");
|
||||
}
|
||||
|
||||
ToneGenPlugin::~ToneGenPlugin()
|
||||
{
|
||||
Output("Unloading module ToneGen");
|
||||
ObjList *l = &chans;
|
||||
while (l) {
|
||||
ToneChan *t = static_cast<ToneChan *>(l->get());
|
||||
if (t)
|
||||
t->disconnect();
|
||||
if (l->get() == t)
|
||||
l = l->next();
|
||||
}
|
||||
chans.clear();
|
||||
tones.clear();
|
||||
}
|
||||
|
||||
void ToneGenPlugin::initialize()
|
||||
{
|
||||
Output("Initializing module ToneGen");
|
||||
if (!m_handler) {
|
||||
m_handler = new ToneHandler("call");
|
||||
Engine::install(m_handler);
|
||||
Engine::install(new StatusHandler);
|
||||
}
|
||||
}
|
||||
|
||||
INIT_PLUGIN(ToneGenPlugin);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,226 @@
|
|||
/**
|
||||
* wavefile.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* Wave file driver (record+playback)
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
#include <telephony.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
class WaveChan;
|
||||
|
||||
class WaveSource : public ThreadedSource
|
||||
{
|
||||
public:
|
||||
WaveSource(const char *file, WaveChan *chan);
|
||||
~WaveSource();
|
||||
virtual void run();
|
||||
virtual void cleanup();
|
||||
private:
|
||||
WaveChan *m_chan;
|
||||
int m_fd;
|
||||
unsigned m_brate;
|
||||
unsigned m_total;
|
||||
};
|
||||
|
||||
class WaveConsumer : public DataConsumer
|
||||
{
|
||||
public:
|
||||
WaveConsumer(const char *file);
|
||||
~WaveConsumer();
|
||||
virtual void Consume(const DataBlock &data);
|
||||
private:
|
||||
int m_fd;
|
||||
unsigned m_total;
|
||||
};
|
||||
|
||||
class WaveChan : public DataEndpoint
|
||||
{
|
||||
public:
|
||||
WaveChan(const char *file, bool record);
|
||||
~WaveChan();
|
||||
virtual void disconnected();
|
||||
};
|
||||
|
||||
class WaveHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
WaveHandler(const char *name) : MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class WaveFilePlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
WaveFilePlugin();
|
||||
virtual void initialize();
|
||||
private:
|
||||
WaveHandler *m_handler;
|
||||
};
|
||||
|
||||
WaveSource::WaveSource(const char *file, WaveChan *chan)
|
||||
: m_chan(chan), m_fd(-1), m_brate(16000), m_total(0)
|
||||
{
|
||||
Debug(DebugAll,"WaveSource::WaveSource(\"%s\") [%p]",file,this);
|
||||
m_fd = ::open(file,O_RDONLY|O_NOCTTY);
|
||||
if (m_fd >= 0)
|
||||
start("WaveSource");
|
||||
else
|
||||
Debug(DebugFail,"Opening '%s': error %d: %s",
|
||||
file, errno, ::strerror(errno));
|
||||
}
|
||||
|
||||
WaveSource::~WaveSource()
|
||||
{
|
||||
Debug(DebugAll,"WaveSource::~WaveSource() [%p] total=%u",this,m_total);
|
||||
if (m_fd >= 0) {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void WaveSource::run()
|
||||
{
|
||||
DataBlock data(0,480);
|
||||
int r = 0;
|
||||
unsigned long long tpos = Time::now();
|
||||
do {
|
||||
r = ::read(m_fd,data.data(),data.length());
|
||||
if (r < 0) {
|
||||
if (errno == EINTR) {
|
||||
r = 1;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (r < (int)data.length())
|
||||
data.assign(data.data(),r);
|
||||
long long dly = tpos - Time::now();
|
||||
if (dly > 0) {
|
||||
#ifdef DEBUG
|
||||
Debug("WaveSource",DebugAll,"Sleeping for %lld usec",dly);
|
||||
#endif
|
||||
::usleep((unsigned long)dly);
|
||||
}
|
||||
Forward(data);
|
||||
m_total += r;
|
||||
tpos += (r*1000000ULL/m_brate);
|
||||
} while (r > 0);
|
||||
Debug(DebugAll,"WaveSource [%p] end of data",this);
|
||||
}
|
||||
|
||||
void WaveSource::cleanup()
|
||||
{
|
||||
Debug(DebugAll,"WaveSource [%p] cleanup, total=%u",this,m_total);
|
||||
m_chan->disconnect();
|
||||
}
|
||||
|
||||
WaveConsumer::WaveConsumer(const char *file)
|
||||
: m_fd(-1), m_total(0)
|
||||
{
|
||||
Debug(DebugAll,"WaveConsumer::WaveConsumer(\"%s\") [%p]",file,this);
|
||||
m_fd = ::creat(file,S_IRUSR|S_IWUSR);
|
||||
if (m_fd < 0)
|
||||
Debug(DebugFail,"Creating '%s': error %d: %s",
|
||||
file, errno, ::strerror(errno));
|
||||
}
|
||||
|
||||
WaveConsumer::~WaveConsumer()
|
||||
{
|
||||
Debug(DebugAll,"WaveConsumer::~WaveConsumer() [%p] total=%u",this,m_total);
|
||||
if (m_fd >= 0) {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void WaveConsumer::Consume(const DataBlock &data)
|
||||
{
|
||||
if ((m_fd >= 0) && !data.null()) {
|
||||
::write(m_fd,data.data(),data.length());
|
||||
m_total += data.length();
|
||||
}
|
||||
}
|
||||
|
||||
WaveChan::WaveChan(const char *file, bool record)
|
||||
: DataEndpoint("wavefile")
|
||||
{
|
||||
Debug(DebugAll,"WaveChan::WaveChan(%s) [%p]",(record ? "record" : "play"),this);
|
||||
if (record) {
|
||||
setConsumer(new WaveConsumer(file));
|
||||
getConsumer()->deref();
|
||||
}
|
||||
else {
|
||||
setSource(new WaveSource(file,this));
|
||||
getSource()->deref();
|
||||
}
|
||||
}
|
||||
|
||||
WaveChan::~WaveChan()
|
||||
{
|
||||
Debug(DebugAll,"WaveChan::~WaveChan() [%p]",this);
|
||||
}
|
||||
|
||||
void WaveChan::disconnected()
|
||||
{
|
||||
Debugger debug("WaveChan::disconnected()"," [%p]",this);
|
||||
destruct();
|
||||
}
|
||||
|
||||
bool WaveHandler::received(Message &msg)
|
||||
{
|
||||
String dest(msg.getValue("callto"));
|
||||
if (dest.null())
|
||||
return false;
|
||||
Regexp r("^wave/\\([^/]*\\)/\\(.*\\)$");
|
||||
if (!dest.matches(r))
|
||||
return false;
|
||||
if (!msg.userData()) {
|
||||
Debug(DebugFail,"Wave call found but no data channel!");
|
||||
return false;
|
||||
}
|
||||
DataEndpoint *dd = static_cast<DataEndpoint *>(msg.userData());
|
||||
if (dest.matchString(1) == "record") {
|
||||
Debug(DebugInfo,"Record to wave file '%s'",dest.matchString(2).c_str());
|
||||
dd->connect(new WaveChan(dest.matchString(2).c_str(),true));
|
||||
return true;
|
||||
}
|
||||
else if (dest.matchString(1) == "play") {
|
||||
Debug(DebugInfo,"Play from wave file '%s'",dest.matchString(2).c_str());
|
||||
dd->connect(new WaveChan(dest.matchString(2).c_str(),false));
|
||||
return true;
|
||||
}
|
||||
Debug(DebugFail,"Invalid wavefile method '%s', use 'record' or 'play'",
|
||||
dest.matchString(1).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
WaveFilePlugin::WaveFilePlugin()
|
||||
: m_handler(0)
|
||||
{
|
||||
Output("Loaded module WaveFile");
|
||||
}
|
||||
|
||||
void WaveFilePlugin::initialize()
|
||||
{
|
||||
Output("Initializing module WaveFile");
|
||||
if (!m_handler) {
|
||||
m_handler = new WaveHandler("call");
|
||||
Engine::install(m_handler);
|
||||
}
|
||||
}
|
||||
|
||||
INIT_PLUGIN(WaveFilePlugin);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
|||
#! /bin/sh
|
||||
|
||||
yate="./yate"
|
||||
set_conf="-c ./conf.d"
|
||||
set_mods="-m ./modules"
|
||||
if [ "$1" = "--gdb" ]; then
|
||||
shift
|
||||
yate="gdb --args $yate"
|
||||
fi
|
||||
if [ "$1" = "--core" ]; then
|
||||
shift
|
||||
yate="gdb $yate core*"
|
||||
set_conf=""
|
||||
set_mods=""
|
||||
fi
|
||||
if [ "$1" = "--valgrind" ]; then
|
||||
shift
|
||||
yate="valgrind $1 $yate"
|
||||
shift
|
||||
fi
|
||||
|
||||
for opt in $@; do
|
||||
case "$opt" in
|
||||
-c)
|
||||
set_conf=
|
||||
;;
|
||||
-m)
|
||||
set_mods=
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
LD_LIBRARY_PATH=.@H323_RUN@:$LD_LIBRARY_PATH exec $yate $set_conf $set_mods "$@"
|
|
@ -0,0 +1,4 @@
|
|||
core*
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -0,0 +1,8 @@
|
|||
core*
|
||||
*.h
|
||||
*.raw
|
||||
?2?
|
||||
gen
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -0,0 +1,25 @@
|
|||
# Makefile
|
||||
# This file holds the make rules for the Telephony Engine
|
||||
|
||||
all: all.h
|
||||
|
||||
all.h: ./gen.c ./gen.sh ./gen.awk
|
||||
./gen.sh "."
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.raw ?2? gen core*
|
||||
|
||||
.PHONY: mrproper
|
||||
mrproper: clean
|
||||
rm -f *.h
|
||||
|
||||
.PHONY: strip
|
||||
strip: all
|
||||
|
||||
install:
|
||||
|
||||
uninstall:
|
||||
|
||||
Makefile: ./Makefile.in ../config.status
|
||||
cd .. && ./config.status
|
|
@ -0,0 +1,10 @@
|
|||
BEGIN {
|
||||
for (i=0;i<=255;i++)
|
||||
printf("%c",i) >"08b.raw";
|
||||
|
||||
for (j=0;j<=255;j++)
|
||||
for (i=0;i<=255;i++)
|
||||
printf("%c%c",i,j) >"16b.raw";
|
||||
|
||||
exit;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#include <stdio.h>
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
int n = 0;
|
||||
if (argv[1][0] == 'b') {
|
||||
unsigned char c;
|
||||
printf("static unsigned char %s[] = {",argv[2]);
|
||||
while (fread(&c,1,1,stdin)) {
|
||||
if (n)
|
||||
printf(",");
|
||||
if (((n++) % 16) == 0)
|
||||
printf("\n");
|
||||
printf(" 0x%02X",c);
|
||||
}
|
||||
printf("\n};\n");
|
||||
}
|
||||
else {
|
||||
unsigned short s;
|
||||
printf("static unsigned short %s[] = {",argv[2]);
|
||||
while (fread(&s,2,1,stdin)) {
|
||||
if (n)
|
||||
printf(",");
|
||||
if (((n++) % 8) == 0)
|
||||
printf("\n");
|
||||
printf(" 0x%04X",s);
|
||||
}
|
||||
printf("\n};\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#! /bin/sh
|
||||
|
||||
awk -f "$1/gen.awk"
|
||||
sc="sox -r 8000 -c 1 -t raw"
|
||||
$sc -w -s 16b.raw -b -U -t raw s2u
|
||||
$sc -w -s 16b.raw -b -A -t raw s2a
|
||||
|
||||
$sc -b -U 08b.raw -w -s -t raw u2s
|
||||
$sc -b -A 08b.raw -w -s -t raw a2s
|
||||
|
||||
$sc -b -U 08b.raw -b -A -t raw u2a
|
||||
$sc -b -A 08b.raw -b -U -t raw a2u
|
||||
|
||||
gcc -o gen "$1/gen.c"
|
||||
|
||||
for i in ?2?; do
|
||||
case "$i" in
|
||||
*2s)
|
||||
./gen w "$i" <"$i" >"$i.h"
|
||||
;;
|
||||
*)
|
||||
./gen b "$i" <"$i" >"$i.h"
|
||||
;;
|
||||
esac
|
||||
echo "#include \"tables/$i.h\""
|
||||
done >all.h
|
||||
|
||||
rm *.raw ?2? gen
|
|
@ -0,0 +1,5 @@
|
|||
core*
|
||||
*.tar.gz
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -0,0 +1,9 @@
|
|||
Makefile
|
||||
core*
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.yate
|
||||
*.orig
|
||||
*~
|
||||
.*.swp
|
|
@ -0,0 +1,50 @@
|
|||
# Makefile
|
||||
# This file holds the make rules for the Telephony Engine test cases
|
||||
|
||||
CC := g++ -Wall
|
||||
SED := sed
|
||||
DEFS :=
|
||||
INCLUDES := -I@top_srcdir@
|
||||
CFLAGS := -O0 @MODULE_CFLAGS@
|
||||
LDFLAGS:= -L.. -lyate
|
||||
MODFLAGS:= @MODULE_LDFLAGS@
|
||||
|
||||
PROGS = msgsniff.yate randcall.yate
|
||||
LIBS =
|
||||
OBJS =
|
||||
|
||||
LOCALFLAGS =
|
||||
LOCALLIBS =
|
||||
COMPILE = $(CC) $(DEFS) $(INCLUDES) $(CFLAGS)
|
||||
LINK = $(CC) $(LDFLAGS)
|
||||
MODLINK = $(CC) $(MODFLAGS) $(LDFLAGS)
|
||||
MODCOMP = $(COMPILE) $(MODFLAGS) $(LDFLAGS)
|
||||
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
|
||||
.PHONY: all
|
||||
all: $(LIBS) $(PROGS)
|
||||
|
||||
.PHONY: strip
|
||||
strip: all
|
||||
strip --strip-debug --discard-locals $(PROGS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@-rm $(PROGS) $(LIBS) $(OBJS) core 2>/dev/null
|
||||
|
||||
%.o: @srcdir@/%.cpp @top_srcdir@/telengine.h
|
||||
$(COMPILE) -c $<
|
||||
|
||||
%.o: @srcdir@/%.c
|
||||
$(COMPILE) -c $<
|
||||
|
||||
Makefile: @srcdir@/Makefile.in ../config.status
|
||||
cd .. && ./config.status
|
||||
|
||||
lib%.so: %.o
|
||||
$(LINK) -shared -o $@ $^
|
||||
|
||||
%.yate: @srcdir@/%.cpp $(INCFILES)
|
||||
$(MODCOMP) -o $@ $(LOCALFLAGS) $< $(LOCALLIBS)
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
test.c
|
||||
This file holds the entry point of the Telephony Engine
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
class RandThread : public Thread
|
||||
{
|
||||
public:
|
||||
RandThread() : Thread("RandThread") { }
|
||||
virtual void run();
|
||||
};
|
||||
|
||||
class RandPlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
RandPlugin();
|
||||
virtual void initialize();
|
||||
RandThread *m_thread;
|
||||
};
|
||||
|
||||
class TestHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
TestHandler(const char *name) : MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
void RandThread::run()
|
||||
{
|
||||
for (;;) {
|
||||
::usleep(::random() % 5000000);
|
||||
String id("random/"+String((int)::random() %1000));
|
||||
Message *m = new Message("preroute");
|
||||
m->addParam("id",id);
|
||||
m->addParam("caller",String((int)(::random() % 1000000)));
|
||||
m->addParam("called",String((int)(::random() % 1000000)));
|
||||
Engine::dispatch(m);
|
||||
*m = "route";
|
||||
bool routed = Engine::dispatch(m);
|
||||
Debug(DebugMild,"Routed %ssuccessfully in %llu usec",(routed ? "" : "un"),
|
||||
Time::now()-m->msgTime().usec());
|
||||
if (routed) {
|
||||
m->addParam("callto",m->retValue());
|
||||
m->retValue() = "";
|
||||
*m = "call";
|
||||
m->msgTime() = Time::now();
|
||||
if (Engine::dispatch(m)) {
|
||||
::usleep(::random() % 5000000);
|
||||
if ((::random() % 100) < 33) {
|
||||
*m = "answered";
|
||||
m->msgTime() = Time::now();
|
||||
m->addParam("status","answered");
|
||||
Engine::dispatch(m);
|
||||
::usleep(::random() % 10000000);
|
||||
}
|
||||
else if ((::random() % 100) < 50)
|
||||
*m = "busy";
|
||||
else
|
||||
*m = "no answer";
|
||||
}
|
||||
else {
|
||||
Debug(DebugMild,"Noone processed call to '%s'",m->getValue("callto"));
|
||||
m->addParam("status","rejected");
|
||||
}
|
||||
*m = "hangup";
|
||||
m->msgTime() = Time::now();
|
||||
Engine::dispatch(m);
|
||||
}
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
|
||||
RandPlugin::RandPlugin()
|
||||
: m_thread(0)
|
||||
{
|
||||
Output("Loaded random call generator");
|
||||
}
|
||||
|
||||
void RandPlugin::initialize()
|
||||
{
|
||||
Output("Initializing module RandPlugin");
|
||||
if (!m_thread)
|
||||
m_thread = new RandThread;
|
||||
}
|
||||
|
||||
INIT_PLUGIN(RandPlugin);
|
||||
|
||||
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
test.c
|
||||
This file holds the entry point of the Telephony Engine
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
class TestPlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
TestPlugin();
|
||||
virtual void initialize();
|
||||
};
|
||||
|
||||
TestPlugin::TestPlugin()
|
||||
{
|
||||
Output("Hello, I am module TestPlugin");
|
||||
}
|
||||
|
||||
void TestPlugin::initialize()
|
||||
{
|
||||
Output("Initializing module TestPlugin");
|
||||
// Regexp r("\\([a-z]*\\)\\(.*\\)");
|
||||
Regexp r("\\([a-z]\\+\\)\\([0-9]\\+\\)");
|
||||
String s("123abc456xyz");
|
||||
if (s.matches(r)) {
|
||||
Output("Found %d matches of '%s' in '%s'",s.matchCount(),r.c_str(),s.c_str());
|
||||
for (int i=0; i<=s.matchCount(); i++)
|
||||
Output("match[%d]='%s' pos=%d len=%d",i,s.matchString(i).c_str(),s.matchOffset(i),s.matchLength(i));
|
||||
String t("\\0-ABC-\\1-DEF-\\2-GHI-\\\\");
|
||||
Output("Replacing matches in '%s' got '%s'",t.c_str(),s.replaceMatches(t).c_str());
|
||||
}
|
||||
r = "[a-z]\\+[0-9]\\+";
|
||||
s.matches(r);
|
||||
Output("Found %d matches of '%s' in '%s'",s.matchCount(),r.c_str(),s.c_str());
|
||||
for (int i=0; i<=s.matchCount(); i++)
|
||||
Output("match[%d]='%s' pos=%d len=%d",i,s.matchString(i).c_str(),s.matchOffset(i),s.matchLength(i));
|
||||
}
|
||||
|
||||
INIT_PLUGIN(TestPlugin);
|
||||
|
||||
/*
|
||||
* vim:ts=4:et:sw=4:ht=8
|
||||
*/
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
test.c
|
||||
This file holds the entry point of the Telephony Engine
|
||||
*/
|
||||
|
||||
#include <telengine.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
static bool noisy = false;
|
||||
|
||||
class TestThread : public Thread
|
||||
{
|
||||
public:
|
||||
virtual void run();
|
||||
virtual void cleanup();
|
||||
};
|
||||
|
||||
class TestPlugin1 : public Plugin
|
||||
{
|
||||
public:
|
||||
TestPlugin1();
|
||||
~TestPlugin1();
|
||||
virtual void initialize();
|
||||
private:
|
||||
bool m_first;
|
||||
};
|
||||
|
||||
class TestHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
TestHandler(const char *name) : MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
void TestThread::run()
|
||||
{
|
||||
Debug(DebugInfo,"TestThread::run() [%p]",this);
|
||||
for (;;) {
|
||||
Engine::dispatch(Message("test.thread.direct"));
|
||||
Engine::enqueue(new Message("test.thread.queued"));
|
||||
::sleep(2);
|
||||
}
|
||||
}
|
||||
|
||||
void TestThread::cleanup()
|
||||
{
|
||||
Debug(DebugInfo,"TestThread::cleanup() [%p]",this);
|
||||
Debug(DebugInfo,"Thread::current() = %p",Thread::current());
|
||||
}
|
||||
|
||||
bool TestHandler::received(Message &msg)
|
||||
{
|
||||
if (noisy)
|
||||
Output("Received message '%s' time=%llu thread=%p",
|
||||
msg.c_str(),msg.msgTime().usec(),Thread::current());
|
||||
return false;
|
||||
};
|
||||
|
||||
TestPlugin1::TestPlugin1()
|
||||
: m_first(true)
|
||||
{
|
||||
Output("Hello, I am module TestPlugin1");
|
||||
}
|
||||
|
||||
TestPlugin1::~TestPlugin1()
|
||||
{
|
||||
Message msg("test1.exit","ok");
|
||||
msg.addParam("foo","bar").addParam("x","y");
|
||||
Engine::dispatch(&msg);
|
||||
}
|
||||
|
||||
void TestPlugin1::initialize()
|
||||
{
|
||||
Output("Initializing module TestPlugin1");
|
||||
Configuration *cfg = new Configuration(Engine::configFile("test1"));
|
||||
noisy = cfg->getBoolValue("general","noisy");
|
||||
int n = cfg->getIntValue("general","threads");
|
||||
delete cfg;
|
||||
Engine::install(new TestHandler("engine.halt"));
|
||||
Engine::install(new TestHandler(""));
|
||||
Engine::enqueue(new Message("test.queued1"));
|
||||
Engine::enqueue(new Message("test.queued2"));
|
||||
if (m_first) {
|
||||
m_first = false;
|
||||
for (int i=0; i<n; i++) {
|
||||
::usleep(10000);
|
||||
new TestThread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INIT_PLUGIN(TestPlugin1);
|
|
@ -0,0 +1,55 @@
|
|||
#! /bin/sh
|
||||
|
||||
ustr='Usage: yate-config [--cflags] [--includes] [--c-all]
|
||||
[--ldflags] [--libs] [--ld-all]
|
||||
[--config] [--modules] [--version]'
|
||||
if [ "$#" = 0 ]; then
|
||||
echo "$ustr"
|
||||
exit 0
|
||||
fi
|
||||
prefix="@prefix@"
|
||||
exec_prefix="@exec_prefix@"
|
||||
moddir="@libdir@/yate"
|
||||
confdir="@sysconfdir@/yate"
|
||||
s1="@MODULE_CFLAGS@"
|
||||
s2="-I@includedir@/yate"
|
||||
s3="@MODULE_LDFLAGS@"
|
||||
s4="-lyate"
|
||||
|
||||
while [ "$#" != 0 ]; do
|
||||
case "$1" in
|
||||
--version)
|
||||
echo "@PACKAGE_VERSION@"
|
||||
;;
|
||||
--cflags)
|
||||
echo "$s1"
|
||||
;;
|
||||
--includes)
|
||||
echo "$s2"
|
||||
;;
|
||||
--c-all)
|
||||
echo "$s1 $s2"
|
||||
;;
|
||||
--ldflags)
|
||||
echo "$s3"
|
||||
;;
|
||||
--libss)
|
||||
echo "$s4"
|
||||
;;
|
||||
--ld-all)
|
||||
echo "$s3 $s4"
|
||||
;;
|
||||
--config)
|
||||
echo "$confdir"
|
||||
;;
|
||||
--modules)
|
||||
echo "$moddir"
|
||||
;;
|
||||
*)
|
||||
echo "I didn't understand: $1" >&2
|
||||
echo "$ustr" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
|
@ -0,0 +1,81 @@
|
|||
.\"
|
||||
.\" YATE - Yet Another Telephony Engine
|
||||
.\"
|
||||
.\" 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
|
||||
.\"
|
||||
.\"
|
||||
.TH YATE 8 "March 2004" "YATE" "Telephony Engine"
|
||||
.SH NAME
|
||||
\fByate\fP \- launch the YATE telephony engine
|
||||
.SH SYNOPSIS
|
||||
.B yate
|
||||
.RI [options]
|
||||
.SH DESCRIPTION
|
||||
.B yate
|
||||
is a telephony engine that supports PBX and IVR functions trough plugins.
|
||||
.SH OPTIONS
|
||||
.SS General
|
||||
.TP
|
||||
.B \-h, \-\-help
|
||||
Display a short help message
|
||||
.TP
|
||||
.B \-v
|
||||
Verbose debugging (you can use more than once)
|
||||
.TP
|
||||
.B \-q
|
||||
Quieter debugging (you can use more than once)
|
||||
.TP
|
||||
.B \-d
|
||||
Daemonify, suppress output unless logged
|
||||
.TP
|
||||
.B \-l filename
|
||||
Log to file
|
||||
.SS Debugging (may not be compiled in)
|
||||
.TP
|
||||
.B \-D[options]
|
||||
Special debugging options
|
||||
.TP
|
||||
.B \-Dc
|
||||
Call dlclose() until it gets an error
|
||||
.TP
|
||||
.B \-Di
|
||||
Reinitialize after first initialization
|
||||
.TP
|
||||
.B \-Dx
|
||||
Exit immediately after initialization
|
||||
.TP
|
||||
.B \-Dw
|
||||
Delay for one secod the creation of the first worker thread
|
||||
.SH SIGNALS
|
||||
.TP
|
||||
\- SIGTERM and SIGINT (Ctrl\-C) will cleanly stop the engine
|
||||
.TP
|
||||
\- SIGHUP and SIGQUIT (Ctrl\-\\) will reinitialize the modules
|
||||
.SH BUGS
|
||||
Under various
|
||||
.B *BSD
|
||||
implementations the
|
||||
.I dlclose()
|
||||
function is broken and may generate a segfault on exit.
|
||||
.PP
|
||||
Some
|
||||
.B libpthread
|
||||
implementations are broken and may fail to start the threads or fail to cleanup
|
||||
when the thread terminates.
|
||||
.SH AUTHOR
|
||||
Paul Chitescu <paulc-devel@null.ro>
|
||||
.SH SEE ALSO
|
||||
.\" .BR libyate (3),
|
||||
.BR bayonne (8)
|
|
@ -0,0 +1,13 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
yate=yate
|
||||
yate-config=yate-config
|
||||
|
||||
Name: Yate
|
||||
Description: Yet Another Telephony Engine
|
||||
Version: @PACKAGE_VERSION@
|
||||
Libs: -L${libdir} -lyate @MODULE_LDFLAGS@
|
||||
Cflags: -I${includedir}/yate @MODULE_CFLAGS@
|
|
@ -0,0 +1,82 @@
|
|||
Summary: Yet Another Telephony Engine
|
||||
Name: yate
|
||||
Version: @PACKAGE_VERSION@
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Packager: Paul Chitescu <paulc-devel@null.ro>
|
||||
Source: http://yate.null.ro/%{name}-%{version}.tar.gz
|
||||
Group: Applications/Communications
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-root
|
||||
URL: http://yate.null.ro/
|
||||
|
||||
BuildRequires: gcc-c++
|
||||
Group: System Environment/Libraries
|
||||
|
||||
%define prefix /usr
|
||||
|
||||
%description
|
||||
YATE is a telephony engine designed to implement PBX and IVR solutions
|
||||
for small to large scale projects.
|
||||
|
||||
%files
|
||||
%defattr(-, root, root)
|
||||
%doc /usr/share/doc/yate-%{version}/README
|
||||
%doc /usr/share/doc/yate-%{version}/COPYING
|
||||
%doc /usr/share/doc/yate-%{version}/ChangeLog
|
||||
%config /etc/yate/*
|
||||
/usr/lib/lib*.so*
|
||||
/usr/bin/yate
|
||||
/usr/share/man/*
|
||||
/usr/lib/yate/*
|
||||
|
||||
%post
|
||||
ldconfig
|
||||
|
||||
%postun
|
||||
ldconfig
|
||||
|
||||
%package devel
|
||||
Summary: Development package for yate
|
||||
Group: Development/Libraries
|
||||
Requires: %{name} = %{version}
|
||||
|
||||
%description devel
|
||||
The yate-devel package includes the libraries and header files for YATE
|
||||
|
||||
%files devel
|
||||
%defattr(-, root, root)
|
||||
%doc /usr/share/doc/yate-%{version}/*.html
|
||||
%doc /usr/share/doc/yate-%{version}/api/*
|
||||
/usr/include/*
|
||||
/usr/bin/yate-config
|
||||
/usr/lib/pkgconfig/yate.pc
|
||||
|
||||
%prep
|
||||
%setup -q -n %{name}
|
||||
|
||||
%build
|
||||
./configure --prefix=%{prefix} --sysconfdir=/etc --mandir=%{prefix}/share/man
|
||||
make strip
|
||||
|
||||
%install
|
||||
make install DESTDIR=%{buildroot}
|
||||
|
||||
%clean
|
||||
# make clean
|
||||
rm -rf %{buildroot}
|
||||
|
||||
%changelog
|
||||
* Sat May 15 2004 Paul Chitescu <paulc-devel@null.ro>
|
||||
- Added pkgconfig support
|
||||
- Improved detection of Postgress' include file path
|
||||
- Support for detecting libraries required for SIP
|
||||
- Better detection of OpenH323 versions
|
||||
|
||||
* Sat May 15 2004 Diana Cionoiu <diana@diana.null.ro>
|
||||
- Added SIP channel and registration module
|
||||
|
||||
* Sun Apr 04 2004 Paul Chitescu <paulc-devel@null.ro>
|
||||
- Added yate-config to the devel package
|
||||
|
||||
* Mon Mar 29 2004 Paul Chitescu <paulc-devel@null.ro>
|
||||
- Created specfile
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,522 @@
|
|||
/**
|
||||
* telephony.h
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*/
|
||||
#ifndef __TELEPHONY_H
|
||||
#define __TELEPHONY_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#error C++ is required
|
||||
#endif
|
||||
|
||||
#include <telengine.h>
|
||||
|
||||
/**
|
||||
* Holds all Telephony Engine related classes.
|
||||
*/
|
||||
namespace TelEngine {
|
||||
|
||||
/**
|
||||
* The DataBlock holds a data buffer with no specific formatting.
|
||||
* @short A class that holds just a block of raw data
|
||||
*/
|
||||
class DataBlock : public GenObject
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructs an empty data block
|
||||
*/
|
||||
DataBlock();
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*/
|
||||
DataBlock(const DataBlock &value);
|
||||
|
||||
/**
|
||||
* Constructs an initialized data block
|
||||
* @param value Data to assign, may be NULL to fill with zeros
|
||||
* @param len Length of data, may be zero (then @ref value is ignored)
|
||||
* @param copyData True to make a copy of the data, false to just insert the pointer
|
||||
*/
|
||||
DataBlock(void *value, unsigned int len, bool copyData = true);
|
||||
|
||||
/**
|
||||
* Destroys the data, disposes the memory.
|
||||
*/
|
||||
virtual ~DataBlock();
|
||||
|
||||
/**
|
||||
* Get a pointer to the stored data.
|
||||
* @return A pointer to the data or NULL.
|
||||
*/
|
||||
inline void *data() const
|
||||
{ return m_data; }
|
||||
|
||||
/**
|
||||
* Checks if the block holds a NULL pointer.
|
||||
* @return True if the block holds NULL, false otherwise.
|
||||
*/
|
||||
inline bool null() const
|
||||
{ return !m_data; }
|
||||
|
||||
/**
|
||||
* Get the length of the stored data.
|
||||
* @return The length of the stored data, zero for NULL.
|
||||
*/
|
||||
inline unsigned int length() const
|
||||
{ return m_length; }
|
||||
|
||||
/**
|
||||
* Clear the data and optionally free the memory
|
||||
* @param deleteData True to free the deta block, false to just forget it
|
||||
*/
|
||||
void clear(bool deleteData = true);
|
||||
|
||||
/**
|
||||
* Assign data to the object
|
||||
* @param value Data to assign, may be NULL to fill with zeros
|
||||
* @param len Length of data, may be zero (then @ref value is ignored)
|
||||
* @param copyData True to make a copy of the data, false to just insert the pointer
|
||||
*/
|
||||
DataBlock& assign(void *value, unsigned int len, bool copyData = true);
|
||||
|
||||
/**
|
||||
* Append data to the current block
|
||||
* @param value Data to append
|
||||
*/
|
||||
void append(const DataBlock &value);
|
||||
|
||||
/**
|
||||
* Insert data before the current block
|
||||
* @param value Data to insert
|
||||
*/
|
||||
void insert(const DataBlock &value);
|
||||
|
||||
/**
|
||||
* Truncate the data block
|
||||
* @param len The maximum length to keep
|
||||
*/
|
||||
void truncate(unsigned int len);
|
||||
|
||||
/**
|
||||
* Cut off a number of bytes from the data block
|
||||
* @param len Amount to cut, positive to cut from end, negative to cut from start of block
|
||||
*/
|
||||
void cut(int len);
|
||||
|
||||
/**
|
||||
* Assignment operator.
|
||||
*/
|
||||
DataBlock& operator=(const DataBlock &value);
|
||||
|
||||
/**
|
||||
* Appending operator.
|
||||
*/
|
||||
inline DataBlock& operator+=(const DataBlock &value)
|
||||
{ append(value); return *this; }
|
||||
|
||||
/**
|
||||
* Convert data from a different format
|
||||
* @param src Source data block
|
||||
* @param sFormat Name of the source format
|
||||
* @param dFormat Name of the destination format
|
||||
* @param maxlen Maximum amount to convert, 0 to use source
|
||||
* @return True if converted successfully, false on failure
|
||||
*/
|
||||
bool convert(const DataBlock &src, const String &sFormat,
|
||||
const String &dFormat, unsigned maxlen = 0);
|
||||
|
||||
private:
|
||||
void *m_data;
|
||||
unsigned int m_length;
|
||||
};
|
||||
|
||||
/**
|
||||
* A generic data handling object
|
||||
*/
|
||||
class DataNode : public RefObject
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct a DataNode
|
||||
* @param format Name of the data format, default none
|
||||
*/
|
||||
DataNode(const char *format = 0)
|
||||
: m_format(format) { }
|
||||
|
||||
/**
|
||||
* Get the computing cost of converting the data to the format asked
|
||||
* @param format Name of the format to check for
|
||||
* @return -1 if unsupported, 0 for native format else cost in KIPS
|
||||
*/
|
||||
virtual int costFormat(const String &format)
|
||||
{ return -1; }
|
||||
|
||||
/**
|
||||
* Change the format used to transfer data
|
||||
* @param format Name of the format to set for data
|
||||
* @return True if the format changed successfully, false if not changed
|
||||
*/
|
||||
virtual bool setFormat(const String &format)
|
||||
{ return false; }
|
||||
|
||||
/**
|
||||
* Get the name of the format currently in use
|
||||
* @return Name of the data format
|
||||
*/
|
||||
inline const String &getFormat() const
|
||||
{ return m_format; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The name of the data format the node is currently using
|
||||
*/
|
||||
String m_format;
|
||||
};
|
||||
|
||||
/**
|
||||
* A data consumer
|
||||
*/
|
||||
class DataConsumer : public DataNode
|
||||
{
|
||||
friend class DataSource;
|
||||
public:
|
||||
/**
|
||||
* Consumer constructor
|
||||
* @param format Name of the data format, default "slin" (Signed Linear)
|
||||
*/
|
||||
DataConsumer(const char *format = "slin")
|
||||
: DataNode(format), m_source(0) { }
|
||||
|
||||
/**
|
||||
* Consumes the data sent to it from a source
|
||||
* @param data The raw data block to process; an empty block ends data
|
||||
*/
|
||||
virtual void Consume(const DataBlock &data) = 0;
|
||||
|
||||
/**
|
||||
* Get the data source of this object if it's connected
|
||||
* @return A pointer to the DataSource object or NULL
|
||||
*/
|
||||
DataSource *getConnSource() const
|
||||
{ return m_source; }
|
||||
|
||||
/**
|
||||
* Get the data source of a translator object
|
||||
* @return A pointer to the DataSource object or NULL
|
||||
*/
|
||||
virtual DataSource *getTransSource() const
|
||||
{ return 0; }
|
||||
|
||||
private:
|
||||
inline void setSource(DataSource *source)
|
||||
{ m_source = source; }
|
||||
DataSource *m_source;
|
||||
};
|
||||
|
||||
/**
|
||||
* A data source
|
||||
*/
|
||||
class DataSource : public DataNode
|
||||
{
|
||||
friend class DataTranslator;
|
||||
public:
|
||||
/**
|
||||
* Source constructor
|
||||
* @param format Name of the data format, default "slin" (Signed Linear)
|
||||
*/
|
||||
DataSource(const char *format = "slin")
|
||||
: DataNode(format), m_translator(0) { }
|
||||
|
||||
/**
|
||||
* Forwards the data to its consumers
|
||||
* @param data The raw data block to forward; an empty block ends data
|
||||
*/
|
||||
void Forward(const DataBlock &data);
|
||||
|
||||
/**
|
||||
* Attach a data consumer
|
||||
* @param consumer Data consumer to attach
|
||||
* @return True on success, false on failure
|
||||
*/
|
||||
bool attach(DataConsumer *consumer);
|
||||
|
||||
/**
|
||||
* Detach a data consumer
|
||||
* @param consumer Data consumer to detach
|
||||
* @return True on success, false on failure
|
||||
*/
|
||||
bool detach(DataConsumer *consumer);
|
||||
|
||||
/**
|
||||
* Detach all data consumers
|
||||
*/
|
||||
inline void clear()
|
||||
{ m_consumers.clear(); }
|
||||
|
||||
/**
|
||||
* Get the master translator object if this source is part of a translator
|
||||
* @return A pointer to the DataTranslator object or NULL
|
||||
*/
|
||||
DataTranslator *getTranslator() const
|
||||
{ return m_translator; }
|
||||
|
||||
protected:
|
||||
inline void setTranslator(DataTranslator *translator)
|
||||
{ m_translator = translator; }
|
||||
DataTranslator *m_translator;
|
||||
ObjList m_consumers;
|
||||
Mutex m_mutex;
|
||||
};
|
||||
|
||||
/**
|
||||
* A data source with a thread of its own
|
||||
*/
|
||||
class ThreadedSource : public DataSource
|
||||
{
|
||||
friend class ThreadedSourcePrivate;
|
||||
public:
|
||||
/**
|
||||
* The destructor, stops the thread
|
||||
*/
|
||||
virtual ~ThreadedSource();
|
||||
|
||||
/**
|
||||
* Start the worker thread
|
||||
*/
|
||||
void start(const char *name = "ThreadedSource");
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Threaded Source constructor
|
||||
* @param format Name of the data format, default "slin" (Signed Linear)
|
||||
*/
|
||||
ThreadedSource(const char *format = "slin")
|
||||
: DataSource(format), m_thread(0) { }
|
||||
|
||||
/**
|
||||
* The worker method. You have to reimplement it as you need
|
||||
*/
|
||||
virtual void run() = 0;
|
||||
|
||||
/**
|
||||
* The cleanup after thread method
|
||||
*/
|
||||
virtual void cleanup();
|
||||
|
||||
private:
|
||||
ThreadedSourcePrivate *m_thread;
|
||||
};
|
||||
|
||||
/**
|
||||
* The DataEndpoint holds an endpoint capable of performing unidirectional
|
||||
* or bidirectional data transfers
|
||||
* @short A data transfer endpoint capable of sending and/or receiving data
|
||||
*/
|
||||
class DataEndpoint : public RefObject
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Creates am empty data ednpoint
|
||||
*/
|
||||
inline DataEndpoint(const char *name = 0)
|
||||
: m_name(name), m_source(0), m_consumer(0), m_peer(0) { }
|
||||
|
||||
/**
|
||||
* Destroys the endpoint, source and consumer
|
||||
*/
|
||||
~DataEndpoint();
|
||||
|
||||
/**
|
||||
* Connect the source and consumer of the endpoint to a peer
|
||||
* @param peer Pointer to the peer data endpoint
|
||||
* @return True if connected, false if incompatible source/consumer
|
||||
*/
|
||||
bool connect(DataEndpoint *peer);
|
||||
|
||||
/**
|
||||
* Disconnect from the connected endpoint
|
||||
*/
|
||||
void disconnect();
|
||||
|
||||
/**
|
||||
* Set the data source of this object
|
||||
* @param source A pointer to the new source or NULL
|
||||
*/
|
||||
void setSource(DataSource *source = 0);
|
||||
|
||||
/**
|
||||
* Get the data source of this object
|
||||
* @return A pointer to the DataSource object or NULL
|
||||
*/
|
||||
DataSource *getSource() const
|
||||
{ return m_source; }
|
||||
|
||||
/**
|
||||
* Set the data consumer of this object
|
||||
* @param consumer A pointer to the new consumer or NULL
|
||||
*/
|
||||
void setConsumer(DataConsumer *consumer = 0);
|
||||
|
||||
/**
|
||||
* Get the data consumer of this object
|
||||
* @return A pointer to the DataConsumer object or NULL
|
||||
*/
|
||||
DataConsumer *getConsumer() const
|
||||
{ return m_consumer; }
|
||||
|
||||
/*
|
||||
* Get a pointer to the peer endpoint
|
||||
* @return A pointer to the peer endpoint or NULL
|
||||
*/
|
||||
inline DataEndpoint *getPeer() const
|
||||
{ return m_peer; }
|
||||
|
||||
/**
|
||||
* Get the name set in constructor
|
||||
* @return A reference to the name as hashed string
|
||||
*/
|
||||
inline const String &name() const
|
||||
{ return m_name; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Connect notification method
|
||||
*/
|
||||
virtual void connected() { }
|
||||
|
||||
/**
|
||||
* Disconnect notification method
|
||||
*/
|
||||
virtual void disconnected() { }
|
||||
|
||||
/**
|
||||
* Attempt to connect the endpoint to a peer of the same type
|
||||
* @param peer Pointer to the endpoint data driver
|
||||
* @return True if connected, false if failed native connection
|
||||
*/
|
||||
virtual bool nativeConnect(DataEndpoint *peer)
|
||||
{ return false; }
|
||||
|
||||
/*
|
||||
* Set the peer endpoint pointer
|
||||
* @param peer A pointer to the new peer or NULL
|
||||
*/
|
||||
void setPeer(DataEndpoint *peer);
|
||||
|
||||
private:
|
||||
String m_name;
|
||||
DataSource *m_source;
|
||||
DataConsumer *m_consumer;
|
||||
DataEndpoint *m_peer;
|
||||
};
|
||||
|
||||
/**
|
||||
* The DataTranslator holds a translator (codec) capable of unidirectional
|
||||
* conversion of data from one type to another
|
||||
* @short An unidirectional data translator (codec)
|
||||
*/
|
||||
class DataTranslator : public DataConsumer
|
||||
{
|
||||
friend class TranslatorFactory;
|
||||
public:
|
||||
/**
|
||||
* Construct a data translator
|
||||
* @param sFormat Name of the source format (data received from the consumer)
|
||||
* @param dFormat Name of the destination format (data supplied to the source)
|
||||
*/
|
||||
DataTranslator(const char *sFormat, const char *dFormat);
|
||||
|
||||
/**
|
||||
* Creates a data translator from an existing source
|
||||
* does not increment the source's reference counter
|
||||
* @param sFormat Name of the source format (data received from the consumer)
|
||||
* @param source Optional pointer to a DataSource object
|
||||
*/
|
||||
DataTranslator(const char *sFormat, DataSource *source = 0);
|
||||
|
||||
/**
|
||||
* Destroys the translator and its source
|
||||
*/
|
||||
~DataTranslator();
|
||||
|
||||
/**
|
||||
* Get the data source of a translator object
|
||||
* @return A pointer to the DataSource object or NULL
|
||||
*/
|
||||
virtual DataSource *getTransSource() const
|
||||
{ return m_tsource; }
|
||||
|
||||
/**
|
||||
* Creates a translator given the source and destination format names
|
||||
* @param sFormat Name of the source format (data received from the consumer)
|
||||
* @param dFormat Name of the destination format (data supplied to the source)
|
||||
* @return A pointer to a DataTranslator object or NULL if no known codec exists
|
||||
*/
|
||||
static DataTranslator *create(const String &sFormat, const String &dFormat);
|
||||
|
||||
/**
|
||||
* Attach a consumer to a source, possibly trough a chain of translators
|
||||
* @param source Source to attach the chain to
|
||||
* @param consumer Consumer where the chain ends
|
||||
* @return True if successfull, false if no translator chain could be built
|
||||
*/
|
||||
static bool attachChain(DataSource *source, DataConsumer *consumer);
|
||||
|
||||
/**
|
||||
* Detach a consumer from a source, possibly trough a chain of translators
|
||||
* @param source Source to dettach the chain from
|
||||
* @param consumer Consumer where the chain ends
|
||||
* @return True if successfull, false if source and consumers were not attached
|
||||
*/
|
||||
static bool detachChain(DataSource *source, DataConsumer *consumer);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Install a Translator Factory in the list of known codecs
|
||||
* @param factory A pointer to a TranslatorFactory instance
|
||||
*/
|
||||
static void install(TranslatorFactory *factory);
|
||||
|
||||
/**
|
||||
* Remove a Translator Factory from the list of known codecs
|
||||
* @param factory A pointer to a TranslatorFactory instance
|
||||
*/
|
||||
static void uninstall(TranslatorFactory *factory);
|
||||
|
||||
private:
|
||||
DataTranslator(); // No default constructor please
|
||||
DataSource *m_tsource;
|
||||
static Mutex s_mutex;
|
||||
static ObjList s_factories;
|
||||
};
|
||||
|
||||
/**
|
||||
* A factory for constructing data translators by format name
|
||||
* conversion of data from one type to another
|
||||
* @short An unidirectional data translator (codec)
|
||||
*/
|
||||
class TranslatorFactory : public GenObject
|
||||
{
|
||||
public:
|
||||
TranslatorFactory()
|
||||
{ DataTranslator::install(this); }
|
||||
|
||||
virtual ~TranslatorFactory()
|
||||
{ DataTranslator::uninstall(this); }
|
||||
|
||||
/**
|
||||
* Creates a translator given the source and destination format names
|
||||
* @param sFormat Name of the source format (data received from the consumer)
|
||||
* @param dFormat Name of the destination format (data supplied to the source)
|
||||
* @return A pointer to a DataTranslator object or NULL
|
||||
*/
|
||||
virtual DataTranslator *create(const String &sFormat, const String &dFormat) = 0;
|
||||
};
|
||||
|
||||
}; // namespace TelEngine
|
||||
|
||||
#endif /* __TELEPHONY_H */
|
Loading…
Reference in New Issue