only for backup, still in coding state - no compile!!!
authorSuper User <root@isdn.jolly.ten>
Sun, 6 May 2007 13:54:52 +0000 (15:54 +0200)
committerSuper User <root@isdn.jolly.ten>
Sun, 6 May 2007 13:54:52 +0000 (15:54 +0200)
438 files changed:
COPYING [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
README
action.cpp [new file with mode: 0644]
action_efi.cpp [new file with mode: 0644]
action_vbox.cpp [new file with mode: 0644]
admin.h [new file with mode: 0644]
admin_client.c [new file with mode: 0644]
admin_server.c [new file with mode: 0644]
admin_server.h [new file with mode: 0644]
alawulaw.c [new file with mode: 0644]
alawulaw.h [new file with mode: 0644]
apppbx.cpp [new file with mode: 0644]
apppbx.h [new file with mode: 0644]
call.cpp [new file with mode: 0644]
call.h [new file with mode: 0644]
callchan.cpp [new file with mode: 0644]
callchan.h [new file with mode: 0644]
callpbx.cpp [new file with mode: 0644]
callpbx.h [new file with mode: 0644]
cause.c [new file with mode: 0644]
cause.h [new file with mode: 0644]
crypt.cpp [new file with mode: 0644]
crypt.h [new file with mode: 0644]
default/directory.list [new file with mode: 0644]
default/h323_gateway.conf [new file with mode: 0644]
default/interface.conf [new file with mode: 0644]
default/options.conf [new file with mode: 0644]
default/routing.conf [new file with mode: 0644]
dss1.cpp [new file with mode: 0644]
dss1.h [new file with mode: 0644]
endpoint.cpp [new file with mode: 0644]
endpoint.h [new file with mode: 0644]
endpointapp.cpp [new file with mode: 0644]
endpointapp.h [new file with mode: 0644]
extension.c [new file with mode: 0644]
extension.h [new file with mode: 0644]
genext.c [new file with mode: 0644]
genrc.c [new file with mode: 0644]
gentones.c [new file with mode: 0644]
genwave.c [new file with mode: 0644]
h323.cpp [new file with mode: 0644]
h323.h [new file with mode: 0644]
h323_chan.cpp [new file with mode: 0644]
h323_chan.h [new file with mode: 0644]
h323_con.cpp [new file with mode: 0644]
h323_con.h [new file with mode: 0644]
h323_ep.cpp [new file with mode: 0644]
h323_ep.h [new file with mode: 0644]
h323conf.c [new file with mode: 0644]
h323conf.h [new file with mode: 0644]
ie.cpp [new file with mode: 0644]
interface.c [new file with mode: 0644]
interface.h [new file with mode: 0644]
mISDN.cpp [new file with mode: 0644]
mISDN.h [new file with mode: 0644]
mail.c [new file with mode: 0644]
main.c [new file with mode: 0644]
main.h [new file with mode: 0644]
message.c [new file with mode: 0644]
message.h [new file with mode: 0644]
message.txt [new file with mode: 0644]
options.c [new file with mode: 0644]
options.h [new file with mode: 0644]
port.cpp [new file with mode: 0644]
port.h [new file with mode: 0644]
q931.h [new file with mode: 0644]
route.c [new file with mode: 0644]
route.h [new file with mode: 0644]
save.h [new file with mode: 0644]
todo.txt [new file with mode: 0644]
tones.c [new file with mode: 0644]
tones.h [new file with mode: 0644]
tones_american/activated_loop.isdn [new file with mode: 0644]
tones_american/busy_loop.isdn [new file with mode: 0644]
tones_american/cause_01_loop.isdn [new file with mode: 0644]
tones_american/cause_02_loop.isdn [new file with mode: 0644]
tones_american/cause_03_loop.isdn [new file with mode: 0644]
tones_american/cause_05_loop.isdn [new file with mode: 0644]
tones_american/cause_06_loop.isdn [new file with mode: 0644]
tones_american/cause_12_loop.isdn [new file with mode: 0644]
tones_american/cause_13_loop.isdn [new file with mode: 0644]
tones_american/cause_14_loop.isdn [new file with mode: 0644]
tones_american/cause_15_loop.isdn [new file with mode: 0644]
tones_american/cause_16_loop.isdn [new file with mode: 0644]
tones_american/cause_1b_loop.isdn [new file with mode: 0644]
tones_american/cause_1c_loop.isdn [new file with mode: 0644]
tones_american/cause_1d_loop.isdn [new file with mode: 0644]
tones_american/cause_1f_loop.isdn [new file with mode: 0644]
tones_american/cause_22_loop.isdn [new file with mode: 0644]
tones_american/cause_26_loop.isdn [new file with mode: 0644]
tones_american/cause_29_loop.isdn [new file with mode: 0644]
tones_american/cause_2a_loop.isdn [new file with mode: 0644]
tones_american/cause_2f_loop.isdn [new file with mode: 0644]
tones_american/cause_31_loop.isdn [new file with mode: 0644]
tones_american/cause_32_loop.isdn [new file with mode: 0644]
tones_american/cause_39_loop.isdn [new file with mode: 0644]
tones_american/cause_3a_loop.isdn [new file with mode: 0644]
tones_american/cause_3f_loop.isdn [new file with mode: 0644]
tones_american/cause_41_loop.isdn [new file with mode: 0644]
tones_american/cause_42_loop.isdn [new file with mode: 0644]
tones_american/cause_45_loop.isdn [new file with mode: 0644]
tones_american/cause_46_loop.isdn [new file with mode: 0644]
tones_american/cause_55_loop.isdn [new file with mode: 0644]
tones_american/cause_80_loop.isdn [new file with mode: 0644]
tones_american/cause_81_loop.isdn [new file with mode: 0644]
tones_american/cause_82_loop.isdn [new file with mode: 0644]
tones_american/cause_83_loop.isdn [new file with mode: 0644]
tones_american/cause_84_loop.isdn [new file with mode: 0644]
tones_american/cause_85_loop.isdn [new file with mode: 0644]
tones_american/cause_86_loop.isdn [new file with mode: 0644]
tones_american/cause_87_loop.isdn [new file with mode: 0644]
tones_american/crypt_off.isdn [new file with mode: 0644]
tones_american/crypt_on.isdn [new file with mode: 0644]
tones_american/deactivated_loop.isdn [new file with mode: 0644]
tones_american/dialpbx.isdn [new file with mode: 0644]
tones_american/dialpbx_loop.isdn [new file with mode: 0644]
tones_american/dialtone_loop.isdn [new symlink]
tones_american/error_loop.isdn [new file with mode: 0644]
tones_american/hold_loop.isdn [new file with mode: 0644]
tones_american/password_loop.isdn [new file with mode: 0644]
tones_american/redial.isdn [new file with mode: 0644]
tones_american/release_loop.isdn [new symlink]
tones_american/ringing_loop.isdn [new file with mode: 0644]
tones_american/ringpbx_loop.isdn [new file with mode: 0644]
tones_american/test_loop.isdn [new file with mode: 0644]
tones_efi/die.isdn [new file with mode: 0644]
tones_efi/number_00.isdn [new file with mode: 0644]
tones_efi/number_01.isdn [new file with mode: 0644]
tones_efi/number_02.isdn [new file with mode: 0644]
tones_efi/number_03.isdn [new file with mode: 0644]
tones_efi/number_04.isdn [new file with mode: 0644]
tones_efi/number_05.isdn [new file with mode: 0644]
tones_efi/number_06.isdn [new file with mode: 0644]
tones_efi/number_07.isdn [new file with mode: 0644]
tones_efi/number_08.isdn [new file with mode: 0644]
tones_efi/number_09.isdn [new file with mode: 0644]
tones_german/activated_loop.isdn [new file with mode: 0644]
tones_german/busy_loop.isdn [new file with mode: 0644]
tones_german/cause_01_loop.isdn [new file with mode: 0644]
tones_german/cause_02_loop.isdn [new file with mode: 0644]
tones_german/cause_03_loop.isdn [new file with mode: 0644]
tones_german/cause_05_loop.isdn [new file with mode: 0644]
tones_german/cause_06_loop.isdn [new file with mode: 0644]
tones_german/cause_12_loop.isdn [new file with mode: 0644]
tones_german/cause_13_loop.isdn [new file with mode: 0644]
tones_german/cause_14_loop.isdn [new file with mode: 0644]
tones_german/cause_15_loop.isdn [new file with mode: 0644]
tones_german/cause_16_loop.isdn [new file with mode: 0644]
tones_german/cause_1b_loop.isdn [new file with mode: 0644]
tones_german/cause_1c_loop.isdn [new file with mode: 0644]
tones_german/cause_1d_loop.isdn [new file with mode: 0644]
tones_german/cause_1f_loop.isdn [new file with mode: 0644]
tones_german/cause_22_loop.isdn [new file with mode: 0644]
tones_german/cause_26_loop.isdn [new file with mode: 0644]
tones_german/cause_29_loop.isdn [new file with mode: 0644]
tones_german/cause_2a_loop.isdn [new file with mode: 0644]
tones_german/cause_2f_loop.isdn [new file with mode: 0644]
tones_german/cause_31_loop.isdn [new file with mode: 0644]
tones_german/cause_32_loop.isdn [new file with mode: 0644]
tones_german/cause_39_loop.isdn [new file with mode: 0644]
tones_german/cause_3a_loop.isdn [new file with mode: 0644]
tones_german/cause_3f_loop.isdn [new file with mode: 0644]
tones_german/cause_41_loop.isdn [new file with mode: 0644]
tones_german/cause_42_loop.isdn [new file with mode: 0644]
tones_german/cause_45_loop.isdn [new file with mode: 0644]
tones_german/cause_46_loop.isdn [new file with mode: 0644]
tones_german/cause_55_loop.isdn [new file with mode: 0644]
tones_german/cause_80_loop.isdn [new file with mode: 0644]
tones_german/cause_81_loop.isdn [new file with mode: 0644]
tones_german/cause_82_loop.isdn [new file with mode: 0644]
tones_german/cause_83_loop.isdn [new file with mode: 0644]
tones_german/cause_84_loop.isdn [new file with mode: 0644]
tones_german/cause_85_loop.isdn [new file with mode: 0644]
tones_german/cause_86_loop.isdn [new file with mode: 0644]
tones_german/cause_87_loop.isdn [new file with mode: 0644]
tones_german/crypt_off.isdn [new symlink]
tones_german/crypt_on.isdn [new symlink]
tones_german/deactivated_loop.isdn [new file with mode: 0644]
tones_german/dialpbx_loop.isdn [new file with mode: 0644]
tones_german/dialtone_loop.isdn [new file with mode: 0644]
tones_german/error_loop.isdn [new file with mode: 0644]
tones_german/hold_loop.isdn [new symlink]
tones_german/password_loop.isdn [new file with mode: 0644]
tones_german/redial.isdn [new file with mode: 0644]
tones_german/release_loop.isdn [new file with mode: 0644]
tones_german/ringing.isdn [new file with mode: 0644]
tones_german/ringing_loop.isdn [new file with mode: 0644]
tones_german/ringpbx_loop.isdn [new file with mode: 0644]
tones_german/test_loop.isdn [new file with mode: 0644]
vbox.cpp [new file with mode: 0644]
vbox.h [new file with mode: 0644]
vbox_english/call_anonymous.isdn [new file with mode: 0644]
vbox_english/call_from.isdn [new file with mode: 0644]
vbox_english/day_01.isdn [new file with mode: 0644]
vbox_english/day_02.isdn [new file with mode: 0644]
vbox_english/day_03.isdn [new file with mode: 0644]
vbox_english/day_04.isdn [new file with mode: 0644]
vbox_english/day_05.isdn [new file with mode: 0644]
vbox_english/day_06.isdn [new file with mode: 0644]
vbox_english/day_07.isdn [new file with mode: 0644]
vbox_english/day_08.isdn [new file with mode: 0644]
vbox_english/day_09.isdn [new file with mode: 0644]
vbox_english/day_10.isdn [new file with mode: 0644]
vbox_english/day_11.isdn [new file with mode: 0644]
vbox_english/day_12.isdn [new file with mode: 0644]
vbox_english/day_13.isdn [new file with mode: 0644]
vbox_english/day_14.isdn [new file with mode: 0644]
vbox_english/day_15.isdn [new file with mode: 0644]
vbox_english/day_16.isdn [new file with mode: 0644]
vbox_english/day_17.isdn [new file with mode: 0644]
vbox_english/day_18.isdn [new file with mode: 0644]
vbox_english/day_19.isdn [new file with mode: 0644]
vbox_english/day_20.isdn [new file with mode: 0644]
vbox_english/day_21.isdn [new file with mode: 0644]
vbox_english/day_22.isdn [new file with mode: 0644]
vbox_english/day_23.isdn [new file with mode: 0644]
vbox_english/day_24.isdn [new file with mode: 0644]
vbox_english/day_25.isdn [new file with mode: 0644]
vbox_english/day_26.isdn [new file with mode: 0644]
vbox_english/day_27.isdn [new file with mode: 0644]
vbox_english/day_28.isdn [new file with mode: 0644]
vbox_english/day_29.isdn [new file with mode: 0644]
vbox_english/day_30.isdn [new file with mode: 0644]
vbox_english/day_31.isdn [new file with mode: 0644]
vbox_english/delete_ask.isdn [new file with mode: 0644]
vbox_english/delete_done.isdn [new file with mode: 0644]
vbox_english/intro.isdn [new file with mode: 0644]
vbox_english/menu.isdn [new file with mode: 0644]
vbox_english/minute.isdn [new file with mode: 0644]
vbox_english/minutes.isdn [new file with mode: 0644]
vbox_english/month_01.isdn [new file with mode: 0644]
vbox_english/month_02.isdn [new file with mode: 0644]
vbox_english/month_03.isdn [new file with mode: 0644]
vbox_english/month_04.isdn [new file with mode: 0644]
vbox_english/month_05.isdn [new file with mode: 0644]
vbox_english/month_06.isdn [new file with mode: 0644]
vbox_english/month_07.isdn [new file with mode: 0644]
vbox_english/month_08.isdn [new file with mode: 0644]
vbox_english/month_09.isdn [new file with mode: 0644]
vbox_english/month_10.isdn [new file with mode: 0644]
vbox_english/month_11.isdn [new file with mode: 0644]
vbox_english/month_12.isdn [new file with mode: 0644]
vbox_english/nothing.isdn [new file with mode: 0644]
vbox_english/number_00.isdn [new file with mode: 0644]
vbox_english/number_01.isdn [new file with mode: 0644]
vbox_english/number_02.isdn [new file with mode: 0644]
vbox_english/number_03.isdn [new file with mode: 0644]
vbox_english/number_04.isdn [new file with mode: 0644]
vbox_english/number_05.isdn [new file with mode: 0644]
vbox_english/number_06.isdn [new file with mode: 0644]
vbox_english/number_07.isdn [new file with mode: 0644]
vbox_english/number_08.isdn [new file with mode: 0644]
vbox_english/number_09.isdn [new file with mode: 0644]
vbox_english/number_10.isdn [new file with mode: 0644]
vbox_english/number_11.isdn [new file with mode: 0644]
vbox_english/number_12.isdn [new file with mode: 0644]
vbox_english/number_13.isdn [new file with mode: 0644]
vbox_english/number_14.isdn [new file with mode: 0644]
vbox_english/number_15.isdn [new file with mode: 0644]
vbox_english/number_16.isdn [new file with mode: 0644]
vbox_english/number_17.isdn [new file with mode: 0644]
vbox_english/number_18.isdn [new file with mode: 0644]
vbox_english/number_19.isdn [new file with mode: 0644]
vbox_english/number_20.isdn [new file with mode: 0644]
vbox_english/number_21.isdn [new file with mode: 0644]
vbox_english/number_22.isdn [new file with mode: 0644]
vbox_english/number_23.isdn [new file with mode: 0644]
vbox_english/number_24.isdn [new file with mode: 0644]
vbox_english/number_25.isdn [new file with mode: 0644]
vbox_english/number_26.isdn [new file with mode: 0644]
vbox_english/number_27.isdn [new file with mode: 0644]
vbox_english/number_28.isdn [new file with mode: 0644]
vbox_english/number_29.isdn [new file with mode: 0644]
vbox_english/number_30.isdn [new file with mode: 0644]
vbox_english/number_31.isdn [new file with mode: 0644]
vbox_english/number_32.isdn [new file with mode: 0644]
vbox_english/number_33.isdn [new file with mode: 0644]
vbox_english/number_34.isdn [new file with mode: 0644]
vbox_english/number_35.isdn [new file with mode: 0644]
vbox_english/number_36.isdn [new file with mode: 0644]
vbox_english/number_37.isdn [new file with mode: 0644]
vbox_english/number_38.isdn [new file with mode: 0644]
vbox_english/number_39.isdn [new file with mode: 0644]
vbox_english/number_40.isdn [new file with mode: 0644]
vbox_english/number_41.isdn [new file with mode: 0644]
vbox_english/number_42.isdn [new file with mode: 0644]
vbox_english/number_43.isdn [new file with mode: 0644]
vbox_english/number_44.isdn [new file with mode: 0644]
vbox_english/number_45.isdn [new file with mode: 0644]
vbox_english/number_46.isdn [new file with mode: 0644]
vbox_english/number_47.isdn [new file with mode: 0644]
vbox_english/number_48.isdn [new file with mode: 0644]
vbox_english/number_49.isdn [new file with mode: 0644]
vbox_english/number_50.isdn [new file with mode: 0644]
vbox_english/number_51.isdn [new file with mode: 0644]
vbox_english/number_52.isdn [new file with mode: 0644]
vbox_english/number_53.isdn [new file with mode: 0644]
vbox_english/number_54.isdn [new file with mode: 0644]
vbox_english/number_55.isdn [new file with mode: 0644]
vbox_english/number_56.isdn [new file with mode: 0644]
vbox_english/number_57.isdn [new file with mode: 0644]
vbox_english/number_58.isdn [new file with mode: 0644]
vbox_english/number_59.isdn [new file with mode: 0644]
vbox_english/oclock_am.isdn [new file with mode: 0644]
vbox_english/oclock_pm.isdn [new file with mode: 0644]
vbox_english/pause.isdn [new file with mode: 0644]
vbox_english/play.isdn [new file with mode: 0644]
vbox_english/record_ask.isdn [new file with mode: 0644]
vbox_english/record_play.isdn [new file with mode: 0644]
vbox_english/record_record.isdn [new file with mode: 0644]
vbox_english/store_ask.isdn [new file with mode: 0644]
vbox_english/store_done.isdn [new file with mode: 0644]
vbox_german/call_anonymous.isdn [new file with mode: 0644]
vbox_german/call_from.isdn [new file with mode: 0644]
vbox_german/day_01.isdn [new file with mode: 0644]
vbox_german/day_02.isdn [new file with mode: 0644]
vbox_german/day_03.isdn [new file with mode: 0644]
vbox_german/day_04.isdn [new file with mode: 0644]
vbox_german/day_05.isdn [new file with mode: 0644]
vbox_german/day_06.isdn [new file with mode: 0644]
vbox_german/day_07.isdn [new file with mode: 0644]
vbox_german/day_08.isdn [new file with mode: 0644]
vbox_german/day_09.isdn [new file with mode: 0644]
vbox_german/day_10.isdn [new file with mode: 0644]
vbox_german/day_11.isdn [new file with mode: 0644]
vbox_german/day_12.isdn [new file with mode: 0644]
vbox_german/day_13.isdn [new file with mode: 0644]
vbox_german/day_14.isdn [new file with mode: 0644]
vbox_german/day_15.isdn [new file with mode: 0644]
vbox_german/day_16.isdn [new file with mode: 0644]
vbox_german/day_17.isdn [new file with mode: 0644]
vbox_german/day_18.isdn [new file with mode: 0644]
vbox_german/day_19.isdn [new file with mode: 0644]
vbox_german/day_20.isdn [new file with mode: 0644]
vbox_german/day_21.isdn [new file with mode: 0644]
vbox_german/day_22.isdn [new file with mode: 0644]
vbox_german/day_23.isdn [new file with mode: 0644]
vbox_german/day_24.isdn [new file with mode: 0644]
vbox_german/day_25.isdn [new file with mode: 0644]
vbox_german/day_26.isdn [new file with mode: 0644]
vbox_german/day_27.isdn [new file with mode: 0644]
vbox_german/day_28.isdn [new file with mode: 0644]
vbox_german/day_29.isdn [new file with mode: 0644]
vbox_german/day_30.isdn [new file with mode: 0644]
vbox_german/day_31.isdn [new file with mode: 0644]
vbox_german/delete_ask.isdn [new file with mode: 0644]
vbox_german/delete_done.isdn [new file with mode: 0644]
vbox_german/intro.isdn [new file with mode: 0644]
vbox_german/menu.isdn [new file with mode: 0644]
vbox_german/minute.isdn [new file with mode: 0644]
vbox_german/minutes.isdn [new file with mode: 0644]
vbox_german/month_01.isdn [new file with mode: 0644]
vbox_german/month_02.isdn [new file with mode: 0644]
vbox_german/month_03.isdn [new file with mode: 0644]
vbox_german/month_04.isdn [new file with mode: 0644]
vbox_german/month_05.isdn [new file with mode: 0644]
vbox_german/month_06.isdn [new file with mode: 0644]
vbox_german/month_07.isdn [new file with mode: 0644]
vbox_german/month_08.isdn [new file with mode: 0644]
vbox_german/month_09.isdn [new file with mode: 0644]
vbox_german/month_10.isdn [new file with mode: 0644]
vbox_german/month_11.isdn [new file with mode: 0644]
vbox_german/month_12.isdn [new file with mode: 0644]
vbox_german/nothing.isdn [new file with mode: 0644]
vbox_german/number_00.isdn [new file with mode: 0644]
vbox_german/number_01.isdn [new file with mode: 0644]
vbox_german/number_02.isdn [new file with mode: 0644]
vbox_german/number_03.isdn [new file with mode: 0644]
vbox_german/number_04.isdn [new file with mode: 0644]
vbox_german/number_05.isdn [new file with mode: 0644]
vbox_german/number_06.isdn [new file with mode: 0644]
vbox_german/number_07.isdn [new file with mode: 0644]
vbox_german/number_08.isdn [new file with mode: 0644]
vbox_german/number_09.isdn [new file with mode: 0644]
vbox_german/number_10.isdn [new file with mode: 0644]
vbox_german/number_11.isdn [new file with mode: 0644]
vbox_german/number_12.isdn [new file with mode: 0644]
vbox_german/number_13.isdn [new file with mode: 0644]
vbox_german/number_14.isdn [new file with mode: 0644]
vbox_german/number_15.isdn [new file with mode: 0644]
vbox_german/number_16.isdn [new file with mode: 0644]
vbox_german/number_17.isdn [new file with mode: 0644]
vbox_german/number_18.isdn [new file with mode: 0644]
vbox_german/number_19.isdn [new file with mode: 0644]
vbox_german/number_20.isdn [new file with mode: 0644]
vbox_german/number_21.isdn [new file with mode: 0644]
vbox_german/number_22.isdn [new file with mode: 0644]
vbox_german/number_23.isdn [new file with mode: 0644]
vbox_german/number_24.isdn [new file with mode: 0644]
vbox_german/number_25.isdn [new file with mode: 0644]
vbox_german/number_26.isdn [new file with mode: 0644]
vbox_german/number_27.isdn [new file with mode: 0644]
vbox_german/number_28.isdn [new file with mode: 0644]
vbox_german/number_29.isdn [new file with mode: 0644]
vbox_german/number_30.isdn [new file with mode: 0644]
vbox_german/number_31.isdn [new file with mode: 0644]
vbox_german/number_32.isdn [new file with mode: 0644]
vbox_german/number_33.isdn [new file with mode: 0644]
vbox_german/number_34.isdn [new file with mode: 0644]
vbox_german/number_35.isdn [new file with mode: 0644]
vbox_german/number_36.isdn [new file with mode: 0644]
vbox_german/number_37.isdn [new file with mode: 0644]
vbox_german/number_38.isdn [new file with mode: 0644]
vbox_german/number_39.isdn [new file with mode: 0644]
vbox_german/number_40.isdn [new file with mode: 0644]
vbox_german/number_41.isdn [new file with mode: 0644]
vbox_german/number_42.isdn [new file with mode: 0644]
vbox_german/number_43.isdn [new file with mode: 0644]
vbox_german/number_44.isdn [new file with mode: 0644]
vbox_german/number_45.isdn [new file with mode: 0644]
vbox_german/number_46.isdn [new file with mode: 0644]
vbox_german/number_47.isdn [new file with mode: 0644]
vbox_german/number_48.isdn [new file with mode: 0644]
vbox_german/number_49.isdn [new file with mode: 0644]
vbox_german/number_50.isdn [new file with mode: 0644]
vbox_german/number_51.isdn [new file with mode: 0644]
vbox_german/number_52.isdn [new file with mode: 0644]
vbox_german/number_53.isdn [new file with mode: 0644]
vbox_german/number_54.isdn [new file with mode: 0644]
vbox_german/number_55.isdn [new file with mode: 0644]
vbox_german/number_56.isdn [new file with mode: 0644]
vbox_german/number_57.isdn [new file with mode: 0644]
vbox_german/number_58.isdn [new file with mode: 0644]
vbox_german/number_59.isdn [new file with mode: 0644]
vbox_german/number_ein.isdn [new file with mode: 0644]
vbox_german/number_eine.isdn [new file with mode: 0644]
vbox_german/oclock.isdn [new file with mode: 0644]
vbox_german/pause.isdn [new file with mode: 0644]
vbox_german/play.isdn [new file with mode: 0644]
vbox_german/record_ask.isdn [new file with mode: 0644]
vbox_german/record_play.isdn [new file with mode: 0644]
vbox_german/record_record.isdn [new file with mode: 0644]
vbox_german/store_ask.isdn [new file with mode: 0644]
vbox_german/store_done.isdn [new file with mode: 0644]
watch.c [new file with mode: 0644]
wizzard.c [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..048315d
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,16 @@
+Copyright  Andreas Eversberg (jolly@jolly.de)
+
+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.
+
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..0781c9f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,342 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year 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.
+
+
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..d57816a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,361 @@
+#*****************************************************************************\
+#*                                                                           **
+#* PBX4Linux                                                                 **
+#*                                                                           **
+#*---------------------------------------------------------------------------**
+#* Copyright: Andreas Eversberg                                              **
+#*                                                                           **
+#* Makefile                                                                  **
+#*                                                                           **
+#*****************************************************************************/ 
+
+WITH-PBX = 42 # MUST BE SET for now
+#WITH-H323 = 42 # comment this out, if no h323 should be compiled
+#WITH-OPAL = 42 # NOT SUPPORTED YET
+WITH-CRYPTO = 42 # comment this out, if no libcrypto should be used
+# note: check your location and the names of libraries.
+
+# select location to install
+INSTALL_BIN = /usr/local/bin
+INSTALL_DATA = /usr/local/pbx
+
+# give locations for the libraries (comment out H323_LIB and PWLIB_LIB, if they are installed on the system)
+LINUX_INCLUDE = -I/usr/src/linux/include
+H323_INCLUDE = -I/usr/local/include/openh323
+#H323_LIB = -L/usr/local/lib
+#PWLIB_INCLUDE = -I/usr/local/include/ptlib/unix
+#PWLIB_LIB = -L/usr/local/lib
+
+# give location of the mISDN libraries
+MISDNUSER_INCLUDE = -I../mISDNuser/include
+MISDNUSER_LIB = -L../mISDNuser/lib -L../mISDNuser/i4lnet
+LIBS += -lisdnnet -lmISDN -lpthread
+
+# give location of the curses or ncurses library
+CURSES = -lncurses
+
+CC = g++
+LD = $(CC)
+WIZZARD = ./wizzard
+PBX = ./pbx
+PBXADMIN = ./pbxadmin
+PBXWATCH = ./pbxwatch
+GEN = ./gentones
+GENW = ./genwave
+GENRC = ./genrc
+GENEXT = ./genextension
+CFLAGS = -Wall -g -DINSTALL_DATA=\"$(INSTALL_DATA)\"
+CFLAGS += $(LINUX_INCLUDE) $(MISDNUSER_INCLUDE)
+ifdef WITH-PBX
+CFLAGS += -DPBX
+endif
+ifdef WITH-CRYPTO
+CFLAGS += -DCRYPTO
+endif
+CFLAGS_OPAL = $(CFLAGS)
+CFLAGS_H323 = $(CFLAGS)
+LIBDIR += $(MISDNUSER_LIB)
+ifdef WITH-OPAL
+OPAL = opal.o opal_mgr.o opal_pbxep.o opal_pbxcon.o opal_pbxms.o
+CFLAGS += -DOPAL
+CFLAGS_OPAL += $(OPAL_INCLUDE) -DOPAL
+LIBDIR += $(OPAL_LIB)
+endif
+ifdef WITH-H323
+H323 = h323.o h323_ep.o h323_con.o h323_chan.o
+LIBS += -lh323_linux_x86_r -lpt_linux_x86_r -ldl
+CFLAGS += -DH323
+CFLAGS_H323 += $(H323_INCLUDE) $(PWLIB_INCLUDE) -DH323INCLUDE -DH323 -D_REENTRANT -DPBYTE_ORDER=PLITTLE_ENDIAN -DP_PTHREADS -DP_HAS_SEMAPHORES -DPHAS_TEMPLATES -DP_LINUX -DPTRACING
+LIBDIR += $(H323_LIB) $(PWLIB_LIB)
+endif
+ifdef WITH-CRYPTO
+LIBDIR += -L/usr/local/ssl/lib
+CFLAGS += -I/usr/local/ssl/include
+#LIBS += -lcrypto
+LIBS += /usr/local/ssl/lib/libcrypto.a
+endif
+
+#all:
+#      @echo Note that this version is a beta release. It is only for testing purpose.
+#      @echo Please report any bug. To compile use \"make beta\".
+#      @exit
+
+all: $(PBXADMIN) $(PBX) $(GEN) $(GENW) $(GENRC) $(GENEXT)
+       @sh -c 'grep -n strcpy *.c* ; if test $$''? = 0 ; then echo "dont use strcpy, use makro instead." ; exit -1 ; fi'
+       @sh -c 'grep -n strncpy *.c* ; if test $$''? = 0 ; then echo "dont use strncpy, use makro instead." ; exit -1 ; fi'
+       @sh -c 'grep -n strcat *.c* ; if test $$''? = 0 ; then echo "dont use strcat, use makro instead." ; exit -1 ; fi'
+       @sh -c 'grep -n strncat *.c* ; if test $$''? = 0 ; then echo "dont use strncat, use makro instead." ; exit -1 ; fi'
+       @sh -c 'grep -n sprintf *.c* ; if test $$''? = 0 ; then echo "dont use sprintf, use makro instead." ; exit -1 ; fi'
+       @sh -c 'grep -n snprintf *.c* ; if test $$''? = 0 ; then echo "dont use snprintf, use makro instead." ; exit -1 ; fi'
+       @echo "All PBX binaries done"
+       @sync
+       @exit
+
+main.o: main.c *.h Makefile
+       $(CC) -c $(CFLAGS_H323) main.c -o main.o
+
+message.o: message.c *.h Makefile
+       $(CC) -c $(CFLAGS) message.c -o message.o
+
+options.o: options.c *.h Makefile
+       $(CC) -c $(CFLAGS) options.c -o options.o
+
+interface.o: interface.c *.h Makefile
+       $(CC) -c $(CFLAGS) interface.c -o interface.o
+
+h323conf.o: h323conf.c *.h Makefile
+       $(CC) -c $(CFLAGS) h323conf.c -o h323conf.o
+
+extension.o: extension.c *.h Makefile
+       $(CC) -c $(CFLAGS) extension.c -o extension.o
+
+route.o: route.c *.h Makefile
+       $(CC) -c $(CFLAGS) route.c -o route.o
+
+port.o: port.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) port.cpp -o port.o
+
+mISDN.o: mISDN.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) mISDN.cpp -o mISDN.o
+
+dss1.o: dss1.cpp ie.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) dss1.cpp -o dss1.o
+
+opal.o: opal.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) opal.cpp -o opal.o
+
+opal_mgr.o: opal_mgr.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) opal_mgr.cpp -o opal_mgr.o
+
+opal_pbxep.o: opal_pbxep.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) opal_pbxep.cpp -o opal_pbxep.o
+
+opal_pbxcon.o: opal_pbxcon.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) opal_pbxcon.cpp -o opal_pbxcon.o
+
+opal_pbxms.o: opal_pbxms.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) opal_pbxms.cpp -o opal_pbxms.o
+
+#knock.o: knock.cpp *.h Makefile
+#      $(CC) -c $(CFLAGS) knock.cpp -o knock.o
+#
+vbox.o: vbox.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) vbox.cpp -o vbox.o
+
+mail.o: mail.c *.h Makefile
+       $(CC) -c $(CFLAGS) mail.c -o mail.o
+
+action.o: action.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) action.cpp -o action.o
+
+action_vbox.o: action_vbox.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) action_vbox.cpp -o action_vbox.o
+
+action_efi.o: action_efi.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) action_efi.cpp -o action_efi.o
+
+endpoint.o: endpoint.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) endpoint.cpp -o endpoint.o
+
+endpointapp.o: endpointapp.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) endpointapp.cpp -o endpointapp.o
+
+apppbx.o: apppbx.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) apppbx.cpp -o apppbx.o
+
+call.o: call.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) call.cpp -o call.o
+
+callpbx.o: callpbx.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) callpbx.cpp -o callpbx.o
+
+callchan.o: callchan.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) callchan.cpp -o callchan.o
+
+cause.o: cause.c *.h Makefile
+       $(CC) -c $(CFLAGS) cause.c -o cause.o
+
+alawulaw.o: alawulaw.c *.h Makefile
+       $(CC) -c $(CFLAGS) alawulaw.c -o alawulaw.o
+
+tones.o: tones.c *.h Makefile
+       $(CC) -c $(CFLAGS) tones.c -o tones.o
+
+crypt.o: crypt.cpp *.h Makefile
+       $(CC) -c $(CFLAGS) crypt.cpp -o crypt.o
+
+h323.o: h323.cpp *.h Makefile
+       $(CC) -c $(CFLAGS_H323) h323.cpp -o h323.o
+
+h323_ep.o: h323_ep.cpp *.h Makefile
+       $(CC) -c $(CFLAGS_H323) h323_ep.cpp -o h323_ep.o
+
+h323_chan.o: h323_chan.cpp *.h Makefile
+       $(CC) -c $(CFLAGS_H323) h323_chan.cpp -o h323_chan.o
+
+h323_con.o: h323_con.cpp *.h Makefile
+       $(CC) -c $(CFLAGS_H323) h323_con.cpp -o h323_con.o
+
+genext.o: genext.c *.h Makefile
+       $(CC) -c $(CFLAGS) genext.c -o genext.o
+
+#admin_client.o: admin_client.c *.h Makefile
+#      $(CC) -c $(CFLAGS) admin_client.c -o admin_client.o
+
+admin_server.o: admin_server.c *.h Makefile
+       $(CC) -c $(CFLAGS) admin_server.c -o admin_server.o
+
+
+#$(WIZZARD): wizzard.c Makefile
+#      $(CC) $(LIBDIR) $(CFLAGS) -lm wizzard.c \
+#      -o $(WIZZARD) 
+
+$(PBX):        $(H323) $(OPAL) \
+               main.o \
+       options.o \
+       interface.o \
+       h323conf.o \
+       extension.o \
+       cause.o \
+       alawulaw.o \
+       tones.o \
+       message.o \
+       route.o \
+       port.o \
+       mISDN.o \
+       dss1.o \
+       vbox.o \
+       endpoint.o \
+       endpointapp.o \
+       apppbx.o \
+       crypt.o \
+       action.o \
+       action_vbox.o \
+       action_efi.o \
+       mail.o \
+       call.o \
+       callpbx.o \
+       callchan.o \
+       admin_server.o
+       $(LD) $(LIBDIR) $(H323) $(OPAL) \
+               main.o \
+       options.o \
+       interface.o \
+       h323conf.o \
+       extension.o \
+       cause.o \
+       alawulaw.o \
+       tones.o \
+       message.o \
+       route.o \
+       port.o \
+       mISDN.o \
+       dss1.o \
+       vbox.o \
+       endpoint.o \
+       endpointapp.o \
+       apppbx.o \
+       crypt.o \
+       action.o \
+       action_vbox.o \
+       action_efi.o \
+       mail.o \
+       call.o \
+       callpbx.o \
+       callchan.o \
+       admin_server.o \
+       $(LIBS) -o $(PBX) 
+
+$(PBXADMIN): admin_client.c cause.c *.h Makefile
+       $(CC) $(LIBDIR) $(CFLAGS) $(CURSES) -lm admin_client.c cause.c \
+       -o $(PBXADMIN) 
+
+$(PBXWATCH): watch.c *.h Makefile
+       $(CC) $(LIBDIR) $(CFLAGS) -lm watch.c \
+       -o $(PBXWATCH) 
+
+$(GEN):        gentones.c *.h Makefile 
+       $(CC) $(LIBDIR) $(CFLAGS) -lm gentones.c \
+       -o $(GEN) 
+
+$(GENW):genwave.c *.h Makefile 
+       $(CC) $(LIBDIR) $(CFLAGS) -lm genwave.c \
+       -o $(GENW) 
+
+$(GENRC): genrc.c *.h Makefile
+       $(CC) $(LIBDIR) $(CFLAGS) -lm genrc.c \
+       -o $(GENRC) 
+
+$(GENEXT): options.o extension.o genext.o
+       $(CC) $(CFLAGS) options.o extension.o genext.o -o $(GENEXT) 
+
+#install:
+#      @echo Remember, this is a beta release. To overwrite your current installed
+#      @echo version, use \"make beta_install\".
+#      @exit
+
+install:
+       -killall -9 -w -q pbx # the following error must be ignored
+       cp $(PBX) $(INSTALL_BIN)
+       cp $(PBXADMIN) $(INSTALL_BIN)
+#      cp $(PBXWATCH) $(INSTALL_BIN)
+       cp $(GEN) $(INSTALL_BIN)
+       cp $(GENW) $(INSTALL_BIN)
+       cp $(GENRC) $(INSTALL_BIN)
+       cp $(GENEXT) $(INSTALL_BIN)
+       mkdir -p $(INSTALL_DATA)
+       mkdir -p $(INSTALL_DATA)/extensions
+       @if test -a $(INSTALL_DATA)/options.conf ; then \
+               echo "NOTE: options.conf already exists, not changed." ; else \
+               cp -v default/options.conf $(INSTALL_DATA) ; fi
+       @if test -a $(INSTALL_DATA)/interface.conf ; then \
+               echo "NOTE: interface.conf already exists, not changed." ; else \
+               cp -v default/interface.conf $(INSTALL_DATA) ; fi
+       @if test -a $(INSTALL_DATA)/routing.conf ; then \
+               echo "NOTE: routing.conf already exists, not changed." ; else \
+               cp -v default/routing.conf $(INSTALL_DATA) ; fi
+       @if test -a $(INSTALL_DATA)/numbering_int.conf ; then \
+               echo "NOTE: numbering_int.conf is obsolete, please use routing." ; fi
+       @if test -a $(INSTALL_DATA)/numbering_ext.conf ; then \
+               echo "NOTE: numbering_ext.conf is obsolete, please use routing." ; fi
+       @if test -a $(INSTALL_DATA)/h323_gateway.conf ; then \
+               echo "NOTE: h323_gateway.conf already exists, not changed." ; else \
+               cp -v default/h323_gateway.conf $(INSTALL_DATA) ; fi
+       @if test -a $(INSTALL_DATA)/directory.list ; then \
+               echo "NOTE: directory.list already exists, not changed." ; else \
+               cp -v default/directory.list $(INSTALL_DATA) ; fi
+       cp -a tones_* $(INSTALL_DATA)
+       cp -a vbox_english/ $(INSTALL_DATA)
+       cp -a vbox_german/ $(INSTALL_DATA)
+       cp -a tones_efi/ $(INSTALL_DATA)
+       sync
+
+clean:
+       touch *
+       rm -f $(PBX) $(PBXADMIN) $(PBXWATCH) $(GEN) $(GENW) $(GENRC) $(GENEXT)
+       rm -f *.o
+       rm -f .*.c.sw* .*.cpp.sw* .*.h.sw*
+       rm -f bla nohup.out
+       rm -f debug*.log
+
+tar:
+       make clean
+       cd .. &&  tar -cvzf pbx4linux_`date +%Y%m%d`.tar.gz pbx4linux
+
+start: $(PBX)
+       sync
+       -killall -9 -w -q pbx # the following error must be ignored
+       $(PBX) start
+
+s: $(PBX)
+       sync
+       -killall -9 -w -q pbx # the following error must be ignored
+       $(PBX) start
+
+fork: $(PBX)
+       sync
+       -killall -9 -w -q pbx # the following error must be ignored
+       $(PBX) fork
+
+
+
diff --git a/README b/README
index e69de29..1a51fef 100644 (file)
--- a/README
+++ b/README
@@ -0,0 +1,341 @@
+
+Read the documentation at ./doc/ and visit http://isdn.jolly.de.
+
+
+Changes in Version 20021228 
+- first release
+
+Changes in Version 20030111 (buggy and unuseable)
+- support dtmf for callback and dtmf dialing mode (dial through via dtmf)
+- bug fixes
+- dialing improvement: dialing h323 now possible with port and alias
+- other stuff
+- new Makefile: make install will now install binaries and data on your system
+
+Changes in Version 20030118 (buggy and unuseable)
+- information exchange between isdn/h323-ports, endpoints and calls are messages now
+  previousely using direct calls with pointers were dangerous
+- removed bug since 200301011, which caued h323 calls to deadlock
+
+Changes in Version 20030120
+- login function
+- callback authentication
+- h323 audio bug. no h323 audio transmission sice version 20030111
+- some other bug fixes
+
+Changes in Version 20030206 (first beta release 1.0)
+- callerid (CLIP/COLP) is now processed correctly with all features
+- hold sound and active/inactive notification
+- CD notification
+- many callerid display function
+- bug fixes
+- documentation as word document (partly done)
+- Note: This week I have my vakation, so there is no response to any question in the mailing list.
+
+Changes in Version 1.0 
+- first release
+- finished the first version of the documentation
+- new style internet page with documentation in html
+- all enities (port, endpoint, call) are now c++ objects, rather than structures
+- tones may now be played and recorded using wave (8bit mono, 16bit mono, 16bit stereo) or law (alwa/ulaw mono)
+- fixed corruption in wave-file creation
+- now call forwad (cfb, cfnr) is implemented and working
+- an answering machine with playback function is implemented.
+- lots of bug fixes
+
+Changes in Version 1.1
+- option to fetch tones into memory, causing faster access, then from hard disk
+- Memory is now locked using mlockall(), to prevent paging which causes jitter and interruption.
+- Answering machine now works with keypad information.
+- callback on internal port, if hangup with call on hold.
+- A pocket calcularor with simple terms (*, /, +, -) and floating point is added.
+- If a calls had more than two endpoints, any message has been ignored.
+  This caused not to receive the retreive notification, which caused the hold
+  music to continue to play.
+- minor buf fixes
+- Disconnect/release "causes" are now processed by priority, if a multipoint call is made.
+
+Changes in Version 1.2
+- bugfix: dialing of vbox-caller now works.
+- bugfix: minor answering machine announcement bug
+- fixed compiling error: h323_con.cpp (p_type not declared)  thanx arne!
+- added include definition for kernel api in Makefile. Hope it works...
+- fix: dummyid is used for external calls when no caller id is available. the
+  dummy id is transmitted as restricted id. if the police is called, it will
+  see the dummyid rather than the pbx line id. this is used on forwarded calls
+  when the caller id is not available.
+- doc: added a simple instruction to build a cross over cable.
+
+Changes in Version 2.0pre
+- NEW ISDN DRIVER SUPPORT (Forget the HiSax, now use 'mISDN'.)
+- NEW KERNEL REALTIME MODULE (mISDN_dsp.o)
+- NEW NT-MODE LIBRARY SUPPORT (now MULTIPOINT!!!)
+- NEW TE-MODE STACK SUPPORT
+- support of call suspension and retrieval (switch between calls)
+- call waiting on internal phone (calls when no bchannel is available)
+- doc: Now headlines are moved to the next page if they are at the bottom of a
+  page.
+- vbox: minor speech syntax bugfix
+- up to 50 (compiler flag) dialed numbers can be recalled.
+- up to 50 (compiler flag) received calls are listed and can be replied.
+- Dialing informations are now queued by endpoint until port has received 
+  setup acknowledge on the outgoing connection.
+- Starting PBX without parameter gives a list of options.
+- Query option for listing available ports/cards.
+- CNIP (calling name) Some Simens switches and telephones support this.
+- Extensions no have names.
+- Timeouts can now be specified for different call states on ISDN phones.
+- Tones/Announcements can now be overridden at different call states.
+- isdn.cpp is completely reworked.
+- Tones/Announcements can be played externally, if supported by the external
+  line.
+- Commandline parameters must be given on startup of pbx.
+- query option to check out the current isdn cards and protocolls.
+- Debug flags can now be used to speciallize the debug output.
+- vbox: Recorded calls can now be sent as attached sound file via email, or
+  just a notifaction mail without sound file can be sent.
+- PBX now runs with highest prio (99) to get as much cpu as possible.
+- CPU scheduler now runs PBX4Linux as real time process.
+- Internal calls now use internal extension number as caller id.
+- Rework of hold/conference.
+- New option to allow an incoming h323-call to be connected at ringing state.
+- New codecs supported with h323 (speex and law)
+- COLP now works with h323
+- Answering machine now delays to avoid the dtmf tone when start recording
+- Answering machine now adds the beep behind the announcement file.
+- keypad facility dialing option
+- Conference now really works using call hold feature in conjunction with
+  keypad feature
+- Picking of an incoming call on isdn now really works.
+- picking of a call forwarded to vbox
+- I fixed a bug that did not queue the dialed digits correctly before getting
+  an external connecting.
+- Logfiles of calls now show the correct year.
+- Callback now rejects if no password is given or the given extension doesn't
+  exist.
+- Incoming H.323 calls may now instantly connect using a special option.
+- H323 compiles faster, because the H323 includes are only used for the H323
+  code part.
+- Using 'curses' for a state debugging. The current state of all instances
+  is displayed on the screen including calls, endpoints and ports.
+- manny, manny more things that I forgot
+
+Changes in Version 2.0
+- fixed memory leak
+- fixed COLP bug
+- finally SUSPEND/RESUME (call parking), resume is allowed from any ISDN-port
+- some CRITICAL bugfixes
+- Fixed and reworked sample counting when playing tones. Now fast wind and
+  rewind works correctly. Also 8-bit recording can be played back now.
+  I hope it works now without problems.
+- An internal phone without caller id will now be rejected rather than
+  treated as an extenal call.
+- Fixed bug in rejecting an external call.
+- Corrected handeling of 'inbandpatterns'.
+- Data calls will not enable DTMF detection.
+- Data calls will not use any audio transmission from user space.
+- Forward to VBOX only if call is an audio call.
+- Fixed library bug, that caused not to process keypad-information during
+  setup message.
+- Fixed a 'release_complete' bug.
+- Debug information now have the correct month.
+- Fixed bug of wave-playback of voicebox recoding, caused by rework of the
+  audio routines. It caused a SEGMENTATION FAULT!
+- Using threads now to send email and using libcrypto.
+- Introducing encryption of external calls using Blowfish.
+- Key exchange using RSA.
+- Fixed a bug in dialing H323-IP with numerical digits.
+- Fixed a bug that causes endpoint, which receives audio data, to crash when
+  no port is related to it.
+- Fixed a bug that did not release endpoint, when it receives a disconnect
+  if it has no port (parked).
+- Fixed a channel assignment bug when retrieving call. (second B-channel)
+- Now COLP with H.323 works. No more crash!
+- Park attribute was not set, which caused a crash.
+- Conference now works correctly with dsp-module.
+- Fixed a serious NT-mode process handling problem. (crash after some calls)
+- Added log file which is also displayed on the 'state' screen.
+- * Happy new year 2004 *
+
+Changes in Version 2.1
+- Fixed a bug that caused not to reply external calls (also VBOX).
+- 'genrc' now supports loading HFC-4S, HFC-8S and HFC-E1 drivers
+- Outgoing setup now expires after 8 seconds!
+- Fixed a bug that causes mISDNuser to crash during cleanup.
+- Fixed memory bug, thanx Paul!
+- hfc-4s/8s driver support (mISDN)
+- Improvement of isdn audio processing, hardware support.
+- Fixed diplay callerid bug "anonymousunknown anon"
+- Added more stable malloc (calloc) / free handling
+
+Changes in Version 2.2
+- PRI proof (2 MBit interface support when using HFC-E1)
+- Fixed data call bug
+- Improved display of PRI channels
+- Now VBOX playback says "no messages" if the last message has been deleted
+  and will not play the last but one, unless the "previous" button has been
+  pressed.
+
+Changes in Version 2.3
+- Fixed HFC_MULTI driver activation problem (HW_RESET was not implemented)
+- Fixed login prefix bug. Thanx Karsten V.
+- MISDN: better layer 2 check
+- Now facility informations are transfered during call to terminal
+  (finally advice of charge is displayed) MUST BE ENABLED BY SETTINGS!
+- Fixed 'reply' dialing bug, that caused a crash. Thanx Karsten V.
+- Added L1 activation for NT-Mode. Fixed problems with inactive links.
+- Fixed a bug that caused subsequent data calls after a data call. Thanx JC.
+- mISDN: layer 1 now works correct with E1 cards.
+
+Changes in Version 2.4
+- Fixed parallel ringing to multiple external numbers.
+- Fixed login again (was still buggy).
+
+Changes in Version 2.5
+- Fixed callback bug. (International numbers were not detected.)
+- Fixed typos (mostly "incomming") - thanx Lars.
+- Fixed vbox-email bug - thanx Martin. (and also the compiler error)
+- Fixed compiler bug, that caused compiling without crypto lib to fail.
+- Fixed some mISDN crash problems.
+- Now it should also compile with the original CVS tree.
+- Fixed hfc_multi unloading bug - thanx Karsten!
+- Now disabling DSP_MODULE really causes DSP to be disabled.
+- Now disabling real time scheduling really works.
+- mISDNuser (CVS) now compiles with the mISDN (CVS)
+- Adding the outdial prefix to the caller ID is now possible.
+- Fixed bug that caused echo test not to work.
+- And finally hardware echo now works on HFC 4s/8s/E1 (hfc_multi)
+  For echo dial 993 (Test + 3) for standard configuration.
+- Added new hfc_multi vendor IDs including "Beronet Cards".
+
+Changes in Version 2.6
+- Fixed hookflash bug in conjunction with prefix. Thanx Tobias!
+- Fixed cleanup bug when loading of ISDN driver failed.
+- Fixed mISDN bug that caused cards not to be found, if loaded in different
+  order as found by kernel.
+- Fixed a bug that causes a segfault when a phone disconnects while
+  parallel ringing multiple phones/ports.
+- Added capability for Point-To-Point in NT mode, including PRI.
+- Added L1 link control for NT mode.
+- Fixed bug in hfc_multi and mISDN driver that caused mISDN not to work
+  with kernel > 2.6.7.
+- Fixed a but when detecting different cards with hfc_multi.
+- Fixed timer bug that caused timers of multiple NT ports not to work
+  correctly.
+
+Changes in Version 2.7
+- Fixed lots of bugs.
+- Now receive stream from mISDN is disabled when not needed.
+- Added NT mode support for incomming "SETUP_ACKNOWLEDGE".
+
+Changes in Version 3.0
+- Advanced routing capability to replace the numbering_*.conf
+  (Don't worry, internal and external numbering is a feature of the routing
+  capability and is easy to convert.)
+- Now correct cause location is generated and handled.
+- New cause display feature. Location is displayed with the cause number.
+- Many source cleanups.
+- New interface (Unix socket) to administrate. Status informations are now
+  viewable without restarting PBX. Even may processes may view status info.
+  Starting / stopping state debugging, doesn't require to restart PBX.
+- Status information now has selectable details.
+- Better structure for debugging functions and better logging. (code)
+- Dialing may also be done via command line interface.
+- Now internel/external dialtone and ringing depends on internal/external call.
+- Now endpoints (partys) can be released via command line (admin tool).
+- Watchdog "pbxwatch" to automatically restart and even debug PBX4Linux.
+- Removed problem with uninitialized variable in ISDNPort object causing to
+  crash. It did not happen very often.(only after some hundred/thousand calls)
+- HFC-E1 cards did not correctly synchronize to external lines.
+- dsp.o now allocates only one timeslot per call, as expected.
+- mISDNuser now correctly connects PRI calls.
+- PRI improvements and bugfixes.
+- Support for conference rooms.
+- Voice box is now able to play announcement before connecting the call.
+  A special feature on the external line is required to send audio before
+  answering the call.
+- It is now possible to include seconds (time) in the connect message. This
+  might not be supported by all telephones, so it is an optional feature.
+- Moved open and close of recording audio to the "Port" class, where it
+  belongs. The mixer will be more performant this way.
+- Notify is now supported by mISDN and also correctly handled and queued
+  by PBX.
+- Fixed bug that caused not to free broadcast process IDs in certain cases.
+  This would cause calls to internal phones (from extern or intern) not to
+  work after a while.
+- Added HFC-S USB to 'genrc' tool.
+- printisdn now shows corret month.
+
+Changes in Version 3.0-fix1
+- Rule for changing the forwarding now works. Enter "pbx rules forward" for
+  description. Also the example in "defaults/route.conf" is corrected.
+- Forking now forks twice and suppresses debug output. Closing of shell is
+  possible.
+
+Changes in Version 3.0-fix2
+- Fixed memory leak bug in pbxadmin that caused to eat all memory and make it
+  stop.
+- Fixed audio handling that cause forking calls to be mute. (Parallel
+  forwarding causes calls to fork to multiple destinations.)
+
+Changes in Version 3.0-fix3
+- Added "nopassword" parameter for login action.
+- Fixed bolean condition bug.
+- pbxadmin will not exit if terminal size changes.
+
+Changes in Version 3.1
+- Internal structure changed. "Endpoints" and "applications" are now two
+  linked classes. The code is now reusable for other projects than
+  "PBX4Linux". (No added features!)
+- Some source cleanups.
+- Now keypad must be enabled for each extension if required. (settings)
+- Removed a new bug that caused remotely parked/holded calls not to be removed
+  from conference. The conference got disturbed by park/hold sound from
+  remote.
+- Removed bug that caused printing of unset pointers.
+
+Changes in Version 3.2
+- PBX now works with mqueue branch. This is the latest CVS source:
+  * HFCmulti is ported
+  * HFC-PCI is ported
+  * DSP is ported
+  * nt-mode lib (libi4l) is ported
+  * source is now SMP (multiple processors or hyperthreading) save.
+- Fixed bug that caused not to record if annoucement is missing
+- A prefix may be specified with callback for predefined dialing after
+  callback.
+- Now b-channels are displayed more compressed on admin tool.
+
+Changes in Version 3.3
+- * te-mode works
+- * te-mode layer 1 and layer 2 control works (SHORTMESSAGE)
+
+Changes in Version 3.3-fix2
+- OpenH323 midas release compiles
+- Fixed bug in MESSAGE_NOTIFY which cases display information not to dliver.
+- OpenH323 midas release works currently only with law-codecs
+- Dixed some dial string parsing for Openh323.
+
+Changes in Version 3.3-fix3
+- Rework of kernel audio briding. Much faster (less delay), dynamically
+  handles jitter. Ready for future RTP / ISDNoIP modules.
+
+Changes in Version 3.4
+- Removed DSP_MODULE switch, because it will be essential for PBX operation.
+- Fixed pbxadmin offset bug.
+- Added special feature "efi" to announce caller's ID. You call, and it tells
+  your caller ID. (if available) Sample set is not complete!
+- Now caller ID and type can be given for external call rule.
+- Now caller ID and type can be given for changing caller ID.
+- Removed a display bug in pbxadmin, that caused busy channels to be omitted.
+- Fixed layer 2 handling bug.
+- Increased performance of pbx-status screen. Many interfaces/calls caused
+  lock up of machine.
+- Timeout condition seems to work now.
+- Timeout action seems to work now.
+
+What you might expect in later versions
+  -> ISDN over IP (proprietary solution)
+  -> OPAL, with a new interface between user space audio and kernel space audio (SIP / H323)
+  -> (maybe later) Kernel space RTP that makes VoIP reeeeeeeeeeeeally fast!
+
diff --git a/action.cpp b/action.cpp
new file mode 100644 (file)
index 0000000..51aa8db
--- /dev/null
@@ -0,0 +1,2877 @@
+/*****************************************************************************\
+**                                                                           **
+** PBX4Linux                                                                 **
+**                                                                           **
+**---------------------------------------------------------------------------**
+** Copyright: Andreas Eversberg                                              **
+**                                                                           **
+** all actions (and hangup) are processed here                               **
+**                                                                           **
+\*****************************************************************************/ 
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <poll.h>
+#include "main.h"
+#include "linux/isdnif.h"
+
+extern char **environ;
+
+
+/* create caller id from digits by comparing with national and international
+ * prefixes.
+ */
+char *nationalize_callerinfo(char *string, int *ntype)
+{
+       if (!strncmp(options.international, string, strlen(options.international)))
+       {
+               *ntype = INFO_NTYPE_INTERNATIONAL;
+               return(string+strlen(options.international)); 
+       }
+       if (!strncmp(options.national, string, strlen(options.national)))
+       {
+               *ntype = INFO_NTYPE_NATIONAL;
+               return(string+strlen(options.national)); 
+       }
+       *ntype = INFO_NTYPE_SUBSCRIBER;
+       return(string);
+}
+
+/* create number (including access codes) from caller id
+ * prefixes.
+ */
+char *numberrize_callerinfo(char *string, int ntype)
+{
+       static char result[256];
+
+       switch(ntype)
+       {
+               case INFO_NTYPE_INTERNATIONAL:
+               UCPY(result, options.international);
+               SCAT(result, string);
+               return(result);
+               break;
+
+               case INFO_NTYPE_NATIONAL:
+               UCPY(result, options.national);
+               SCAT(result, string);
+               return(result);
+               break;
+
+               default:
+               return(string);
+       }
+}
+
+
+/*
+ * process init 'internal' / 'external' / 'h323' / 'chan' / 'vbox-record' / 'partyline'...
+ */
+void EndpointAppPBX::_action_init_call(int chan)
+{
+       class Call              *call;
+       struct port_list        *portlist = ea_endpoint->ep_portlist;
+
+       /* a created call, this should never happen */
+       if (ea_endpoint->ep_call_id)
+       {
+               if (options.deb & DEBUG_EPOINT)
+                       PERROR("EPOINT(%d): We already have a call instance, this should never happen!\n", ea_endpoint->ep_serial);
+               return;
+       }
+
+       /* create call */
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): Creating new call instance.\n", ea_endpoint->ep_serial);
+       if (chan)
+               call = new CallChan(ea_endpoint);
+       else
+               call = new CallPBX(ea_endpoint);
+       if (!call)
+       {
+               /* resource not available */
+               message_disconnect_port(portlist, CAUSE_RESSOURCEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               set_tone(portlist,"cause_22");
+               return;
+       }
+       ea_endpoint->ep_call_id = call->c_serial;
+}
+void EndpointAppPBX::action_init_call(void)
+{
+       _action_init_call(0);
+}
+void EndpointAppPBX::action_init_chan(void)
+{
+       _action_init_call(1);
+}
+
+/*
+ * process dialing 'internal'
+ */
+void EndpointAppPBX::action_dialing_internal(void)
+{
+       struct capa_info        capainfo;
+       struct caller_info      callerinfo;
+       struct redir_info       redirinfo;
+       struct dialing_info     dialinginfo;
+       struct port_list        *portlist = ea_endpoint->ep_portlist;
+       struct message          *message;
+       struct extension        ext;
+       struct route_param      *rparam;
+
+       /* send proceeding, because number is complete */
+       set_tone(portlist, "proceeding");
+       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_PROCEEDING);
+       message_put(message);
+       logmessage(message);
+       new_state(EPOINT_STATE_IN_PROCEEDING);
+
+       /* create bearer/caller/dialinginfo */
+       memcpy(&capainfo, &e_capainfo, sizeof(capainfo));
+       memcpy(&callerinfo, &e_callerinfo, sizeof(callerinfo));
+       memcpy(&redirinfo, &e_redirinfo, sizeof(redirinfo));
+       memset(&dialinginfo, 0, sizeof(dialinginfo));
+       dialinginfo.itype = INFO_ITYPE_INTERN;
+       SCPY(dialinginfo.number, e_dialinginfo.number);
+
+       /* process extension */
+       if ((rparam = routeparam(e_action, PARAM_EXTENSION)))
+               SCPY(dialinginfo.number, rparam->string_value);
+
+       /* process number type */
+       if ((rparam = routeparam(e_action, PARAM_TYPE)))
+               dialinginfo.ntype = rparam->integer_value;
+
+       /* process service */
+       if ((rparam = routeparam(e_action, PARAM_CAPA)))
+       {
+               capainfo.bearer_capa = rparam->integer_value;
+               if (capainfo.bearer_capa != INFO_BC_SPEECH
+                && capainfo.bearer_capa != INFO_BC_AUDIO)
+               {
+                       capainfo.bearer_mode = INFO_BMODE_PACKET;
+               }
+               capainfo.bearer_info1 = INFO_INFO1_NONE;
+       }
+       if ((rparam = routeparam(e_action, PARAM_BMODE)))
+       {
+               capainfo.bearer_mode = rparam->integer_value;
+       }
+       if ((rparam = routeparam(e_action, PARAM_INFO1)))
+       {
+               capainfo.bearer_info1 = rparam->integer_value;
+       }
+       if ((rparam = routeparam(e_action, PARAM_HLC)))
+       {
+               capainfo.hlc = rparam->integer_value;
+       }
+       if ((rparam = routeparam(e_action, PARAM_EXTHLC)))
+       {
+               capainfo.exthlc = rparam->integer_value;
+       }
+
+       /* process presentation */
+       if ((rparam = routeparam(e_action, PARAM_PRESENT)))
+       {
+               callerinfo.present = (rparam->integer_value)?INFO_PRESENT_ALLOWED:INFO_PRESENT_RESTRICTED;
+       }
+
+       /* check if extension exists AND only if not multiple extensions */
+       if (!read_extension(&ext, dialinginfo.number) && !strchr(dialinginfo.number,','))
+       {
+               printlog("%3d  action   INTERN dialed extension %s doesn't exist.\n", ea_endpoint->ep_serial, dialinginfo.number);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): extension %s doesn't exist\n", ea_endpoint->ep_serial, dialinginfo.number);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_UNALLOCATED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_86");
+               return;
+       }
+       /* check if internal calls are denied */
+       if (e_ext.rights < 1)
+       {
+               printlog("%3d  action   INTERN access to internal phones are denied for this caller.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): internal call from terminal %s denied.\n", ea_endpoint->ep_serial, e_terminal);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_REJECTED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_81");
+               return;
+       }
+
+       /* add or update internal call */
+       printlog("%3d  action   INTERN call to extension %s.\n", ea_endpoint->ep_serial, dialinginfo.number);
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_call_id, EPOINT_TO_CALL, MESSAGE_SETUP);
+       memcpy(&message->param.setup.dialinginfo, &dialinginfo, sizeof(struct dialing_info));
+       memcpy(&message->param.setup.redirinfo, &redirinfo, sizeof(struct redir_info));
+       memcpy(&message->param.setup.callerinfo, &callerinfo, sizeof(struct caller_info));
+       memcpy(&message->param.setup.capainfo, &capainfo, sizeof(struct capa_info));
+       message_put(message);
+}
+
+/* process dialing external
+ */
+void EndpointAppPBX::action_dialing_external(void)
+{
+       struct capa_info capainfo;
+       struct caller_info callerinfo;
+       struct redir_info redirinfo;
+       struct dialing_info dialinginfo;
+       char *p;
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct message *message;
+       struct route_param *rparam;
+
+       /* special processing of delete characters '*' and '#' */
+       if (e_ext.delete_ext)
+       {
+               /* dialing a # causes a clearing of complete number */
+               if (strchr(e_extdialing, '#'))
+               {
+                       e_extdialing[0] = '\0';
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): '#' detected: terminal '%s' selected caller id '%s' and continues dialing: '%s'\n", ea_endpoint->ep_serial, e_terminal, e_callerinfo.id, e_extdialing);
+               }
+               /* eliminate digits before '*', which is a delete digit
+                */
+               if (strchr(e_extdialing, '*'))
+               {
+                       /* remove digits */
+                       while((p=strchr(e_extdialing, '*')))
+                       {
+                               if (p > e_extdialing) /* only if there is a digit in front */
+                               {
+                                       UCPY(p-1, p);
+                                       p--;
+                               }
+                               UCPY(p, p+1); /* remove '*' */
+                       }
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s deleted digits and got new string: %s\n", ea_endpoint->ep_serial, e_terminal, e_extdialing);
+               }
+       }
+
+       /* create bearer/caller/dialinginfo */
+       memcpy(&capainfo, &e_capainfo, sizeof(capainfo));
+       memcpy(&callerinfo, &e_callerinfo, sizeof(callerinfo));
+       memcpy(&redirinfo, &e_redirinfo, sizeof(redirinfo));
+       memset(&dialinginfo, 0, sizeof(dialinginfo));
+       dialinginfo.itype = INFO_ITYPE_EXTERN;
+       dialinginfo.sending_complete = 0;
+       SCPY(dialinginfo.number, e_extdialing);
+
+       /* process prefix */
+       if ((rparam = routeparam(e_action, PARAM_PREFIX)))
+               SPRINT(dialinginfo.number, "%s%s", rparam->string_value, e_extdialing);
+
+       /* process number complete */
+       if ((rparam = routeparam(e_action, PARAM_COMPLETE)))
+               if ((rparam = routeparam(e_action, PARAM_PREFIX)))
+                       SCPY(dialinginfo.number, rparam->string_value);
+               dialinginfo.sending_complete = 1;
+
+       /* process number type */
+       if ((rparam = routeparam(e_action, PARAM_TYPE)))
+               dialinginfo.ntype = rparam->integer_value;
+
+       /* process service */
+       if ((rparam = routeparam(e_action, PARAM_CAPA)))
+       {
+               capainfo.bearer_capa = rparam->integer_value;
+               if (capainfo.bearer_capa != INFO_BC_SPEECH
+                && capainfo.bearer_capa != INFO_BC_AUDIO)
+               {
+                       capainfo.bearer_mode = INFO_BMODE_PACKET;
+               }
+               capainfo.bearer_info1 = INFO_INFO1_NONE;
+       }
+       if ((rparam = routeparam(e_action, PARAM_BMODE)))
+       {
+               capainfo.bearer_mode = rparam->integer_value;
+       }
+       if ((rparam = routeparam(e_action, PARAM_INFO1)))
+       {
+               capainfo.bearer_info1 = rparam->integer_value;
+       }
+       if ((rparam = routeparam(e_action, PARAM_HLC)))
+       {
+               capainfo.hlc = rparam->integer_value;
+       }
+       if ((rparam = routeparam(e_action, PARAM_EXTHLC)))
+       {
+               capainfo.exthlc = rparam->integer_value;
+       }
+
+
+       /* process callerid */
+       if ((rparam = routeparam(e_action, PARAM_CALLERID)))
+       {
+               SCPY(callerinfo.id, rparam->string_value);
+       }
+       if ((rparam = routeparam(e_action, PARAM_CALLERIDTYPE)))
+       {
+               callerinfo.ntype = rparam->integer_value;
+       }
+
+       /* process presentation */
+       if ((rparam = routeparam(e_action, PARAM_PRESENT)))
+       {
+               callerinfo.present = (rparam->integer_value)?INFO_PRESENT_ALLOWED:INFO_PRESENT_RESTRICTED;
+       }
+
+       /* process interfaces */
+       if ((rparam = routeparam(e_action, PARAM_INTERFACES)))
+               SCPY(dialinginfo.interfaces, rparam->string_value);
+
+       /* check if local calls are denied */
+       if (e_ext.rights < 2)
+       {
+               printlog("%3d  action   EXTERN calls are denied for this caller.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): external call from terminal denied: %s\n", ea_endpoint->ep_serial, e_terminal);
+               release(RELEASE_CALL, LOCATION_PRIVATE_LOCAL, CAUSE_REJECTED, LOCATION_PRIVATE_LOCAL, 0);
+               set_tone(portlist, "cause_82");
+               denied:
+               message_disconnect_port(portlist, CAUSE_REJECTED, LOCATION_PRIVATE_LOCAL, "");
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               return;
+       }
+
+       if (!strncmp(dialinginfo.number, options.national, strlen(options.national))
+        || dialinginfo.ntype == INFO_NTYPE_NATIONAL
+        || dialinginfo.ntype == INFO_NTYPE_INTERNATIONAL)
+       {
+               /* check if national calls are denied */
+               if (e_ext.rights < 3)
+               {
+                       printlog("%3d  action   EXTERN national calls are denied for this caller.\n", ea_endpoint->ep_serial);
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): national call from terminal %s denied.\n", ea_endpoint->ep_serial, e_terminal);
+                       release(RELEASE_CALL, LOCATION_PRIVATE_LOCAL, CAUSE_REJECTED, LOCATION_PRIVATE_LOCAL, 0);
+                       set_tone(portlist, "cause_83");
+                       goto denied;
+               }
+       }
+
+       if (!strncmp(dialinginfo.number, options.international, strlen(options.international))
+        || dialinginfo.ntype == INFO_NTYPE_INTERNATIONAL)
+       {
+               /* check if international calls are denied */
+               if (e_ext.rights < 4)
+               {
+                       printlog("%3d  action   EXTERN international calls are denied for this caller.\n", ea_endpoint->ep_serial);
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): international call from terminal %s denied.\n", ea_endpoint->ep_serial, e_terminal);
+                       release(RELEASE_CALL, LOCATION_PRIVATE_LOCAL, CAUSE_REJECTED, LOCATION_PRIVATE_LOCAL, 0);
+                       set_tone(portlist, "cause_84");
+                       goto denied;
+               }
+       }
+
+       /* add or update outgoing call */
+       printlog("%3d  action   EXTERN call to destination %s.\n", ea_endpoint->ep_serial, dialinginfo.number);
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_call_id, EPOINT_TO_CALL, MESSAGE_SETUP);
+       memcpy(&message->param.setup.dialinginfo, &dialinginfo, sizeof(struct dialing_info));
+       memcpy(&message->param.setup.redirinfo, &redirinfo, sizeof(struct redir_info));
+       memcpy(&message->param.setup.callerinfo, &callerinfo, sizeof(struct caller_info));
+       memcpy(&message->param.setup.capainfo, &capainfo, sizeof(struct capa_info));
+       message_put(message);
+}
+
+
+#ifdef H323
+/*
+ * process dialing h323
+ */
+#define set_ip_macro \
+       UNCPY(helpbuffer, address, sizeof(helpbuffer)); \
+       helpbuffer[sizeof(helpbuffer)-1] = '\0'; \
+       UNPRINT(helpbuffer + (int)(address - dial), sizeof(helpbuffer)-1 - (int)(address - dial), "%d.%d.%d.%d", ip_a, ip_b, ip_c, ip_d); \
+       ii = strlen(helpbuffer); \
+       UNCAT(helpbuffer, dial+i, sizeof(helpbuffer)-1); \
+       dial = address + ii; \
+       i = 0; \
+       UCPY(address, helpbuffer);
+#define set_port_macro \
+       UNCPY(helpbuffer, address, sizeof(helpbuffer)); \
+       helpbuffer[sizeof(helpbuffer)-1] = '\0'; \
+       UNPRINT(helpbuffer + (int)(address - dial), sizeof(helpbuffer)-1 - (int)(address - dial), "%d", port); \
+       ii = strlen(helpbuffer); \
+       UNCAT(helpbuffer, dial+i, sizeof(helpbuffer)-1); \
+       dial = address + ii; \
+       i = 0; \
+       UCPY(address, helpbuffer);
+void EndpointAppPBX::action_dialing_h323(void)
+{
+       struct message *message;
+       struct dialing_info dialinginfo;
+       int i,j, ii;
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       char *dial;
+       char address_buffer[256], *address=address_buffer;
+       char host[128] = "";
+       int ip_a=0, ip_b=0, ip_c=0, ip_d=0, port=0;
+       struct route_param *rparam;
+       char helpbuffer[128];
+
+       /* check if address parameter is given */
+       if ((rparam = routeparam(e_action, PARAM_ADDRESS)))
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): complete address is given by parameter: '%s'\n", ea_endpoint->ep_serial, rparam->string_value);
+               SCPY(address_buffer, rparam->string_value);
+               goto address_complete;
+       }
+
+       /* check for given host */
+       if ((rparam = routeparam(e_action, PARAM_HOST)))
+       if (rparam->string_value[0])
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): host is given by parameter: '%s'\n", ea_endpoint->ep_serial, rparam->string_value);
+               SCPY(host, rparam->string_value);
+               if ((rparam = routeparam(e_action, PARAM_PORT)))
+               {
+                       if (rparam->integer_value>0 && rparam->integer_value<65536)
+                       {
+                               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): port is given with the host by parameter: %d\n", ea_endpoint->ep_serial, rparam->integer_value);
+                               if (strlen(host)+7 < sizeof(host))
+                                       UPRINT(host, ":%d/", rparam->integer_value);
+                       } else
+                       {
+                               SCAT(host, "/");
+                       }
+               } else
+               {
+                       SCAT(host, "/");
+               }
+       }
+
+       /* include prefix and put 'host'(port) in front */
+       if ((rparam = routeparam(e_action, PARAM_PREFIX)))
+       {
+               SPRINT(address_buffer, "%s%s%s", host, rparam->string_value, e_extdialing);
+       } else
+       {
+               if (host[0])
+               {
+                       SPRINT(address_buffer, "%s%s", host, e_extdialing);
+                       printf("address:%s host %s extdialing %s\n",address,host, e_extdialing);
+               } else {
+                       address = e_extdialing;
+               }
+       }
+       dial = address;
+
+       /* check dialing */
+       /* check for ip-number only with numerical digits (can be dialed by any phone) */
+       if (host[0])
+       {
+               dial = address + strlen(host);
+               goto check_user; /* we have complete host (port) */
+       }
+       i = 0;
+       while(i < 12)
+       {
+               if (dial[i] == '\0')
+                       return; /* unfinished */
+               if (dial[i]<'0' || dial[i]>'9')
+                       goto check_complex;
+               i++;
+
+               if (i == 3)
+               {
+                       ip_a = (dial[0]-'0')*100 + (dial[1]-'0')*10 + (dial[2]-'0');
+                       if (ip_a > 255)
+                       {
+                               invalid:
+                               printlog("%3d  action   H323 address '%s' is invalid.\n", ea_endpoint->ep_serial, address);
+                               message_disconnect_port(portlist, CAUSE_INVALID, LOCATION_PRIVATE_LOCAL, "");
+                               new_state(EPOINT_STATE_OUT_DISCONNECT);
+                               set_tone(portlist,"cause_1c");
+                               return;
+                       }
+               }
+               if (i == 6)
+               {
+                       ip_b = (dial[3]-'0')*100 + (dial[4]-'0')*10 + (dial[5]-'0');
+                       if (ip_b > 255)
+                               goto invalid;
+               }
+               if (i == 9)
+               {
+                       ip_c = (dial[6]-'0')*100 + (dial[7]-'0')*10 + (dial[8]-'0');
+                       if (ip_c > 255)
+                               goto invalid;
+               }
+               if (i == 12)
+               {
+                       ip_d = (dial[9]-'0')*100 + (dial[10]-'0')*10 + (dial[11]-'0');
+                       if (ip_d > 255)
+                               goto invalid;
+               }
+               if (i==4 || i==7 || i==10)
+               {
+                       if (dial[i-1] > '2')
+                               goto invalid;
+               }
+               if (i==5 || i==8 || i==11)
+               {
+                       if (dial[i-2]=='2' && dial[i-1]>'5')
+                               goto invalid;
+               }
+       }
+       UPRINT(address, "%d.%d.%d.%d", ip_a, ip_b, ip_c, ip_d);
+       i = strlen(address);
+       goto address_complete;
+
+       /* there are three stages of dialing: 1. ip, 2. port, 3. user, let's find out where we at */
+       check_complex:
+       if (strchr(address, '@'))
+       {
+               dial = strchr(address, '\0');
+               goto address_complete;
+       }
+       if (strchr(address, ':'))
+       {
+               dial = strchr(address, ':') + 1;
+               goto check_port;
+       }
+       if (strchr(address, '/'))
+       {
+               dial = strchr(address, '/') + 1;
+               goto check_user;
+       }
+
+       /* get ip from ip-number */
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): checking dialed for ip: %s\n", ea_endpoint->ep_serial, dial);
+       ip_a = ip_b = ip_c = ip_d = 0;
+       i = 0;
+       j = 0;
+       while(42)
+       {
+               if (j==4)
+                       goto invalid;
+               if (ip_a > 255)
+                       goto invalid;
+               if (dial[i]>='0' && dial[i]<='9')
+                       ip_a = (ip_a*10) + dial[i]-'0';
+               else if (dial[i]=='.' || dial[i]=='*')
+               {
+                       dial[i] = '.';
+//                     if (i && dial[i-1]=='.')
+//                     {
+//                             /* add 0 if two dots */
+//                             UCPY(dial+i+1, dial+i);
+//                             dial[i]='0';
+//                             i++;
+//                     }
+                       i++;
+                       break;
+               }
+               else if (dial[i]=='#')
+               {
+                       ip_d = ip_a;
+                       ip_a = 0;
+                       set_ip_macro
+                       i++;
+                       dial+= i;
+                       goto address_complete;
+               }
+               else if (dial[i] == '\0')
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): ip so far: %d*\n", ea_endpoint->ep_serial, ip_a);
+                       return;
+               }
+               else
+                       goto invalid;
+               i++;
+               j++;
+       }
+       j = 0;
+       while(42)
+       {
+               if (j==4)
+                       goto invalid;
+               if (ip_b > 255)
+                       goto invalid;
+               if (dial[i]>='0' && dial[i]<='9')
+                       ip_b = (ip_b*10) + dial[i]-'0';
+               else if (dial[i]=='.' || dial[i]=='*')
+               {
+                       dial[i] = '.';
+                       i++;
+                       break;
+               }
+               else if (dial[i]=='#')
+               {
+                       ip_d = ip_b;
+                       ip_b = 0;
+                       set_ip_macro
+                       i++;
+                       dial+= i;
+                       goto address_complete;
+               }
+               else if (dial[i] == '\0')
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): ip so far: %d.%d*\n", ea_endpoint->ep_serial, ip_a, ip_b);
+                       return;
+               }
+               else
+                       goto invalid;
+               i++;
+               j++;
+       }
+       j = 0;
+       while(42)
+       {
+               if (j==4)
+                       goto invalid;
+               if (ip_c > 255)
+                       goto invalid;
+               if (dial[i]>='0' && dial[i]<='9')
+                       ip_c = (ip_c*10) + dial[i]-'0';
+               else if (dial[i]=='.' || dial[i]=='*')
+               {
+                       dial[i] = '.';
+                       i++;
+                       break;
+               }
+               else if (dial[i]=='#')
+               {
+                       ip_d = ip_c;
+                       ip_c = 0;
+                       set_ip_macro
+                       i++;
+                       dial+= i;
+                       goto address_complete;
+               }
+               else if (dial[i] == '\0')
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): ip so far: %d.%d.%d\n", ea_endpoint->ep_serial, ip_a, ip_b, ip_c);
+                       return;
+               }
+               else
+                       goto invalid;
+               i++;
+               j++;
+       }
+       j = 0;
+       while(42)
+       {
+               if (j==4)
+                       goto invalid;
+               if (ip_d > 255)
+                       goto invalid;
+               if (dial[i]>='0' && dial[i]<='9')
+                       ip_d = (ip_d*10) + dial[i]-'0';
+               else if (dial[i]=='*' || dial[i]==':')
+               {
+                       set_ip_macro
+                       dial[i] = ':';
+                       i++;
+                       dial+= i;
+                       goto check_port;
+               }
+               else if (dial[i]=='#')
+               {
+                       set_ip_macro
+                       i++;
+                       dial+= i;
+                       goto address_complete;
+               }
+               else if (dial[i] == '\0')
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): ip so far: %d.%d.%d.%d*\n", ea_endpoint->ep_serial, ip_a, ip_b, ip_c, ip_d);
+                       return;
+               }
+               else
+                       goto invalid;
+               i++;
+               j++;
+       }
+
+       /* get port */
+       check_port:
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): checking dialed for port: %s\n", ea_endpoint->ep_serial, dial);
+       port = 0;
+       i = 0;
+       j = 0;
+       while(42)
+       {
+               if (j==6)
+                       goto invalid;
+               if (port > 65535)
+                       goto invalid;
+               if (dial[i]>='0' && dial[i]<='9')
+                       port = (port*10) + dial[i]-'0';
+               else if (dial[i]=='*' || dial[i]=='/')
+               {
+                       if (i) /* only if there is something entered */
+                       {
+                               set_port_macro
+                               dial[i] = '/';
+                       } else
+                       {
+                               i--;
+                               UCPY(dial+i, dial+i+1);
+                               dial[i] = '/';
+                       }
+                       i++;
+                       dial+= i;
+                       goto check_user;
+               }
+               else if (dial[i]=='#')
+               {
+                       set_port_macro
+                       i++;
+                       dial+= i;
+                       goto address_complete;
+               }
+               else if (dial[i] == '\0')
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): h323 address so far: %s\n", ea_endpoint->ep_serial, address);
+                       return;
+               }
+               else
+                       goto invalid;
+               i++;
+               j++;
+       }
+
+       /* get user */
+       check_user:
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): checking dialed for user: %s\n", ea_endpoint->ep_serial, dial);
+       port = 0;
+       i = 0;
+       j = 0;
+       while(42)
+       {
+               if (dial[i]=='#')
+               {
+                       dial[i] = '\0';
+                       /* convert to @-notation */
+                       SCPY(helpbuffer, strchr(address, '/')+1);
+                       SCAT(helpbuffer, "@");
+                       *strchr(address, '/') = '\0';
+                       SCAT(helpbuffer, address);
+                       UCPY(address, helpbuffer);
+                       goto address_complete;
+               }
+               else if (dial[i] == '\0')
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): h323 address so far: %s\n", ea_endpoint->ep_serial, address);
+                       return;
+               }
+               i++;
+               j++;
+       }
+
+       address_complete:
+       /* send proceeding, because number is complete */
+       set_tone(portlist, "proceeding");
+       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_PROCEEDING);
+       message_put(message);
+       logmessage(message);
+       new_state(EPOINT_STATE_IN_PROCEEDING);
+
+       memset(&dialinginfo, 0, sizeof(dialinginfo));
+       dialinginfo.itype = INFO_ITYPE_H323;
+       dialinginfo.sending_complete = 1;
+       SPRINT(dialinginfo.number, "%s", address);
+       /* strip the # at the end */
+       if (dialinginfo.number[0])
+               if (dialinginfo.number[strlen(dialinginfo.number)-1] == '#')
+                       dialinginfo.number[strlen(dialinginfo.number)-1] = '\0';
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): complete multi-dial string \"%s\"\n", ea_endpoint->ep_serial, dialinginfo.number);
+
+       /* add or update internal call */
+       printlog("%3d  action   H323 call to address %s.\n", ea_endpoint->ep_serial, dialinginfo.number);
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_call_id, EPOINT_TO_CALL, MESSAGE_SETUP);
+       memcpy(&message->param.setup.dialinginfo, &dialinginfo, sizeof(struct dialing_info));
+       memcpy(&message->param.setup.redirinfo, &e_redirinfo, sizeof(struct redir_info));
+       memcpy(&message->param.setup.callerinfo, &e_callerinfo, sizeof(struct caller_info));
+       memcpy(&message->param.setup.capainfo, &e_capainfo, sizeof(struct capa_info));
+       message_put(message);
+}
+#else
+void EndpointAppPBX::action_dialing_h323(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+
+       printlog("%3d  action   H323 stack not implemented.\n", ea_endpoint->ep_serial);
+       message_disconnect_port(portlist, CAUSE_UNIMPLEMENTED, LOCATION_PRIVATE_LOCAL, "");
+       new_state(EPOINT_STATE_OUT_DISCONNECT);
+       set_tone(portlist,"cause_4f");
+}
+#endif
+
+void EndpointAppPBX::action_dialing_chan(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+
+       printlog("%3d  action   channel API not implemented.\n", ea_endpoint->ep_serial);
+       message_disconnect_port(portlist, CAUSE_UNIMPLEMENTED, LOCATION_PRIVATE_LOCAL, "");
+       new_state(EPOINT_STATE_OUT_DISCONNECT);
+       set_tone(portlist,"cause_4f");
+}
+
+
+/*
+ * process dialing the "am" and record
+ */
+void EndpointAppPBX::action_dialing_vbox_record(void)
+{
+       struct dialing_info dialinginfo;
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct message *message;
+       struct extension ext;
+       struct route_param *rparam;
+
+       portlist = ea_endpoint->ep_portlist;
+
+       /* check for given extension */
+       if (!(rparam = routeparam(e_action, PARAM_EXTENSION)))
+       {
+               printlog("%3d  action   VBOX-RECORD extension not given by parameter.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): cannot record, because no 'extension' parameter has been specified.\n", ea_endpoint->ep_serial);
+
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_SERVICEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               return;
+       }
+
+       /* check if extension exists */
+       if (!read_extension(&ext, rparam->string_value))
+       {
+               printlog("%3d  action   VBOX-RECORD given extension %s doesn't exist.\n", ea_endpoint->ep_serial, rparam->string_value);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): extension %s doesn't exist\n", ea_endpoint->ep_serial, rparam->string_value);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_UNALLOCATED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_86");
+               return;
+       }
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s dialing extension: %s\n", ea_endpoint->ep_serial, e_terminal, rparam->string_value);
+
+       /* check if internal calls are denied */
+       if (e_ext.rights < 1)
+       {
+               printlog("%3d  action   VBOX-RECORD calls are denied for this caller.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): internal call from terminal %s denied.\n", ea_endpoint->ep_serial, e_terminal);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_REJECTED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_81");
+               return;
+       }
+
+       set_tone(portlist, "proceeding");
+       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_PROCEEDING);
+       message_put(message);
+       logmessage(message);
+       new_state(EPOINT_STATE_IN_PROCEEDING);
+
+       memset(&dialinginfo, 0, sizeof(dialinginfo));
+       dialinginfo.itype = INFO_ITYPE_VBOX;
+       dialinginfo.sending_complete = 1;
+       SCPY(dialinginfo.number, rparam->string_value);
+
+       /* append special announcement (if given) */
+       if ((rparam = routeparam(e_action, PARAM_ANNOUNCEMENT)))
+       if (rparam->string_value[0])
+       {
+               SCAT(dialinginfo.number, ",");
+               SCAT(dialinginfo.number, rparam->string_value);
+       }
+
+       /* add or update internal call */
+       printlog("%3d  action   VBOX-RECORD call to extension %s.\n", ea_endpoint->ep_serial, dialinginfo.number);
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_call_id, EPOINT_TO_CALL, MESSAGE_SETUP);
+       memcpy(&message->param.setup.dialinginfo, &dialinginfo, sizeof(struct dialing_info));
+       memcpy(&message->param.setup.redirinfo, &e_redirinfo, sizeof(struct redir_info));
+       memcpy(&message->param.setup.callerinfo, &e_callerinfo, sizeof(struct caller_info));
+       memcpy(&message->param.setup.capainfo, &e_capainfo, sizeof(struct capa_info));
+       message_put(message);
+}
+
+
+/*
+ * process partyline
+ */
+void EndpointAppPBX::action_init_partyline(void)
+{
+       class Call *call;
+       class CallPBX *callpbx;
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct message *message;
+       struct route_param *rparam;
+       int partyline;
+       struct call_relation *relation;
+
+       portlist = ea_endpoint->ep_portlist;
+
+       /* check for given extension */
+       if (!(rparam = routeparam(e_action, PARAM_ROOM)))
+       {
+               printlog("%3d  action   PARTYLINE no 'room' parameter given at routing.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): missing parameter 'room'.\n", ea_endpoint->ep_serial);
+               noroom:
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_SERVICEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               return;
+       }
+       if (rparam->integer_value <= 0)
+       {
+               printlog("%3d  action   PARTYLINE 'room' value must be greate 0.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): invalid value for 'room'.\n", ea_endpoint->ep_serial);
+               goto noroom;
+       }
+       partyline = rparam->integer_value;
+
+       /* don't create call if partyline exists */
+       call = call_first;
+       while(call)
+       {
+               if (call->c_type == CALL_TYPE_PBX)
+               {
+                       callpbx = (class CallPBX *)call;
+                       if (callpbx->c_partyline == rparam->integer_value)
+                               break;
+               }
+               call = call->next;
+       }
+       if (!call)
+       {
+               /* create call */
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): Creating new call instance.\n", ea_endpoint->ep_serial);
+               if (!(call = new CallPBX(ea_endpoint)))
+               {
+                       nores:
+                       /* resource not available */
+                       message_disconnect_port(portlist, CAUSE_RESSOURCEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
+                       new_state(EPOINT_STATE_OUT_DISCONNECT);
+                       set_tone(portlist,"cause_22");
+                       return;
+               }
+       } else
+       {
+//NOTE: callpbx must be set here
+               /* add relation to existing call */
+               if (!(relation=callpbx->add_relation()))
+               {
+                       goto nores;
+               }
+               relation->type = RELATION_TYPE_SETUP;
+               relation->channel_state = CHANNEL_STATE_CONNECT;
+               relation->rx_state = NOTIFY_STATE_ACTIVE;
+               relation->tx_state = NOTIFY_STATE_ACTIVE;
+               relation->epoint_id = ea_endpoint->ep_serial;
+
+       }
+       ea_endpoint->ep_call_id = call->c_serial;
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s dialing room: %d\n", ea_endpoint->ep_serial, e_terminal, partyline);
+
+       set_tone(portlist, "proceeding");
+       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_PROCEEDING);
+       message_put(message);
+       logmessage(message);
+       new_state(EPOINT_STATE_IN_PROCEEDING);
+
+       /* send setup to call */
+       printlog("%3d  action   PARTYLINE call to room %d.\n", ea_endpoint->ep_serial, partyline);
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_call_id, EPOINT_TO_CALL, MESSAGE_SETUP);
+       message->param.setup.partyline = partyline;
+       memcpy(&message->param.setup.dialinginfo, &e_dialinginfo, sizeof(struct dialing_info));
+       memcpy(&message->param.setup.redirinfo, &e_redirinfo, sizeof(struct redir_info));
+       memcpy(&message->param.setup.callerinfo, &e_callerinfo, sizeof(struct caller_info));
+       memcpy(&message->param.setup.capainfo, &e_capainfo, sizeof(struct capa_info));
+       message_put(message);
+}
+
+
+/*
+ * process hangup of all calls
+ */
+void EndpointAppPBX::action_hangup_call(void)
+{
+       int i;
+
+       printlog("%3d  action   CALL to '%s' hangs up.\n", ea_endpoint->ep_serial, e_dialinginfo.number);
+       /* check */
+       if (e_terminal[0] == '\0')
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: cannot store last dialed number '%s' because caller is unknown (not internal).\n", ea_endpoint->ep_serial, e_terminal, e_dialinginfo.number);
+               return;
+       }
+       if (!(read_extension(&e_ext, e_terminal)))
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: cannot store last dialed number '%s' because cannot read settings.\n", ea_endpoint->ep_serial, e_terminal, e_dialinginfo.number);
+               return;
+       }
+       if (e_dialinginfo.number[0] == '\0')
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: cannot store last dialed number because nothing was dialed.\n", ea_endpoint->ep_serial, e_terminal);
+               return;
+       }
+       if (!strcmp(e_dialinginfo.number, e_ext.last_out[0]))
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: cannot store last dialed number '%s' because it is identical with the last one.\n", ea_endpoint->ep_serial, e_terminal, e_dialinginfo.number);
+               return;
+       }
+
+       /* insert */
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: storing last number '%s'.\n", ea_endpoint->ep_serial, e_terminal, e_dialinginfo.number);
+       i = MAX_REMEMBER-1;
+       while(i)
+       {
+               UCPY(e_ext.last_out[i], e_ext.last_out[i-1]);
+               i--;
+       }
+       SCPY(e_ext.last_out[0], e_dialinginfo.number);
+
+       /* write extension */
+       write_extension(&e_ext, e_terminal);
+}
+
+
+/*
+ * process dialing 'login'
+ */
+void EndpointAppPBX::action_dialing_login(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct message *message;
+       char *extension;
+       struct route_param *rparam;
+
+       /* extension parameter */
+       if ((rparam = routeparam(e_action, PARAM_EXTENSION)))
+       {
+               /* extension is given by parameter */
+               extension = rparam->string_value;
+               if (extension[0] == '\0')
+                       return;
+               if (!read_extension(&e_ext, extension))
+               {
+                       printlog("%3d  action   LOGIN given extension %s doesn't exist.\n", ea_endpoint->ep_serial, extension);
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): given extension %s not found.\n", ea_endpoint->ep_serial, extension);
+                       /* extension doesn't exist */
+                       new_state(EPOINT_STATE_OUT_DISCONNECT);
+                       message_disconnect_port(portlist, CAUSE_UNALLOCATED, LOCATION_PRIVATE_LOCAL, "");
+                       set_tone(portlist, "cause_86");
+                       return;
+               }
+       } else
+       {
+               /* extension must be given by dialstring */
+               extension = e_extdialing;
+               if (extension[0] == '\0')
+                       return;
+               if (!read_extension(&e_ext, extension))
+               {
+                       printlog("%3d  action   LOGIN given extension %s incomplete or not found..\n", ea_endpoint->ep_serial, extension);
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): extension %s incomplete or not found\n", ea_endpoint->ep_serial, extension);
+                       return;
+               }
+       }
+
+       /* we changed our extension */
+       SCPY(e_terminal, extension);
+       new_state(EPOINT_STATE_CONNECT);
+       e_dtmf = 1;
+       e_connectedmode = 1;
+
+       /* send connect with extension's caller id (COLP) */
+       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_CONNECT);
+       SCPY(message->param.connectinfo.id, e_ext.callerid);
+       message->param.connectinfo.ntype = e_ext.callerid_type;
+       if (e_ext.callerid_present==INFO_PRESENT_ALLOWED && e_callerinfo.present==INFO_PRESENT_RESTRICTED)
+               message->param.connectinfo.present = INFO_PRESENT_RESTRICTED;
+       else    message->param.connectinfo.present = e_ext.callerid_present;
+       /* handle restricted caller ids */
+       apply_callerid_restriction(e_ext.anon_ignore, portlist->port_type, message->param.connectinfo.id, &message->param.connectinfo.ntype, &message->param.connectinfo.present, &message->param.connectinfo.screen, message->param.connectinfo.voip, message->param.connectinfo.intern, message->param.connectinfo.name);
+       /* display callerid if desired for extension */
+       SCPY(message->param.connectinfo.display, apply_callerid_display(message->param.connectinfo.id, message->param.connectinfo.itype, message->param.connectinfo.ntype, message->param.connectinfo.present, message->param.connectinfo.screen, message->param.connectinfo.voip, message->param.connectinfo.intern, message->param.connectinfo.name));
+       message->param.connectinfo.ntype = e_ext.callerid_type;
+       message_put(message);
+       logmessage(message);
+
+       /* set our caller id */
+       SCPY(e_callerinfo.id, e_ext.callerid);
+       e_callerinfo.ntype = e_ext.callerid_type;
+       e_callerinfo.present = e_ext.callerid_present;
+
+       /* enable connectedmode */
+       e_connectedmode = 1;
+       e_dtmf = 1;
+
+       if (!(rparam = routeparam(e_action, PARAM_NOPASSWORD)))
+       {
+               /* make call state to enter password */
+               printlog("%3d  action   LOGIN to extension %s, ask for password.\n", ea_endpoint->ep_serial, e_terminal);
+               new_state(EPOINT_STATE_IN_OVERLAP);
+               e_ruleset = NULL;
+               e_rule = NULL;
+               e_action = &action_password;
+               e_match_timeout = 0;
+               e_match_to_action = NULL;
+               e_dialinginfo.number[0] = '\0';
+               e_extdialing = strchr(e_dialinginfo.number, '\0');
+
+               /* set timeout */
+               e_password_timeout = now+20;
+
+               /* do dialing */
+               process_dialing();
+       } else 
+       {
+               /* make call state  */
+               new_state(EPOINT_STATE_IN_OVERLAP);
+               e_ruleset = ruleset_main;
+               if (e_ruleset)
+                       e_rule = e_ruleset->rule_first;
+               e_action = NULL;
+               e_dialinginfo.number[0] = '\0';
+               e_extdialing = e_dialinginfo.number;
+               set_tone(portlist, "dialpbx");
+       }
+}
+
+
+/*
+ * process init 'change_callerid'
+ */
+void EndpointAppPBX::action_init_change_callerid(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+
+       if (!e_ext.change_callerid)
+       {
+               /* service not available */
+               printlog("%3d  action   CHANGE-CALLERID denied for this caller.\n", ea_endpoint->ep_serial);
+               message_disconnect_port(portlist, CAUSE_SERVICEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               set_tone(portlist,"cause_87");
+               return;
+       }
+}
+
+/* process dialing callerid
+ */
+void EndpointAppPBX::_action_callerid_calleridnext(int next)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct route_param *rparam;
+       char buffer[64], *callerid;
+
+       if ((rparam = routeparam(e_action, PARAM_CALLERID)))
+       {
+               /* the caller ID is given by parameter */
+               callerid = rparam->string_value;
+       } else
+       {
+               /* caller ID is dialed */
+               if (!strchr(e_extdialing, '#'))
+               {
+                       /* no complete ID yet */
+                       return;
+               }
+               *strchr(e_extdialing, '#') = '\0';
+               callerid = e_extdialing;
+       }
+
+       /* given callerid type */
+       if ((rparam = routeparam(e_action, PARAM_CALLERIDTYPE)))
+               switch(rparam->integer_value)
+               {
+                       case INFO_NTYPE_SUBSCRIBER:
+                       SPRINT(buffer, "s%s", callerid);
+                       callerid = buffer;
+                       break;
+                       case INFO_NTYPE_NATIONAL:
+                       SPRINT(buffer, "n%s", callerid);
+                       callerid = buffer;
+                       break;
+                       case INFO_NTYPE_INTERNATIONAL:
+                       SPRINT(buffer, "i%s", callerid);
+                       callerid = buffer;
+                       break;
+                       default:
+                       SPRINT(buffer, "%s", callerid);
+                       callerid = buffer;
+                       break;
+               }
+
+       /* caller id complete, dialing with new caller id */
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: storing callerid '%s' for all following calls.\n", ea_endpoint->ep_serial, e_terminal, callerid);
+       /* write new parameters */
+       if (read_extension(&e_ext, e_terminal))
+       {
+               if (callerid[0] == '\0')
+               {
+                       /* no caller id */
+                       (!next)?e_ext.callerid_present:e_ext.id_next_call_present = INFO_PRESENT_RESTRICTED;
+               } else
+               {
+                       /* new caller id */
+                       (!next)?e_ext.callerid_present:e_ext.id_next_call_present = INFO_PRESENT_ALLOWED;
+                       if ((rparam = routeparam(e_action, PARAM_PRESENT))) if (rparam->integer_value == 0)
+                               (!next)?e_ext.callerid_present:e_ext.id_next_call_present = INFO_PRESENT_RESTRICTED;
+                       if (e_ext.callerid_type == INFO_NTYPE_UNKNOWN) /* if callerid is unknown, the given id is not nationalized */
+                       {
+                               SCPY((!next)?e_ext.callerid:e_ext.id_next_call, callerid);
+                               (!next)?e_ext.callerid_type:e_ext.id_next_call_type = INFO_NTYPE_UNKNOWN;
+                       } else
+                       {
+                               SCPY((!next)?e_ext.callerid:e_ext.id_next_call, nationalize_callerinfo(callerid,&((!next)?e_ext.callerid_type:e_ext.id_next_call_type)));
+                       }
+                       if (!next) e_ext.id_next_call_type = -1;
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): nationalized callerid: '%s' type=%d\n", ea_endpoint->ep_serial, (!next)?e_ext.callerid:e_ext.id_next_call, (!next)?e_ext.callerid_type:e_ext.id_next_call_type);
+               }
+               write_extension(&e_ext, e_terminal);
+       }
+
+       /* function activated */
+       printlog("%3d  action   CHANGE-CALLERID caller changes caller id%s to '%s'.\n", ea_endpoint->ep_serial, next?" of next call":"", callerid);
+       message_disconnect_port(portlist, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, "");
+       new_state(EPOINT_STATE_OUT_DISCONNECT);
+       set_tone(portlist,"activated");
+}
+
+/* process dialing callerid for all call
+ */
+void EndpointAppPBX::action_dialing_callerid(void)
+{
+       _action_callerid_calleridnext(0);
+}
+
+/* process dialing callerid for next call
+ */
+void EndpointAppPBX::action_dialing_calleridnext(void)
+{
+       _action_callerid_calleridnext(1);
+}
+
+
+/*
+ * process init 'change_forward'
+ */
+void EndpointAppPBX::action_init_change_forward(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+
+       if (!e_ext.change_forward)
+       {
+               printlog("%3d  action   CHANGE-FORWARD denied for this caller.\n", ea_endpoint->ep_serial);
+               /* service not available */             
+               message_disconnect_port(portlist, CAUSE_SERVICEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               set_tone(portlist,"cause_87");
+               return;
+       }
+}
+
+/* process dialing forwarding
+ */
+void EndpointAppPBX::action_dialing_forward(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       int diversion = INFO_DIVERSION_CFU;
+       char *dest = e_extdialing;
+       struct route_param *rparam;
+
+       /* if diversion type is given */
+       if ((rparam = routeparam(e_action, PARAM_DIVERSION)))
+               diversion = rparam->integer_value;
+
+       if ((rparam = routeparam(e_action, PARAM_DEST)))
+       {
+               /* if destination is given */
+               dest = rparam->string_value;
+       } else
+       {
+               if (!strchr(e_extdialing, '#'))
+                       return;
+               *strchr(e_extdialing, '#') = '\0';
+               dest = e_extdialing;
+       }
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: storing forwarding to '%s'.\n", ea_endpoint->ep_serial, e_terminal, dest);
+       if (read_extension(&e_ext, e_terminal))
+       {
+               switch(diversion)
+               {
+                       case INFO_DIVERSION_CFU:
+                       printlog("%3d  action   CHANGE-FORWARD changing CFU (unconditional) to '%s'.\n", ea_endpoint->ep_serial, dest);
+                       SCPY(e_ext.cfu, dest);
+                       break;
+                       case INFO_DIVERSION_CFB:
+                       printlog("%3d  action   CHANGE-FORWARD changing CFB (busy) to '%s'.\n", ea_endpoint->ep_serial, dest);
+                       SCPY(e_ext.cfb, dest);
+                       break;
+                       case INFO_DIVERSION_CFNR:
+                       if ((rparam = routeparam(e_action, PARAM_DELAY)))
+                               e_ext.cfnr_delay = rparam->integer_value;
+                       printlog("%3d  action   CHANGE-FORWARD changing CFNR (no response) to '%s' with delay=%d.\n", ea_endpoint->ep_serial, dest, e_ext.cfnr_delay);
+                       SCPY(e_ext.cfnr, dest);
+                       break;
+                       case INFO_DIVERSION_CFP:
+                       printlog("%3d  action   CHANGE-FORWARD changing CFP (parallel) to '%s'.\n", ea_endpoint->ep_serial, dest);
+                       SCPY(e_ext.cfp, dest);
+                       break;
+               }
+               write_extension(&e_ext, e_terminal);
+       }
+       /* function (de)activated */
+       message_disconnect_port(portlist, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, "");
+       new_state(EPOINT_STATE_OUT_DISCONNECT);
+       if (dest[0])
+               set_tone(portlist,"activated");
+       else
+               set_tone(portlist,"deactivated");
+}
+
+
+/* process dialing redial
+*/
+void EndpointAppPBX::action_init_redial_reply(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+
+       e_select = 0;
+       if (!e_ext.last_out[0])
+       {
+               printlog("%3d  action   REDIAL/REPLY no number available to dial.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): no stored last number.\n", ea_endpoint->ep_serial);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_SERVICEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               return;
+       }
+}
+
+/* process dialing redial
+*/
+void EndpointAppPBX::_action_redial_reply(int in)
+{
+       struct message *message;
+       char *last;
+       struct route_param *rparam;
+
+       last = (in)?e_ext.last_in[0]:e_ext.last_out[0];
+
+       /* if no display is available */
+       if (!e_ext.display_menu)
+               goto nodisplay;
+       if (ea_endpoint->ep_portlist->port_type!=PORT_TYPE_DSS1_NT_IN && ea_endpoint->ep_portlist->port_type!=PORT_TYPE_DSS1_NT_OUT)
+               goto nodisplay;
+
+       /* if select is not given */
+       if (!(rparam = routeparam(e_action, PARAM_SELECT)))
+               goto nodisplay;
+
+       /* scroll menu */
+       if (e_extdialing[0]=='*' || e_extdialing[0]=='1')
+       {
+               /* find prev entry */
+               e_select--;
+               if (e_select < 0)
+                       e_select = 0;
+
+       }
+       if (e_extdialing[0]=='#' || e_extdialing[0]=='3')
+       {
+               /* find next entry */
+               e_select++;
+               if (e_select >= MAX_REMEMBER)
+                       e_select--;
+               else if (in)
+                       if (e_ext.last_in[e_select][0] == '\0')
+                               e_select--;
+               else
+                       if (e_ext.last_out[e_select][0] == '\0')
+                               e_select--;
+
+       }
+
+       last = (in)?e_ext.last_in[e_select]:e_ext.last_out[e_select];
+       if (e_extdialing[0]=='0' || e_extdialing[0]=='2')
+       {
+               nodisplay:
+               printlog("%3d  action   REDIAL/REPLY dialing '%s'.\n", ea_endpoint->ep_serial, last);
+               SCPY(e_dialinginfo.number, last);
+               e_extdialing = e_dialinginfo.number;
+               e_action = NULL;
+               process_dialing();
+               return;
+       }
+       e_extdialing[0] = '\0';
+       
+       /* send display message to port */
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY);
+       if (!strncmp(last, "extern:", 7))
+               SPRINT(message->param.notifyinfo.display, "(%d) %s ext", e_select+1, last+7);
+       else
+       if (!strncmp(last, "intern:", 7))
+               SPRINT(message->param.notifyinfo.display, "(%d) %s int", e_select+1, last+7);
+       else
+       if (!strncmp(last, "h323:", 5))
+               SPRINT(message->param.notifyinfo.display, "(%d) %s h323", e_select+1, last+5);
+       else
+       if (!strncmp(last, "chan:", 4))
+               SPRINT(message->param.notifyinfo.display, "(%d) %s chan", e_select+1, last+5);
+       else
+       if (!strncmp(last, "vbox:", 5))
+               SPRINT(message->param.notifyinfo.display, "(%d) %s vbox", e_select+1, last+5);
+       else
+               SPRINT(message->param.notifyinfo.display, "(%d) %s", e_select+1, (last[0])?last:"- empty -");
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s sending display:%s\n", ea_endpoint->ep_serial, e_terminal, message->param.notifyinfo.display);
+       message_put(message);
+       logmessage(message);
+}
+
+/* process dialing redial
+*/
+void EndpointAppPBX::action_dialing_redial(void)
+{
+       _action_redial_reply(0);
+}
+
+/* process dialing reply
+*/
+void EndpointAppPBX::action_dialing_reply(void)
+{
+       _action_redial_reply(1);
+}
+
+
+/* dialing powerdialing delay
+ */
+void EndpointAppPBX::action_dialing_powerdial(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct message *message;
+       struct route_param *rparam;
+
+       /* power dialing only possible if we have a last dialed number */
+       if (!e_ext.last_out[0])
+       {
+               printlog("%3d  action   POWERDIAL no number available to redial.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): no stored last number.\n", ea_endpoint->ep_serial);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_SERVICEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               return;
+       }
+
+       /* limit */
+       if ((rparam = routeparam(e_action, PARAM_LIMIT)))
+       {
+               e_powerlimit = rparam->integer_value;
+       } else
+       {
+               e_powerlimit = 0;
+       }
+
+       /* delay */
+       if ((rparam = routeparam(e_action, PARAM_DELAY)))
+       {
+               e_powerdelay = rparam->integer_value;
+       } else
+       {
+               /* delay incomplete */
+               if (!strchr(e_extdialing, '#'))
+                       return;
+               *strchr(e_extdialing, '#') = '\0';
+               e_powerdelay = e_extdialing[0]?atoi(e_extdialing): 0;
+       }
+
+       if (e_powerdelay < 1)
+               e_powerdelay = 0.2;
+       printlog("%3d  action   POWERDIAL to '%s' with delay=%d.\n", ea_endpoint->ep_serial, e_ext.last_out[0], (int)e_powerdelay);
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): powerdialing to '%s' (delay=%d).\n", ea_endpoint->ep_serial, e_ext.last_out[0], (int)e_powerdelay);
+
+       /* send connect to avoid overlap timeout */
+//     new_state(EPOINT_STATE_CONNECT); connect may prevent further dialing
+       if (e_terminal[0])
+               e_dtmf = 1;
+       memset(&e_connectinfo, 0, sizeof(e_connectinfo));
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_CONNECT);
+       message_put(message);
+       logmessage(message);
+
+       /* do dialing */
+       SCPY(e_dialinginfo.number, e_ext.last_out[0]);
+       e_powerdialing = -1; /* indicates the existence of powerdialing but no redial time given */
+       e_powercount = 0;
+       e_action = NULL;
+       process_dialing();
+}
+
+
+/* dialing callback
+ */
+void EndpointAppPBX::action_dialing_callback(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct route_param *rparam;
+       struct extension cbext;
+
+       portlist = ea_endpoint->ep_portlist;
+
+       /* check given extension */
+       if (!(rparam = routeparam(e_action, PARAM_EXTENSION)))
+       {
+               printlog("%3d  action   CALLBACK no extension was specified in routing.conf.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): rejecting callback, because no extension was specified in routing.conf\n", ea_endpoint->ep_serial);
+
+               disconnect:
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_SERVICEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               e_action = NULL;
+               e_cbcaller[0] = e_cbdialing[0] = '\0';
+               return;
+       }
+
+       /* if extension is given */
+       SCPY(e_cbcaller, rparam->string_value);
+       if (e_cbcaller[0] == '\0')
+       {
+               printlog("%3d  action   CALLBACK extension specified in routing.conf is an empty string.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): rejecting callback, because given extension is an empty string.\n", ea_endpoint->ep_serial);
+               goto disconnect;
+       }
+
+       /* read callback extension */
+       memset(&cbext, 0, sizeof(cbext));
+       if (!read_extension(&cbext, e_cbcaller))
+       {
+               printlog("%3d  action   CALLBACK extension '%s' specified in routing.conf doesn't exist.\n", ea_endpoint->ep_serial, e_cbcaller);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): rejecting callback, because given extension does not exist.\n", ea_endpoint->ep_serial);
+               goto disconnect;
+       }
+
+       /* if password is not given */
+       if (cbext.password[0] == '\0')
+       {
+               printlog("%3d  action   CALLBACK extension '%s' has no password specified.\n", ea_endpoint->ep_serial, e_cbcaller);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): rejecting callback, because no password is available in given extension (%s).\n", ea_endpoint->ep_serial, e_cbcaller);
+               goto disconnect;
+       }
+
+       /* callback only possible if callerid exists OR it is given */
+       if ((rparam = routeparam(e_action, PARAM_CALLTO)))
+               SCPY(e_cbto, rparam->string_value);
+       if (e_cbto[0])
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): callback to given number: '%s'\n", ea_endpoint->ep_serial, e_cbto);
+               printlog("%3d  action   CALLBACK callback to given number: '%s'\n", ea_endpoint->ep_serial, e_cbto);
+               SCPY(e_callerinfo.id, e_cbto);
+               e_callerinfo.ntype = INFO_NTYPE_UNKNOWN;
+               e_callerinfo.present = INFO_PRESENT_ALLOWED;
+       }
+       if (e_callerinfo.id[0]=='\0' || e_callerinfo.present==INFO_PRESENT_NOTAVAIL)
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): rejecting callback, because no caller id is available\n", ea_endpoint->ep_serial);
+               printlog("%3d  action   CALLBACK not possible because caller ID is not available.\n", ea_endpoint->ep_serial);
+               goto disconnect;
+       }
+       /* present id */
+       e_callerinfo.present = INFO_PRESENT_ALLOWED;
+
+}
+
+/*
+ * process hangup 'callback'
+ */
+void EndpointAppPBX::action_hangup_callback(void)
+{
+       struct route_param *rparam;
+       int delay;
+
+       /* set delay */
+       delay = 2; /* default value */
+       if ((rparam = routeparam(e_action, PARAM_DELAY)))
+       if (rparam->integer_value>0)
+               delay = rparam->integer_value;
+
+       /* dialing after callback */
+       if ((rparam = routeparam(e_action, PARAM_PREFIX)))
+               SCPY(e_cbdialing, rparam->string_value);
+       else
+               SCPY(e_cbdialing, e_extdialing);
+
+       printlog("%3d  action   CALLBACK extension=%s callerid='%s' delay='%d' dialing after callback='%s' .\n", ea_endpoint->ep_serial, e_cbcaller, e_callerinfo.id, delay, e_cbdialing);
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): caller '%s', callerid '%s', dialing '%s', delay %d\n", ea_endpoint->ep_serial, e_cbcaller, e_callerinfo.id, e_cbdialing, delay);
+
+       /* set time to callback */
+       e_callback = now_d + delay;
+}
+
+
+/*
+ * dialing action abbreviation
+ */
+void EndpointAppPBX::action_dialing_abbrev(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       char *abbrev, *phone, *name;
+       int result;
+
+       portlist = ea_endpoint->ep_portlist;
+
+       /* abbrev dialing is only possible if we have a caller defined */
+       if (!e_terminal[0])
+       {
+               printlog("%3d  action   ABBREVIATION only possible for internal callers.\n", ea_endpoint->ep_serial);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_SERVICEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               return;
+       }
+
+       /* check abbreviation */
+       abbrev = e_extdialing;
+       phone = NULL;
+       name = NULL;
+       result = parse_phonebook(e_terminal, &abbrev, &phone, &name);
+       if (result == 0)
+       {
+               printlog("%3d  action   ABBREVIATION '%s' not found.\n", ea_endpoint->ep_serial, abbrev);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_UNALLOCATED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_01");
+               return;
+       }
+       if (result == -1) /* may match if more digits are dialed */
+       {
+               return;
+       }
+
+       /* dial abbreviation */ 
+       printlog("%3d  action   ABBREVIATION mapping '%s' to '%s' (%s), dialing...\n", ea_endpoint->ep_serial, abbrev, phone, name?name:"unknown");
+       SCPY(e_dialinginfo.number, phone);
+       e_extdialing = e_dialinginfo.number;
+       e_action = NULL;
+       process_dialing();
+}
+
+
+/* process dialing 'test'
+ */
+void EndpointAppPBX::action_dialing_test(void)
+{
+       unsigned int cause;
+       char causestr[16];
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct message *message;
+       class Port *port;
+       char testcode[32] = "";
+       struct route_param *rparam;
+
+       /* given testcode */
+       if ((rparam = routeparam(e_action, PARAM_PREFIX)))
+               SCPY(testcode, rparam->string_value);
+       SCAT(testcode, e_extdialing);
+
+       switch(testcode[0])
+       {
+               case '1':
+               printlog("%3d  action   TESTMODE executing 'proceeding' test.\n", ea_endpoint->ep_serial);
+               new_state(EPOINT_STATE_IN_PROCEEDING);
+               set_tone(portlist, "proceeding");
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_PROCEEDING);
+               message_put(message);
+               logmessage(message);
+               break;
+               
+               case '2':
+               printlog("%3d  action   TESTMODE executing 'alerting' test.\n", ea_endpoint->ep_serial);
+               new_state(EPOINT_STATE_IN_ALERTING);
+               set_tone(portlist, "ringpbx");
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_ALERTING);
+               message_put(message);
+               logmessage(message);
+               break;
+               
+               case '3':
+               printlog("%3d  action   TESTMODE executing 'echo connect' test.\n", ea_endpoint->ep_serial);
+               new_state(EPOINT_STATE_CONNECT);
+               if (e_terminal[0])
+                       e_dtmf = 1;
+               set_tone(portlist, NULL);
+               memset(&e_connectinfo, 0, sizeof(e_connectinfo));
+               SCPY(e_connectinfo.id, e_callerinfo.id);
+               SCPY(e_connectinfo.intern, e_callerinfo.intern);
+               SCPY(e_connectinfo.voip, e_callerinfo.voip);
+               e_connectinfo.itype = e_callerinfo.itype;
+               e_connectinfo.ntype = e_callerinfo.ntype;
+               e_connectinfo.present = e_callerinfo.present;
+               e_connectinfo.screen = e_callerinfo.screen;
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_CONNECT);
+               memcpy(&message->param.connectinfo, &e_connectinfo, sizeof(struct connect_info));
+               /* handle restricted caller ids */
+               apply_callerid_restriction(e_ext.anon_ignore, portlist->port_type, message->param.connectinfo.id, &message->param.connectinfo.ntype, &message->param.connectinfo.present, &message->param.connectinfo.screen, message->param.connectinfo.voip, message->param.connectinfo.intern, message->param.connectinfo.name);
+               /* display callerid if desired for extension */
+               SCPY(message->param.connectinfo.display, apply_callerid_display(message->param.connectinfo.id, message->param.connectinfo.itype, message->param.connectinfo.ntype, message->param.connectinfo.present, message->param.connectinfo.screen, message->param.connectinfo.voip, message->param.connectinfo.intern, message->param.connectinfo.name));
+               message_put(message);
+               logmessage(message);
+
+               port = find_port_id(portlist->port_id);
+               if (port)
+               {
+                       port->set_echotest(1);
+               }
+               break;
+               
+               case '4':
+               printlog("%3d  action   TESTMODE executing 'tone connect' test.\n", ea_endpoint->ep_serial);
+               new_state(EPOINT_STATE_CONNECT);
+               if (e_terminal[0])
+                       e_dtmf = 1;
+               memset(&e_connectinfo, 0, sizeof(e_connectinfo));
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_CONNECT);
+               message_put(message);
+               logmessage(message);
+               set_tone(portlist, "test");
+               break;
+               
+               case '5':
+               printlog("%3d  action   TESTMODE executing 'hold music' test.\n", ea_endpoint->ep_serial);
+               new_state(EPOINT_STATE_CONNECT);
+               if (e_terminal[0])
+                       e_dtmf = 1;
+               memset(&e_connectinfo, 0, sizeof(e_connectinfo));
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_CONNECT);
+               message_put(message);
+               logmessage(message);
+               set_tone(portlist, "hold");
+               break;
+               
+               case '6':
+               if (strlen(testcode) < 4)
+                       break;
+               testcode[4] = '\0';
+               cause = atoi(testcode+1);
+               if (cause > 255)
+                       cause = 0;
+               printlog("%3d  action   TESTMODE executing 'announcement' test with cause %d.\n", ea_endpoint->ep_serial, cause);
+               new_state(EPOINT_STATE_CONNECT);
+               if (e_terminal[0])
+                       e_dtmf = 1;
+               SPRINT(causestr,"cause_%02x",cause);
+               memset(&e_connectinfo, 0, sizeof(e_connectinfo));
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_CONNECT);
+               message_put(message);
+               logmessage(message);
+               set_tone(portlist, causestr);
+               break;
+               
+               case '7':
+               if (strlen(testcode) < 4)
+                       break;
+               testcode[4] = '\0';
+               cause = atoi(testcode+1);
+               if (cause > 127)
+                       cause = 0;
+               printlog("%3d  action   TESTMODE executing 'disconnect' test with cause %d.\n", ea_endpoint->ep_serial, cause);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               SPRINT(causestr,"cause_%02x",cause);
+               message_disconnect_port(portlist, cause, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, causestr);
+               break;
+
+               case '8': /* release */
+               printlog("%3d  action   TESTMODE executing 'release' test.\n", ea_endpoint->ep_serial);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "release");
+               break;
+
+               case '9': /* text callerid test */
+               printlog("%3d  action   TESTMODE executing 'caller id' test.\n", ea_endpoint->ep_serial);
+               new_state(EPOINT_STATE_CONNECT);
+               if (e_terminal[0])
+                       e_dtmf = 1;
+               memset(&e_connectinfo, 0, sizeof(e_connectinfo));
+               SCPY(e_connectinfo.id, "12345678");
+               SCPY(e_connectinfo.name, "Welcome to Linux");
+               SCPY(e_connectinfo.display, "Welcome to Linux");
+               e_connectinfo.ntype = INFO_NTYPE_UNKNOWN;
+               e_connectinfo.present = INFO_PRESENT_ALLOWED;
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_CONNECT);
+               memcpy(&message->param.connectinfo, &e_connectinfo, sizeof(message->param.connectinfo));
+               message_put(message);
+               logmessage(message);
+               set_tone(portlist, "hold");
+               break;
+       }
+}
+
+
+/* process init play
+ */
+void EndpointAppPBX::action_init_play(void)
+{
+       struct route_param *rparam;
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+
+       /* check given sample */
+       if (!(rparam = routeparam(e_action, PARAM_SAMPLE)))
+       {
+               printlog("%3d  action   PLAY no sample given.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): cannot play, because no sample has been specified\n", ea_endpoint->ep_serial);
+
+               disconnect:
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               e_action = NULL;
+               return;
+       }
+
+       /* if sample is given */
+       if (rparam->string_value[0] == '\0')
+       {
+               printlog("%3d  action   PLAY sample name with empty string given.\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): cannot play, because given sample is an empty string.\n", ea_endpoint->ep_serial);
+               goto disconnect;
+       }
+
+       if (e_terminal[0])
+               e_dtmf = 1;
+
+       set_tone(ea_endpoint->ep_portlist, rparam->string_value);
+}
+
+
+/*
+ * action_*_vbox_play is implemented in "action_vbox.cpp"
+ */
+
+
+/*
+ * process dialing of calculator
+ */
+void EndpointAppPBX::action_dialing_calculator(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct message *message;
+       double value1, value2, v, sign1;
+       int komma1, komma2, k, state, mode, first;
+       char *p;
+
+       portlist = ea_endpoint->ep_portlist;
+
+       /* remove error message */
+       if (!strncmp(e_extdialing, "Error", 5))
+       {
+               UCPY(e_extdialing, e_extdialing+5);
+       }
+       if (!strncmp(e_extdialing, "inf", 3))
+       {
+               UCPY(e_extdialing, e_extdialing+3);
+       }
+       if (!strncmp(e_extdialing, "-inf", 4))
+       {
+               UCPY(e_extdialing, e_extdialing+4);
+       }
+
+       /* process dialing */
+       state = 0;
+       value1 = 0;
+       value2 = 0;
+       komma1 = 0;
+       komma2 = 0;
+       sign1 = 1;
+       p = e_extdialing;
+       if (!p)
+               return;
+       first = 1;
+       while(*p)
+       {
+               if (*p>='0' && *p<='9')
+               {
+#if 0
+                       if (first)
+                       {
+                               UCPY(p, p+1);
+                               continue;
+                       }
+                       if ((p[-1]<'0' || p[-1]>'0') && p[-1]!='.')
+                       {
+                               p--;
+                               UCPY(p, p+1);
+                               continue;
+                       }
+#endif
+                       switch(state)
+                       {
+                               case 0: /* first number */
+                               if (!komma1)
+                               {
+                                       value1 = value1*10 + (*p-'0');
+                               } else
+                               {
+                                       k = komma1++;
+                                       v = *p-'0';
+                                       while(k--)
+                                               v /= 10;
+                                       value1 += v; 
+                               }
+                               break;
+                               case 1: /* second number */
+                               if (!komma2)
+                               {
+                                       value2 = value2*10 + (*p-'0');
+                               } else
+                               {
+                                       k = komma2++;
+                                       v = *p-'0';
+                                       while(k--)
+                                               v /= 10;
+                                       value2 += v; 
+                               }
+                               break;
+                       }
+               } else
+               switch(*p)
+               {
+                       case '*':
+                       if (first)
+                       {
+                               UCPY(e_extdialing, "Error");
+                               goto done;
+                       }
+                       /* if there is a multiplication, we change to / */
+                       if (p[-1] == '*')
+                       {
+                               mode = 1;
+                               p[-1] = '/';
+                               UCPY(p, p+1);
+                               p--;
+                               break;
+                       }
+                       /* if there is a division, we change to + */
+                       if (p[-1] == '/')
+                       {
+                               mode = 2;
+                               p[-1] = '+';
+                               UCPY(p, p+1);
+                               p--;
+                               break;
+                       }
+                       /* if there is a addition, we change to - */
+                       if (p[-1] == '+')
+                       {
+                               mode = 3;
+                               p[-1] = '-';
+                               UCPY(p, p+1);
+                               p--;
+                               break;
+                       }
+                       /* if there is a substraction and a comma, we change to * */
+                       if (p[-1]=='-' && komma1)
+                       {
+                               mode = 0;
+                               p[-1] = '*';
+                               UCPY(p, p+1);
+                               p--;
+                               break;
+                       }
+                       /* if there is a substraction and no comma and the first or second value, we change to , */
+                       if (p[-1]=='-')
+                       {
+                               p[-1] = '.';
+                               UCPY(p, p+1);
+                               p--;
+                               komma1 = 1;
+                               break;
+                       }
+                       /* if there is a komma and we are at the first value, we change to * */
+                       if (p[-1]=='.' && state==0)
+                       {
+                               mode = 0;
+                               p[-1] = '*';
+                               UCPY(p, p+1);
+                               p--;
+                               komma1 = 0;
+                               break;
+                       }
+                       /* if there is a komma and we are at the second value, we display error */
+                       if (komma2 && state==1)
+                       {
+                               UCPY(e_extdialing, "Error");
+                               goto done;
+                       }
+                       /* if we are at state 1, we write a comma */
+                       if (state == 1)
+                       {
+                               *p = '.';
+                               komma2 = 1;
+                               break;
+                       }
+                       /* we assume multiplication */
+                       mode = 0;
+                       state = 1;
+                       komma1 = 0;
+                       break;
+
+                       case '#':
+                       /* if just a number is displayed, the input is cleared */
+                       if (state==0)
+                       {
+                               *e_extdialing = '\0';
+                               break;
+                       }
+                       /* calculate the result */
+                       switch(mode)
+                       {
+                               case 0: /* multiply */
+                               UNPRINT(e_extdialing, sizeof(e_dialinginfo.number)-strlen(e_dialinginfo.number), "%.8f", sign1*value1*value2);
+                               break;
+                               case 1: /* divide */
+                               UNPRINT(e_extdialing, sizeof(e_dialinginfo.number)-strlen(e_dialinginfo.number), "%.8f", sign1*value1/value2);
+                               break;
+                               case 2: /* add */
+                               UNPRINT(e_extdialing, sizeof(e_dialinginfo.number)-strlen(e_dialinginfo.number), "%.8f", sign1*value1+value2);
+                               break;
+                               case 3: /* substract */
+                               UNPRINT(e_extdialing, sizeof(e_dialinginfo.number)-strlen(e_dialinginfo.number), "%.8f", sign1*value1-value2);
+                               break;
+                       }
+                       e_dialinginfo.number[sizeof(e_dialinginfo.number)-1] = '\0';
+                       if (strchr(e_extdialing, '.')) /* remove zeroes */
+                       {
+                               while (e_extdialing[strlen(e_extdialing)-1] == '0')
+                                       e_extdialing[strlen(e_extdialing)-1] = '\0';
+                               if (e_extdialing[strlen(e_extdialing)-1] == '.')
+                                       e_extdialing[strlen(e_extdialing)-1] = '\0'; /* and remove dot */
+                       }
+                       p = strchr(e_extdialing,'\0')-1;
+                       break;
+
+                       case '.':
+                       if (state)
+                               komma2 = 1;
+                       else    komma1 = 1;
+                       break;
+
+                       case '/':
+                       komma2 = 0;
+                       state = 1;
+                       mode = 1;
+                       break;
+
+                       case '+':
+                       komma2 = 0;
+                       state = 1;
+                       mode = 2;
+                       break;
+
+                       case '-':
+                       if (first)
+                       {
+                               sign1=-1;
+                               break;
+                       }
+                       komma2 = 0;
+                       state = 1;
+                       mode = 3;
+                       break;
+
+                       default:
+                       UCPY(e_extdialing, "Error");
+                       goto done;
+               }
+
+               p++;
+               first = 0;
+       }
+       done:
+
+       /* display dialing */   
+       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY);
+       SPRINT(message->param.notifyinfo.display, ">%s", e_extdialing);
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s displaying interpreted dialing '%s' internal values: %f %f\n", ea_endpoint->ep_serial, e_terminal, e_extdialing, value1, value2);
+       message_put(message);
+       logmessage(message);
+
+}
+
+/*
+ * process dialing of timer
+ */
+void EndpointAppPBX::action_dialing_timer(void)
+{
+}
+
+
+/*
+ * process 'goto' or 'menu'
+ */
+void EndpointAppPBX::_action_goto_menu(int mode)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct route_param *rparam;
+
+       /* check given ruleset */
+       if (!(rparam = routeparam(e_action, PARAM_RULESET)))
+       {
+               no_ruleset:
+               printlog("%3d  action   GOTO/MENU no ruleset ginven in options.conf\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): no ruleset was secified for action '%s' in routing.conf\n", ea_endpoint->ep_serial, (mode)?"menu":"goto");
+
+               disconnect:
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_SERVICEUNAVAIL, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               e_action = NULL;
+               return;
+       }
+       if (rparam->string_value[0] == '\0')
+               goto no_ruleset;
+       e_ruleset = getrulesetbyname(rparam->string_value);
+       if (!e_ruleset)
+       {
+               printlog("%3d  action   GOTO/MENU given ruleset '%s' not found in options.conf\n", ea_endpoint->ep_serial, rparam->string_value);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): given ruleset '%s' for action '%s' was not found in routing.conf\n", ea_endpoint->ep_serial, rparam->string_value, (mode)?"menu":"goto");
+               goto disconnect;
+       }
+       printlog("%3d  action   GOTO/MENU changing to ruleset '%s'\n", ea_endpoint->ep_serial, rparam->string_value);
+
+       /* if the 'menu' was selected, we will flush all digits */
+       if (mode)
+       {
+               //SCPY(e_dialinginfo.number, e_extdialing);
+               e_dialinginfo.number[0] = 0;
+               e_extdialing = e_dialinginfo.number;
+       } else
+       {
+       }
+
+       /* play sample */
+       if ((rparam = routeparam(e_action, PARAM_SAMPLE)))
+       {
+               printlog("%3d  action   GOTO/MENU start playing sample '%s'\n", ea_endpoint->ep_serial, rparam->string_value);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): playing sample '%s'\n", ea_endpoint->ep_serial, rparam->string_value);
+               set_tone(ea_endpoint->ep_portlist, rparam->string_value);
+       }
+
+       /* do dialing with new ruleset */
+       e_action = NULL;
+       process_dialing();
+}
+
+/* process dialing goto
+*/
+void EndpointAppPBX::action_dialing_goto(void)
+{
+       _action_goto_menu(0);
+}
+
+/* process dialing menu
+*/
+void EndpointAppPBX::action_dialing_menu(void)
+{
+       _action_goto_menu(1);
+}
+
+
+/*
+ * process dialing disconnect
+ */
+void EndpointAppPBX::action_dialing_disconnect(void)
+{
+       struct route_param *rparam;
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct message *message;
+       int cause = CAUSE_NORMAL; /* normal call clearing */
+       int location = LOCATION_PRIVATE_LOCAL;
+       char cause_string[256] = "", display[84] = "";
+
+       /* check cause parameter */
+       if ((rparam = routeparam(e_action, PARAM_CAUSE)))
+       {
+               cause = rparam->integer_value;
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): 'cause' is given: %d\n", ea_endpoint->ep_serial, cause);
+       }
+       if ((rparam = routeparam(e_action, PARAM_LOCATION)))
+       {
+               location = rparam->integer_value;
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): 'location' is given: %d\n", ea_endpoint->ep_serial, location);
+       }
+
+
+       /* use cause as sample, if not given later */
+       SPRINT(cause_string, "cause_%02x", cause);
+
+       /* check sample parameter */
+       if ((rparam = routeparam(e_action, PARAM_SAMPLE)))
+       {
+               SCPY(cause_string, rparam->string_value);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): 'sample' is given: %s\n", ea_endpoint->ep_serial, cause_string);
+       }
+
+       /* check display */
+       if ((rparam = routeparam(e_action, PARAM_DISPLAY)))
+       {
+               SCPY(display, rparam->string_value);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): 'display' is given: %s\n", ea_endpoint->ep_serial, display);
+       }
+
+       /* disconnect only if connect parameter is not given */
+       printlog("%3d  action   DISCONNECT with cause %d, location %d, sample '%s', display '%s'\n", ea_endpoint->ep_serial, cause, location, cause_string, display);
+       new_state(EPOINT_STATE_OUT_DISCONNECT);
+       set_tone(portlist, cause_string);
+       if (!(rparam = routeparam(e_action, PARAM_CONNECT)))
+       {
+               message_disconnect_port(portlist, cause, location, display);
+       } else
+       {
+               message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY);
+               SCPY(message->param.notifyinfo.display, display);
+               message_put(message);
+               logmessage(message);
+       }
+       e_action = NULL;
+}
+
+
+/*
+ * process dialing help
+ */
+void EndpointAppPBX::action_dialing_help(void)
+{
+       /* show all things that would match */
+#if 0
+       struct numbering *numbering = numbering_int;
+       char dialing[sizeof(e_dialinginfo.number)];
+       int i;
+       struct message *message;
+       struct route_param *rparam;
+
+       /* in case we have no menu (this should never happen) */
+       if (!numbering)
+               return;
+
+       /* scroll menu */
+       if (strchr(e_dialinginfo.number,'*'))
+       {
+               e_menu--;
+               e_dialinginfo.number[0] = '\0';
+       }
+       if (strchr(e_dialinginfo.number,'#'))
+       {
+               e_menu++;
+               e_dialinginfo.number[0] = '\0';
+       }
+       
+       /* get position in menu */
+       if (e_menu < 0)
+       {
+               /* get last menu position */
+               e_menu = 0;
+               while(numbering->next)
+               {
+                       e_menu++;
+                       numbering = numbering->next;
+               }
+       } else
+       {
+               /* get menu position */
+               i = 0;
+               while(i < e_menu)
+               {
+                       numbering = numbering->next;
+                       if (!numbering)
+                       {
+                               e_menu = 0;
+                               numbering = numbering_int;
+                               break;
+                       }
+                       i++;
+               }
+       }
+
+       /* if we dial something else we need to add the prefix and change the action */
+       if (e_dialinginfo.number[0])
+       {
+               e_action = NUMB_ACTION_NONE;
+               SCPY(dialing, numbering->prefix);
+               //we ignore the first digit after selecting
+               //SCAT(dialing, e_dialinginfo.number);
+               SCPY(e_dialinginfo.number, dialing);
+               e_extdialing = e_dialinginfo.number+strlen(numbering->prefix);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s selected a new menu '%s' dialing: %s\n", ea_endpoint->ep_serial, e_terminal, numb_actions[numbering->action], e_dialinginfo.number);
+nesting?:
+               process_dialing();
+               return;
+       }
+
+       /* send display message to port */
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY);
+       SPRINT(message->param.notifyinfo.display, ">%s %s%s%s", numbering->prefix, numb_actions[numbering->action], (numbering->param[0])?" ":"", numbering->param);
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s selected a new menu '%s' sending display:%s\n", ea_endpoint->ep_serial, e_terminal, numb_actions[numbering->action], message->param.notifyinfo.display);
+       message_put(message);
+       logmessage(message);
+#endif
+}
+
+
+/*
+ * process dialing deflect
+ */
+void EndpointAppPBX::action_dialing_deflect(void)
+{
+}
+
+
+/*
+ * process dialing setforward
+ */
+void EndpointAppPBX::action_dialing_setforward(void)
+{
+}
+
+
+/*
+ * process hangup 'execute'
+ */
+void EndpointAppPBX::action_hangup_execute(void)
+{
+       struct route_param *rparam;
+       char *command = "", isdn_port[10];
+       char *argv[8+1]; /* check also number of args below */
+       int i = 0;
+
+       /* get script / command */
+       if ((rparam = routeparam(e_action, PARAM_EXECUTE)))
+               command = rparam->string_value;
+       if (command[0] == '\0')
+       {
+               printlog("%3d  action   EXECUTE no 'execute' parameter given at routing.conf.\n", ea_endpoint->ep_serial);
+               PERROR("EPOINT(%d): terminal %s: NO PARAMETER GIVEN for 'execute' action. see routing.conf\n", ea_endpoint->ep_serial, e_terminal);
+               return;
+       }
+       printlog("%3d  action   EXECUTE command='%s'\n", ea_endpoint->ep_serial, command);
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: executing '%s'.\n", ea_endpoint->ep_serial, e_terminal, command);
+
+       argv[0] = command;
+       while(strchr(argv[0], '/'))
+               argv[0] = strchr(argv[0], '/')+1;
+       if ((rparam = routeparam(e_action, PARAM_PARAM)))
+       {
+               argv[1] = rparam->string_value;
+               i++;
+       }
+       argv[1+i] = e_extdialing;
+       argv[2+i] = numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype);
+       argv[3+i] = e_callerinfo.intern;
+       argv[4+i] = e_callerinfo.voip;
+       argv[5+i] = e_callerinfo.name;
+       SPRINT(isdn_port, "%d", e_callerinfo.isdn_port);
+       argv[6+i] = isdn_port;
+       argv[7+i] = NULL; /* check also number of args above */
+       execve("/bin/sh", argv, environ);
+}
+
+
+/*
+ * process hangup 'file'
+ */
+void EndpointAppPBX::action_hangup_file(void)
+{
+       struct route_param *rparam;
+       char *file, *content, *mode;
+       FILE *fp;
+
+       /* get file / content */
+       if (!(rparam = routeparam(e_action, PARAM_FILE)))
+               file = rparam->string_value;
+       else
+               file = "";
+       if (!(rparam = routeparam(e_action, PARAM_CONTENT)))
+               content = rparam->string_value;
+       else
+               content = e_extdialing;
+       if (!(rparam = routeparam(e_action, PARAM_APPEND)))
+               mode = "a";
+       else
+               mode = "w";
+       if (file[0] == '\0')
+       {
+               printlog("%3d  action   FILE no filename given.\n", ea_endpoint->ep_serial);
+               PERROR("EPOINT(%d): terminal %s: NO FILENAME GIVEN for 'file' action. see routing.conf\n", ea_endpoint->ep_serial, e_terminal);
+               return;
+       }
+       if (!(fp = fopen(file, mode)))
+       {
+               printlog("%3d  action   FILE file '%s' cannot be opened. (errno = %d)\n", ea_endpoint->ep_serial, file, errno);
+               PERROR("EPOINT(%d): terminal %s: given file '%s' cannot be opened. see routing.conf\n", ea_endpoint->ep_serial, e_terminal, file);
+               return;
+       }
+       printlog("%3d  action   FILE file='%s' content='%s'\n", ea_endpoint->ep_serial, file, content);
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: writing file '%s' with content '%s'.\n", ea_endpoint->ep_serial, e_terminal, file, content);
+       fprintf(fp, "%s\n", content);
+       fclose(fp);
+}
+
+
+/*
+ * process init 'pick'
+ */
+void EndpointAppPBX::action_init_pick(void)
+{
+       struct route_param *rparam;
+       char *extensions = NULL;
+
+       if ((rparam = routeparam(e_action, PARAM_EXTENSIONS)))
+               extensions = rparam->string_value;
+       
+       printlog("%3d  action   PICK\n", ea_endpoint->ep_serial);
+       pick_call(extensions);
+}
+
+
+/*
+ * process dialing 'password'
+ */
+void EndpointAppPBX::action_dialing_password(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+
+       /* prompt for password */
+       if (e_extdialing[0] == '\0')
+       {
+               /* give password tone */
+               set_tone(portlist, "password");
+       } else // ELSE!!
+       if (e_extdialing[1] == '\0')
+       {
+               /* give password tone */
+               set_tone(portlist, "dialing");
+       }
+
+       /* wait until all digits are dialed */
+       if (strlen(e_ext.password) != strlen(e_extdialing))
+               return; /* more digits needed */
+
+       /* check the password */
+       if (e_ext.password[0]=='\0' || (strlen(e_ext.password)==strlen(e_extdialing) && !!strcmp(e_ext.password,e_extdialing)))
+       {
+               printlog("%3d  action   PASSWORD WRITE password wrong\n", ea_endpoint->ep_serial);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): password wrong %s\n", ea_endpoint->ep_serial, e_extdialing);
+               e_connectedmode = 0;
+               e_dtmf = 0;
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_10");
+               return;
+       }
+
+       /* write caller id if ACTION_PASSWORD_WRITE was selected */
+       if (e_action)
+       if (e_action->index == ACTION_PASSWORD_WRITE)
+       {
+               append_callbackauth(e_terminal, &e_callbackinfo);
+               printlog("%3d  action   PASSWORD WRITE password written\n", ea_endpoint->ep_serial);
+       }
+
+       /* make call state  */
+       new_state(EPOINT_STATE_IN_OVERLAP);
+       e_ruleset = ruleset_main;
+       if (e_ruleset)
+               e_rule = e_ruleset->rule_first;
+       e_action = NULL;
+       e_dialinginfo.number[0] = '\0';
+       e_extdialing = e_dialinginfo.number;
+       set_tone(portlist, "dialpbx");
+}
+
+void EndpointAppPBX::action_dialing_password_wr(void)
+{
+       action_dialing_password();
+}
+
+
+/* general process dialing of incoming call
+ * depending on the detected prefix, subfunctions above (action_*) will be
+ * calles.
+ */
+void EndpointAppPBX::process_dialing(void)
+{
+       struct port_list *portlist = ea_endpoint->ep_portlist;
+       struct message *message;
+       struct route_param *rparam;
+
+//#warning Due to HANG-BUG somewhere here, I added some HANG-BUG-DEBUGGING output that cannot be disabled. after bug has been found, this will be removed.
+//PDEBUG(~0, "HANG-BUG-DEBUGGING: entered porcess_dialing\n");
+       portlist = ea_endpoint->ep_portlist;
+       /* check if we have a port instance linked to our epoint */
+       if (!portlist)
+       {
+               portlist_error:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): note: dialing call requires exactly one port object to process dialing. this case could happen due to a parked call. we end dialing here.\n", ea_endpoint->ep_serial, e_terminal);
+               e_action_timeout = 0;
+               e_match_timeout = 0;
+               return;
+       }
+       if (portlist->next)
+       {
+               goto portlist_error;
+       }
+
+       /* check nesting levels */
+       if (++e_rule_nesting > RULE_NESTING)
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): rules are nesting too deep. (%d levels) check for infinite loops in routing.conf\n", ea_endpoint->ep_serial, e_rule_nesting);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, "");
+               set_tone(portlist, "cause_3f");
+               e_action_timeout = 0;
+               e_match_timeout = 0;
+               goto end;
+       }
+
+//PDEBUG(~0, "HANG-BUG-DEBUGGING: before action-timeout processing\n");
+       /* process timeout */
+       if (e_action_timeout)
+       {
+               e_action_timeout = 0;
+               if (e_state == EPOINT_STATE_CONNECT)
+               {
+                       PDEBUG(DEBUG_ROUTE|DEBUG_EPOINT, "EPOINT(%d): action timed out, but we already have connected, so we stop timer and continue.\n", ea_endpoint->ep_serial);
+                       goto end;
+               }
+               if (e_action->index == ACTION_DISCONNECT
+                || e_state == EPOINT_STATE_OUT_DISCONNECT)
+               {
+                       /* release after disconnect */
+                       release(RELEASE_ALL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL);
+                       goto end;
+               }
+               release(RELEASE_CALL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, 0);
+               e_action = e_action->next;
+               if (!e_action)
+               {
+                       /* nothing more, so we release */
+                       PDEBUG(DEBUG_ROUTE|DEBUG_EPOINT, "EPOINT(%d): action timed out, and we have no next action, so we disconnect.\n", ea_endpoint->ep_serial);
+                       new_state(EPOINT_STATE_OUT_DISCONNECT);
+                       message_disconnect_port(portlist, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, "");
+                       set_tone(portlist, "cause_3f");
+                       goto end;
+               }
+               PDEBUG(DEBUG_ROUTE|DEBUG_EPOINT, "EPOINT(%d): continueing with action '%s'.\n", ea_endpoint->ep_serial, action_defs[e_action->index].name);
+               goto action_timeout;
+       }
+
+//PDEBUG(~0, "HANG-BUG-DEBUGGING: before setup/overlap state checking\n");
+       if (e_state!=EPOINT_STATE_IN_SETUP
+        && e_state!=EPOINT_STATE_IN_OVERLAP)
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): we are not in incomming setup/overlap state, so we ignore init/dialing process.\n", ea_endpoint->ep_serial, e_rule_nesting);
+               e_match_timeout = 0;
+               goto end;
+       }
+
+#if 0
+       /* check if we do menu selection */
+       if (e_action==NUMB_ACTION_NONE && (e_dialinginfo.number[0]=='*' || e_dialinginfo.number[0]=='#'))
+       /* do menu selection */
+       if (e_ext.display_menu)
+       {
+               if (portlist->port_type==PORT_TYPE_DSS1_NT_IN || portlist->port_type==PORT_TYPE_DSS1_NT_OUT) /* only if the dialing terminal is an isdn telephone connected to an internal port */
+               {
+                       e_dialinginfo.number[0] = '\0';
+                       e_action = NUMB_ACTION_MENU;
+                       e_menu = 0;
+                       process_dialing();
+                       e_match_timeout = 0;
+                       goto end;
+               }
+               /* invalid dialing */
+               message_disconnect_port(portlist, CAUSE_INCALID, LOCATION_PRIVATE_LOCAL, "");
+                       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_DISCONNECT);
+                       message->param.disconnectinfo.cause = CAUSE_INVALID;
+                       message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+                               } else
+                               {
+                                       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY);
+                                       SCPY(message->param.notifyinfo.display,get_isdn_cause(LOCATION_PRIVATE_LOCAL, epoint->e_ext.display_cause, param->disconnectinfo.location, param->disconnectinfo.cause));
+                               }
+                       message_put(message);
+                       logmessage(message);
+               }
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               set_tone(portlist,"cause_1c");
+               e_match_timeout = 0;
+               goto end;
+       }
+#endif
+
+//PDEBUG(~0, "HANG-BUG-DEBUGGING: before e_action==NULL\n");
+       /* if no action yet, we will call try to find a matching rule */
+       if (!e_action)
+       {
+               /* be sure that all selectors are initialized */
+               e_select = 0;
+
+               /* check for external call */
+               if (!strncmp(e_dialinginfo.number, "extern:", 7))
+               {
+                       e_extdialing = e_dialinginfo.number+7;
+                       e_action = &action_external;
+                       goto process_action;
+               }
+               /* check for internal call */
+               if (!strncmp(e_dialinginfo.number, "intern:", 7))
+               {
+                       e_extdialing = e_dialinginfo.number+7;
+                       e_action = &action_internal;
+                       goto process_action;
+               }
+               /* check for h323 call */
+               if (!strncmp(e_dialinginfo.number, "h323:", 5))
+               {
+                       e_extdialing = e_dialinginfo.number+5;
+                       e_action = &action_h323;
+                       goto process_action;
+               }
+               /* check for chan call */
+               if (!strncmp(e_dialinginfo.number, "chan:", 5))
+               {
+                       e_extdialing = e_dialinginfo.number+4;
+                       e_action = &action_chan;
+                       goto process_action;
+               }
+               /* check for vbox call */
+               if (!strncmp(e_dialinginfo.number, "vbox:", 5))
+               {
+                       e_extdialing = e_dialinginfo.number+5;
+                       e_action = &action_vbox;
+                       goto process_action;
+               }
+
+               if (e_match_timeout && now_d>=e_match_timeout)
+               {
+                       /* return timeout rule */
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal '%s' dialing: '%s', timeout in ruleset '%s'\n", ea_endpoint->ep_serial, e_terminal, e_dialinginfo.number, e_ruleset->name);
+                       e_match_timeout = 0;
+                       e_action = e_match_to_action;
+                       e_extdialing = e_match_to_extdialing;
+                       printlog("%3d  routing  TIMEOUT processing action '%s' (line %d)\n", ea_endpoint->ep_serial, action_defs[e_action->index].name, e_action->line);
+
+               } else
+               {
+//PDEBUG(~0, "HANG-BUG-DEBUGGING: before routing\n");
+                       /* check for matching rule */
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal '%s' dialing: '%s', checking matching rule of ruleset '%s'\n", ea_endpoint->ep_serial, e_terminal, e_dialinginfo.number, e_ruleset->name);
+                       if (e_ruleset)
+                       {
+                               e_action = route(e_ruleset);
+                               if (e_action)
+                                       printlog("%3d  routing  MATCH processing action '%s' (line %d)\n", ea_endpoint->ep_serial, action_defs[e_action->index].name, e_action->line);
+                       } else
+                       {
+                               e_action = &action_disconnect;
+                               if (e_action)
+                                       printlog("%3d  routing  NO MAIN RULESET, DISCONNECTING! '%s'\n", ea_endpoint->ep_serial, action_defs[e_action->index].name);
+                       }
+//PDEBUG(~0, "HANG-BUG-DEBUGGING: after routing\n");
+               }
+               if (!e_action)
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): no rule within the current ruleset matches yet.\n", ea_endpoint->ep_serial, e_terminal);
+                       goto display;
+               }
+
+               /* matching */
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): a rule with action '%s' matches.\n", ea_endpoint->ep_serial, action_defs[e_action->index].name);
+
+               action_timeout:
+
+               /* set timeout */
+               e_action_timeout = 0;
+               if (e_action->timeout)
+               {
+                       e_action_timeout = now_d + e_action->timeout;
+                       PDEBUG(DEBUG_ROUTE|DEBUG_EPOINT, "EPOINT(%d): action has a timeout of %d secods.\n", ea_endpoint->ep_serial, e_action->timeout);
+               }
+
+               process_action:
+               /* check param proceeding / alerting / connect */
+               if ((rparam = routeparam(e_action, PARAM_CONNECT)))
+               {
+                       /* NOTE: we may not change our state to connect, because dialing will then not possible */
+                       e_dtmf = 1;
+                       memset(&e_connectinfo, 0, sizeof(e_connectinfo));
+                       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_CONNECT);
+                       message_put(message);
+                       logmessage(message);
+               } else
+               if ((rparam = routeparam(e_action, PARAM_ALERTING)))
+               {
+                       /* NOTE: we may not change our state to alerting, because dialing will then not possible */
+                       memset(&e_connectinfo, 0, sizeof(e_connectinfo));
+                       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_ALERTING);
+                       message_put(message);
+                       logmessage(message);
+               } else
+               if ((rparam = routeparam(e_action, PARAM_PROCEEDING)))
+               {
+                       /* NOTE: we may not change our state to proceeding, because dialing will then not possible */
+                       memset(&e_connectinfo, 0, sizeof(e_connectinfo));
+                       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_PROCEEDING);
+                       message_put(message);
+                       logmessage(message);
+               }
+
+               if (action_defs[e_action->index].init_func)
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: current action '%s' has a init function, so we call it...\n", ea_endpoint->ep_serial, e_terminal, action_defs[e_action->index].name);
+                       (this->*(action_defs[e_action->index].init_func))();
+               }
+               if (e_state!=EPOINT_STATE_IN_SETUP
+                && e_state!=EPOINT_STATE_IN_OVERLAP)
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): AFTER init process: we are not in incomming setup/overlap state anymore, so we ignore further dialing process.\n", ea_endpoint->ep_serial, e_rule_nesting);
+                       goto display_action;
+               }
+       }
+
+       /* show what we are doing */
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal '%s' action: %s (dialing '%s')\n", ea_endpoint->ep_serial, e_terminal, action_defs[e_action->index].name, e_extdialing);
+       /* go to action's dialing function */
+       if (action_defs[e_action->index].dialing_func)
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: current action '%s' has a dialing function, so we call it...\n", ea_endpoint->ep_serial, e_terminal, action_defs[e_action->index].name);
+               (this->*(action_defs[e_action->index].dialing_func))();
+       }
+
+       /* display selected dialing action if enabled and still in setup state */
+       display_action:
+       if (e_action)
+       {
+               if (e_action->index==ACTION_MENU
+                || e_action->index==ACTION_REDIAL
+                || e_action->index==ACTION_REPLY
+                || e_action->index==ACTION_TIMER
+                || e_action->index==ACTION_CALCULATOR
+                || e_action->index==ACTION_TEST)
+                       goto end;
+       }
+       display:
+       if (!e_ext.display_dialing)
+               goto end;
+       if (e_state==EPOINT_STATE_IN_OVERLAP || e_state==EPOINT_STATE_IN_PROCEEDING || e_state==EPOINT_STATE_IN_ALERTING || e_state==EPOINT_STATE_CONNECT/* || e_state==EPOINT_STATE_IN_DISCONNECT || e_state==EPOINT_STATE_OUT_DISCONNECT*/)
+       if (portlist->port_type==PORT_TYPE_DSS1_NT_IN || portlist->port_type==PORT_TYPE_DSS1_NT_OUT) /* only if the dialing terminal is an isdn telephone connected to an internal port */
+       {
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY);
+
+               if (!e_action)
+               {
+                       SPRINT(message->param.notifyinfo.display, "> %s", e_dialinginfo.number);
+               } else
+               {
+                       SPRINT(message->param.notifyinfo.display, "%s%s%s", action_defs[e_action->index].name, (e_extdialing[0])?" ":"", e_extdialing);
+               }
+
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s displaying interpreted dialing '%s'\n", ea_endpoint->ep_serial, e_terminal, message->param.notifyinfo.display);
+               message_put(message);
+               logmessage(message);
+       }
+
+end:
+       e_rule_nesting--;
+       return;
+}
+
+
+/* some services store information after hangup */
+void EndpointAppPBX::process_hangup(int cause, int location)
+{
+       char callertext[256], dialingtext[256];
+       int writeext = 0, i;
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal '%s'\n", ea_endpoint->ep_serial, e_terminal);
+       if (e_terminal[0])
+       {
+               if (read_extension(&e_ext, e_terminal))
+                       writeext = 0x10;
+
+               if (!e_start)
+               {
+                       time(&e_start);
+                       e_stop = 0;
+               } else
+               if (!e_stop)
+                       time(&e_stop);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): writing connect from %s to %s into logfile of %s\n", ea_endpoint->ep_serial, e_callerinfo.id, e_dialinginfo.number, e_terminal);
+               switch(e_dialinginfo.itype)
+               {
+                       case INFO_ITYPE_H323:
+                       SPRINT(dialingtext, "h323:%s", e_dialinginfo.number);
+                       break;
+                       case INFO_ITYPE_CHAN:
+                       SPRINT(dialingtext, "chan:%s", e_dialinginfo.number);
+                       break;
+                       case INFO_ITYPE_INTERN:
+                       SPRINT(dialingtext, "intern:%s", e_dialinginfo.number);
+                       break;
+                       case INFO_ITYPE_VBOX:
+                       SPRINT(dialingtext, "vbox:%s", e_dialinginfo.number);
+                       break;
+                       default:
+                       SPRINT(dialingtext, "%s", e_dialinginfo.number);
+               }
+
+               if (e_callerinfo.id[0])
+                       SPRINT(callertext, "%s", numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype));
+               else
+                       SPRINT(callertext, "unknown");
+               /* allpy restriction */
+               if (!e_ext.anon_ignore && e_callerinfo.present==INFO_PRESENT_RESTRICTED)
+                       SPRINT(callertext, "anonymous");
+               if (e_callerinfo.intern[0]) /* add intern if present */
+                       UNPRINT(strchr(callertext,'\0'), sizeof(callertext)-1+strlen(callertext), " (intern %s)", e_callerinfo.intern);
+               if (e_callerinfo.voip[0]) /* add voip if present */
+                       UNPRINT(strchr(callertext,'\0'), sizeof(callertext)-1+strlen(callertext), " (voip %s)", e_callerinfo.voip);
+               write_log(e_terminal, callertext, dialingtext, e_start, e_stop, 0, cause, location);
+
+               /* store last received call for reply-list */
+               if (e_callerinfo.id[0] || e_callerinfo.intern[0])
+               if (e_ext.anon_ignore || e_callerinfo.present!=INFO_PRESENT_RESTRICTED)
+               {
+                       if (e_callerinfo.intern[0])
+                               SPRINT(callertext, "intern:%s", e_callerinfo.intern);
+                       else
+                               SPRINT(callertext, "extern:%s", numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype));
+                       if (!!strcmp(callertext, e_ext.last_in[0]))
+                       {
+                               i = MAX_REMEMBER-1;
+                               while(i)
+                               {
+                                       UCPY(e_ext.last_in[i], e_ext.last_in[i-1]);
+                                       i--;
+                               }
+                               SCPY(e_ext.last_in[0], callertext);
+                               writeext |= 1; /* store extension later */
+                               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: storing last received caller id '%s'.\n", ea_endpoint->ep_serial, e_terminal, e_ext.last_in[0]);
+                       } else
+                               PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: cannot store last received id '%s' because it is identical with the last one.\n", ea_endpoint->ep_serial, e_terminal, callertext);
+               }
+       }
+
+       /* write extension if needed */
+       if (writeext == 0x11)
+               write_extension(&e_ext, e_terminal);
+
+       if (e_action)
+       {
+               if (action_defs[e_action->index].hangup_func)
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s: current action '%s' has a hangup function, so we call it...\n", ea_endpoint->ep_serial, e_terminal, action_defs[e_action->index].name);
+                       (this->*(action_defs[e_action->index].hangup_func))();
+               }
+       }
+}
+
diff --git a/action_efi.cpp b/action_efi.cpp
new file mode 100644 (file)
index 0000000..ce0253f
--- /dev/null
@@ -0,0 +1,161 @@
+/*****************************************************************************\
+**                                                                           **
+** PBX4Linux                                                                 **
+**                                                                           **
+**---------------------------------------------------------------------------**
+** Copyright: Andreas Eversberg                                              **
+**                                                                           **
+** elektronische fernmelder identifikation                                   **
+**                                                                           **
+\*****************************************************************************/ 
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "main.h"
+
+enum {
+       EFI_STATE_DIE,
+       EFI_STATE_BENUTZERDEFINIERTE,
+       EFI_STATE_UNTERDRUECKTE,
+       EFI_STATE_RUFNUMMER_LAUTET,
+       EFI_STATE_DIGIT,
+       EFI_STATE_ICH_WIEDERHOLE,
+       EFI_STATE_STOP,
+};
+
+void EndpointAppPBX::action_init_efi(void)
+{
+//     int                     language = e_ext.vbox_language;
+//     struct route_param      *rparam;
+       struct message          *message;
+       struct port_list        *portlist = ea_endpoint->ep_portlist;
+
+       /* if no caller id */
+       if (e_callerinfo.id[0] == '\0')
+       {
+               /* facility rejected */
+               message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_DISCONNECT);
+               message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+               message->param.disconnectinfo.cause = CAUSE_FACILITYREJECTED;
+               message_put(message);
+               logmessage(message);
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               set_tone(portlist,"cause_22");
+               return;
+       }
+
+       /* connect */
+       new_state(EPOINT_STATE_CONNECT);
+
+       /* initialize the vbox */
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) initializing efi\n", ea_endpoint->ep_serial);
+
+       e_efi_state = EFI_STATE_DIE;
+       set_tone_efi("die");
+
+       e_efi_digit = 0;
+}
+
+/*
+ * the audio file has ended
+ * this is called by Endpoint::message_port(), whenever an audio of has been received
+ */
+void EndpointAppPBX::efi_message_eof(void)
+{
+//     char buffer[32];
+       char digit[] = "number_00";
+       struct message          *message;
+       struct port_list        *portlist = ea_endpoint->ep_portlist;
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) terminal %s end of file during state: %d\n", ea_endpoint->ep_serial, e_terminal, e_vbox_state);
+
+       switch(e_efi_state)
+       {
+               case EFI_STATE_DIE:
+               if (e_callerinfo.screen==INFO_SCREEN_USER)
+               {
+                       e_efi_state = EFI_STATE_BENUTZERDEFINIERTE;
+                       set_tone_efi("benutzerdefinierte");
+                       break;
+               }
+               // fall through
+               case EFI_STATE_BENUTZERDEFINIERTE:
+               if (e_callerinfo.present==INFO_PRESENT_RESTRICTED)
+               {
+                       e_efi_state = EFI_STATE_UNTERDRUECKTE;
+                       set_tone_efi("unterdrueckte");
+                       break;
+               }
+               // fall through
+               case EFI_STATE_UNTERDRUECKTE:
+               e_efi_state = EFI_STATE_RUFNUMMER_LAUTET;
+               set_tone_efi("rufnummer_lautet");
+               break;
+
+               case EFI_STATE_RUFNUMMER_LAUTET:
+               e_efi_state = EFI_STATE_DIGIT;
+               e_efi_digit = 0;
+               // fall through
+               case EFI_STATE_DIGIT:
+               digit[8] = numberrize_callerinfo(e_callerinfo.id,e_callerinfo.ntype)[e_efi_digit];
+               if (digit[8])
+               {
+                       set_tone_efi(digit);
+                       e_efi_digit++;
+               } else
+               {
+                       e_efi_state = EFI_STATE_STOP; //EFI_STATE_ICH_WIEDERHOLE;
+                       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_DISCONNECT);
+                       message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+                       message->param.disconnectinfo.cause = CAUSE_NORMAL;
+                       message_put(message);
+                       logmessage(message);
+                       new_state(EPOINT_STATE_OUT_DISCONNECT);
+                       set_tone(portlist,"cause_10");
+//                     set_tone_efi("ich_wiederhole");
+               }
+               break;
+
+               case EFI_STATE_ICH_WIEDERHOLE:
+               e_efi_state = EFI_STATE_DIE;
+               set_tone_efi("die");
+               break;
+
+               case EFI_STATE_STOP:
+               break;
+
+               default:
+               PERROR("efi_message_eof(ep%d): terminal %s unknown state: %d\n", ea_endpoint->ep_serial, e_terminal, e_vbox_state);
+       }
+}
+
+
+
+/*
+ * set the given vbox-tone with full path (without appending)
+ * the tone is played and after eof, a message is received
+ */
+void EndpointAppPBX::set_tone_efi(char *tone)
+{
+       struct message *message;
+
+       if (tone == NULL)
+               tone = "";
+
+       if (!ea_endpoint->ep_portlist)
+       {
+               PERROR("EPOINT(%d) no portlist\n", ea_endpoint->ep_serial);
+       }
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_VBOX_TONE);
+       SCPY(message->param.tone.dir, (char *)"tones_efi");
+       SCPY(message->param.tone.name, tone);
+       message_put(message);
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) terminal %s set tone '%s'\n", ea_endpoint->ep_serial, e_terminal, tone);
+}
+
diff --git a/action_vbox.cpp b/action_vbox.cpp
new file mode 100644 (file)
index 0000000..afdbc6e
--- /dev/null
@@ -0,0 +1,975 @@
+/*****************************************************************************\
+**                                                                           **
+** PBX4Linux                                                                 **
+**                                                                           **
+**---------------------------------------------------------------------------**
+** Copyright: Andreas Eversberg                                              **
+**                                                                           **
+** dialing for answering machine is processed here                           **
+**                                                                           **
+\*****************************************************************************/ 
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "main.h"
+
+
+// note: the given display message (e_vbox_display) may include "%s" for the counter
+
+/*
+ * these are the state, the vbox is in. if the current tone has been played,
+ * an action will be calles as defined in vbox_message_eof(), which is called 
+ * from Epoint:handler().
+ * also this state is used to determine the correct processing of the current key press
+ */
+enum {
+       VBOX_STATE_MENU,                /* tell the menu */
+VBOX_STATE_CALLINFO_BEGIN, /* this value defines the start of callinfo */
+       VBOX_STATE_CALLINFO_INTRO,      /* tell that the "call is received at" */
+       VBOX_STATE_CALLINFO_MONTH,      /* tell the month */
+       VBOX_STATE_CALLINFO_DAY,        /* tell the day */
+       VBOX_STATE_CALLINFO_HOUR,       /* tell the hour */
+       VBOX_STATE_CALLINFO_OCLOCK,     /* tell the word "o'clock" */
+       VBOX_STATE_CALLINFO_MIN,        /* tell the minute */
+       VBOX_STATE_CALLINFO_MINUTES,    /* tell the word "minutes" */
+       VBOX_STATE_CALLINFO_DIGIT,      /* tell the digits */
+VBOX_STATE_CALLINFO_END, /* this value defines the end of callingo */
+       VBOX_STATE_NOTHING,             /* tells that no calls are recorded */
+       VBOX_STATE_PLAY,                /* play the current recording */
+       VBOX_STATE_PAUSE,               /* tell that the recording is paused */
+       VBOX_STATE_RECORD_ASK,  /* ask for recording */ 
+       VBOX_STATE_RECORD_PLAY, /* play recording */    
+       VBOX_STATE_RECORD_RECORD,       /* record recording */  
+       VBOX_STATE_STORE_ASK,   /* ask for store */
+       VBOX_STATE_DELETE_ASK,  /* ask for delete */
+       VBOX_STATE_STORE_DONE,  /* tell that message is store */
+       VBOX_STATE_DELETE_DONE, /* tell that message is delete */
+};
+
+char *months_english[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
+char *months_german[] = {"Jan","Feb","Maer","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"};
+
+struct vbox_menu {
+       char digit;
+       char *english;
+       char *german;
+       } vbox_menu[] = {
+       {'1', "<< previous", "<< zurueck"},
+       {'2', "-> play", "-> anhoeren"},
+       {'3', ">> next", ">> vor"},
+       {'4', "<  rewind", "<  rueckspulen"},
+       {'5', "[] stop", "[] stop"},
+       {'6', ">  wind", ">  vorspulen"},
+       {'7', "() record", "() Aufnahme"},
+       {'8', "=  store", "=  speichern"},
+       {'9', "X  delete", "X  loeschen"},
+       {'0', "*  call", "*  anrufen"},
+       {'\0', NULL, NULL}
+       };
+
+/*
+ * initialize the vbox. this is called at process_dialing(), when the VBOX_PLAY
+ * action has been selected by the caller
+ */
+void EndpointAppPBX::action_init_vbox_play(void)
+{
+       int                     language = e_ext.vbox_language;
+       struct route_param      *rparam;
+       struct message          *message;
+       struct port_list        *portlist = ea_endpoint->ep_portlist;
+
+       /* get extension */
+       SCPY(e_vbox, e_terminal);
+       if ((rparam = routeparam(e_action, PARAM_EXTENSION)))
+               SCPY(e_vbox, rparam->string_value);
+       if (e_vbox[0] == '\0')
+       {
+               /* facility rejected */
+               message_disconnect_port(portlist, CAUSE_FACILITYREJECTED, LOCATION_PRIVATE_LOCAL, "");
+               new_state(EPOINT_STATE_OUT_DISCONNECT);
+               set_tone(portlist,"cause_22");
+               return;
+       }
+
+       /* connect, but still accept more digits */
+       new_state(EPOINT_STATE_IN_OVERLAP);
+       if (e_terminal[0])
+               e_dtmf = 1;
+       message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_CONNECT);
+       message_put(message);
+       logmessage(message);
+
+       /* initialize the vbox */
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) initializing answering vbox state\n", ea_endpoint->ep_serial);
+
+       e_vbox_state = VBOX_STATE_MENU;
+       SCPY(e_vbox_display, (char *)((language)?"druecke 2 f. wiedergabe":"press 2 to play"));
+       e_vbox_display_refresh = 1;
+       set_tone_vbox("menu");
+
+       e_vbox_menu = -1;
+       e_vbox_play = 0;
+       vbox_index_read(e_vbox_play);
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) number of calls: %d\n", ea_endpoint->ep_serial, e_vbox_index_num);
+
+       if (e_vbox_index_num == 0)
+       {
+               e_vbox_state = VBOX_STATE_NOTHING;
+               SCPY(e_vbox_display, (char *)((language)?"keine Anrufe":"no calls"));
+               e_vbox_display_refresh = 1;
+               set_tone_vbox("nothing");
+       }
+}
+
+/*
+ * read index list, and fill the index variables with the given position
+ * if the index is empty (or doesn't exist), the variables are not filled.
+ * but alway the e_vbox_index_num is given.
+ */
+void EndpointAppPBX::vbox_index_read(int num)
+{
+       FILE *fp;
+       char filename[256];
+       char buffer[256];
+       char name[sizeof(buffer)];
+       char callerid[sizeof(buffer)];
+       int year, mon, mday, hour, min;
+       int i;
+
+       e_vbox_index_num = 0;
+
+       SPRINT(filename, "%s/%s/%s/vbox/index", INSTALL_DATA, options.extensions_dir, e_vbox);
+       if (!(fp = fopen(filename, "r")))
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) no files in index\n", ea_endpoint->ep_serial);
+               return;
+       }
+       fduse++;
+
+       i = 0;
+       while((fgets(buffer,sizeof(buffer),fp)))
+       {
+               buffer[sizeof(buffer)-1] = '\0';
+               if (buffer[0]) buffer[strlen(buffer)-1] = '\0';
+
+               name[0] = callerid[0] = '\0';
+               mon = mday = hour = min = 0;
+               sscanf(buffer, "%s %d %d %d %d %d %s", name, &year, &mon, &mday, &hour, &min, callerid);
+
+               if (name[0]=='\0' || name[0]=='#')
+                       continue;
+
+               /* the selected entry */
+               if (i == num)
+               {
+                       SCPY(e_vbox_index_file, name);
+                       e_vbox_index_year = year;
+                       e_vbox_index_mon = mon;
+                       e_vbox_index_mday = mday;
+                       e_vbox_index_hour = hour;
+                       e_vbox_index_min = min;
+                       SCPY(e_vbox_index_callerid, callerid);
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) read entry #%d: '%s', %02d:%02d %02d:%02d cid='%s'\n", ea_endpoint->ep_serial, i, name, mon+1, mday, hour, min, callerid);
+               }
+
+               i++;
+       }
+
+       e_vbox_index_num = i;
+
+       fclose(fp);
+       fduse--;
+}
+
+
+/*
+ * removes given index from list
+ * after removing, the list should be reread, since e_vbox_index_num
+ * and the current variabled do not change
+ */
+void EndpointAppPBX::vbox_index_remove(int num)
+{
+       FILE *fpr, *fpw;
+       char buffer[256];
+       int i;
+       char filename1[256], filename2[256];
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) removing entrie #%d\n", ea_endpoint->ep_serial, num);
+
+       SPRINT(filename1, "%s/%s/%s/vbox/index", INSTALL_DATA, options.extensions_dir, e_vbox);
+       SPRINT(filename2, "%s/%s/%s/vbox/index-temp", INSTALL_DATA, options.extensions_dir, e_vbox);
+       if (!(fpr = fopen(filename1, "r")))
+       {
+               return;
+       }
+       if (!(fpw = fopen(filename2, "w")))
+       {
+               fclose(fpr);
+               return;
+       }
+       fduse += 2;
+
+       i = 0;
+       while((fgets(buffer,sizeof(buffer),fpr)))
+       {
+               buffer[sizeof(buffer)-1] = '\0';
+               if (buffer[0]) buffer[strlen(buffer)-1] = '\0';
+
+               if (buffer[0]=='\0' || buffer[0]=='#')
+               {
+                       fprintf(fpw, "%s\n", buffer);
+                       continue;       
+               }
+
+               /* the selected entry will not be written */
+               if (i != num)
+               {
+                       fprintf(fpw, "%s\n", buffer);
+               }
+
+               i++;
+       }
+
+       fclose(fpr);
+       fclose(fpw);
+       fduse -= 2;
+
+       rename(filename2, filename1);
+}
+
+
+/*
+ * process dialing of vbox_play (actually the menu)
+ * it is depended by the state, which action is performed
+ */
+void EndpointAppPBX::action_dialing_vbox_play(void)
+{
+       int language = e_ext.vbox_language;
+       struct port_list *portlist;
+       class Port *port;
+       
+       portlist = ea_endpoint->ep_portlist;
+
+       if (e_extdialing[0] == '\0')
+       {
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) called with no digit\n", ea_endpoint->ep_serial);
+               return;
+       }
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) dialing digit: %c\n", ea_endpoint->ep_serial, e_extdialing[0]);
+
+       e_vbox_display_refresh = 1;
+
+       if (e_vbox_state == VBOX_STATE_RECORD_RECORD)
+       {
+               if (e_extdialing[0] == '1')
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) stopping recording of announcement.\n", ea_endpoint->ep_serial);
+
+                       port = find_port_id(portlist->port_id);
+                       if (port)
+                               port->close_record(6000); /* append beep */
+                       goto record_ask;
+               }
+               goto done;
+       }
+
+       if (e_vbox_state == VBOX_STATE_RECORD_PLAY)
+       {
+               if (e_extdialing[0] == '1')
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) stopping playback of announcement.\n", ea_endpoint->ep_serial);
+
+                       goto record_ask;
+               }
+               goto done;
+       }
+
+       if (e_vbox_state == VBOX_STATE_RECORD_ASK)
+       {
+               switch(e_extdialing[0])
+               {
+                       case '3':
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) quit recoding menu.\n", ea_endpoint->ep_serial);
+                       ask_abort:
+                       /* abort */
+                       e_vbox_state = VBOX_STATE_MENU;
+                       SCPY(e_vbox_display, (char *)((language)?"druecke 2 f. wiedergabe":"press 2 to play"));
+                       set_tone_vbox("menu");
+                       break;
+
+                       case '2':
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) play recoding.\n", ea_endpoint->ep_serial);
+                       /* play announcement */
+                       e_vbox_counter = 0;
+                       e_vbox_counter_last = 0;
+                       e_vbox_counter_max = 0;
+                       e_vbox_speed = 1;
+                       e_vbox_state = VBOX_STATE_RECORD_PLAY;
+                       if (e_ext.vbox_language)
+                               SCPY(e_vbox_display, "Wied., 1=stop %s");
+                       else
+                               SCPY(e_vbox_display, "play, 1=stop %s");
+                       if (e_ext.vbox_display == VBOX_DISPLAY_BRIEF)
+                               SCPY(e_vbox_display, "1=stop %s");
+                       set_play_vbox("announcement", 0);
+                       break;
+
+                       case '1':
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) record announcement.\n", ea_endpoint->ep_serial);
+                       /* close recording if already recording */
+                       port = find_port_id(portlist->port_id);
+                       if (port)
+                       {
+                               port->close_record(6000); /* append beep */
+                               port->open_record(CODEC_MONO, 1, 5000, e_terminal, 0, "", 0); /* record announcement, skip the first 5000 samples */
+                       }
+                       e_vbox_state = VBOX_STATE_RECORD_RECORD;
+                       if (e_ext.vbox_language)
+                               SCPY(e_vbox_display, "Aufnahme, 1=stop");
+                       else
+                               SCPY(e_vbox_display, "recording, 1=stop");
+                       set_tone_vbox(NULL);
+                       break;
+
+                       default:
+                       ;
+               }
+               goto done;
+       }
+
+       if (e_vbox_state==VBOX_STATE_STORE_ASK || e_vbox_state==VBOX_STATE_DELETE_ASK)
+       {
+               char filename[256], filename2[256];
+
+               switch(e_extdialing[0])
+               {
+                       case '3':
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) quit store/delete menu.\n", ea_endpoint->ep_serial);
+                       goto ask_abort;
+
+                       case '1':
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) do store/delete.\n", ea_endpoint->ep_serial);
+                       SPRINT(filename, "%s/%s/%s/vbox/%s", INSTALL_DATA, options.extensions_dir, e_vbox, e_vbox_index_file);
+
+                       /* move file */
+                       if (e_vbox_state == VBOX_STATE_STORE_ASK)
+                       {
+                               SPRINT(filename, "%s/%s/%s/recordings", INSTALL_DATA, options.extensions_dir, e_vbox);
+                               if (mkdir(filename, 0755) < 0)
+                               {
+                                       if (errno != EEXIST)
+                                       {
+                                               PERROR("EPOINT(%d) cannot create directory '%s'\n", ea_endpoint->ep_serial, filename);
+                                               goto done;
+                                       }
+                               }
+                               SPRINT(filename2, "%s/%s/%s/recordings/%s", INSTALL_DATA, options.extensions_dir, e_vbox, e_vbox_index_file);
+                               rename(filename, filename2);
+                               e_vbox_state = VBOX_STATE_STORE_DONE;
+                               if (e_ext.vbox_language)
+                                       SCPY(e_vbox_display, "Nachricht gespeichert!");
+                               else
+                                       SCPY(e_vbox_display, "Message stored!");
+                               set_tone_vbox("store_done");
+                       }
+
+                       /* remove file */
+                       if (e_vbox_state == VBOX_STATE_DELETE_ASK)
+                       {
+                               remove(filename);
+                               e_vbox_state = VBOX_STATE_DELETE_DONE;
+                               if (e_ext.vbox_language)
+                                       SCPY(e_vbox_display, "Nachricht geloescht!");
+                               else
+                                       SCPY(e_vbox_display, "Message deleted!");
+                               set_tone_vbox("delete_done");
+                       }
+
+                       /* remove from list */
+                       vbox_index_remove(e_vbox_play);
+                       vbox_index_read(e_vbox_play);
+                       /* stay at the last message+1, so we always get "no messages" */
+                       if (e_vbox_play>e_vbox_index_num && e_vbox_play)
+                       {
+                               e_vbox_play = e_vbox_index_num-1;
+                       }
+                       default:
+                       ;
+               }
+               goto done;
+       }
+
+       /* dialing during menu */
+       switch(e_extdialing[0])
+       {
+               /* process the vbox functions */
+               case '1': /* previous */
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) previous call is selected.\n", ea_endpoint->ep_serial);
+               if (e_vbox_index_num == 0) /* nothing to play */
+               {
+                       no_calls:
+                       e_vbox_state = VBOX_STATE_MENU;
+                       SCPY(e_vbox_display, (char *)((language)?"keine Anrufe":"no calls"));
+                       set_tone_vbox("nothing");
+                       break;
+               }
+               e_vbox_play--;
+               if (e_vbox_play < 0)
+               {
+                       e_vbox_play = 0;
+
+                       e_vbox_state = VBOX_STATE_MENU;
+                       SCPY(e_vbox_display, (char *)((language)?"kein vorheriger Anruf":"no previous call"));
+                       set_tone_vbox("nothing");
+                       break;
+               }
+               /* announce call */
+               announce_call:
+               e_vbox_state = VBOX_STATE_CALLINFO_INTRO;
+               SPRINT(e_vbox_display, "#%d", e_vbox_play+1);
+               vbox_index_read(e_vbox_play);
+               if (e_vbox_index_mon!=now_tm->tm_mon || e_vbox_index_year!=now_tm->tm_year)
+               {
+                       UPRINT(strchr(e_vbox_display,'\0'), " %s", (language)?months_german[e_vbox_index_mon]:months_english[e_vbox_index_mon]);
+               }
+               if (e_vbox_index_mday!=now_tm->tm_mday || e_vbox_index_mon!=now_tm->tm_mon || e_vbox_index_year!=now_tm->tm_year)
+               {
+                       UPRINT(strchr(e_vbox_display,'\0'), " %d", e_vbox_index_mday);
+               }
+               UPRINT(strchr(e_vbox_display,'\0'), " %02d:%02d", e_vbox_index_hour, e_vbox_index_min);
+               if (e_ext.vbox_display == VBOX_DISPLAY_DETAILED)
+                       UPRINT(strchr(e_vbox_display,'\0'), " (%s)", e_vbox_index_callerid);
+               set_tone_vbox("intro");
+               break;
+
+               case '2': /* play */
+               if (e_vbox_play >= e_vbox_index_num)
+                       goto no_messages;
+               if (e_vbox_index_num == 0) /* nothing to play */
+               {
+                       goto no_calls;
+               }
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) play call #%d.\n", ea_endpoint->ep_serial, e_vbox_play+1);
+               if (e_vbox_state>VBOX_STATE_CALLINFO_BEGIN && e_vbox_state<VBOX_STATE_CALLINFO_END)
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) play call #%d. abborting announcement and starting with playback\n", ea_endpoint->ep_serial, e_vbox_play+1);
+                       /* the callinfo is played, so we start with the call */
+                       e_vbox_counter = 0;
+                       e_vbox_counter_last = 0;
+                       e_vbox_counter_max = 0;
+                       e_vbox_speed = 1;
+                       e_vbox_state = VBOX_STATE_PLAY;
+                       SPRINT(e_vbox_display, "#%d %%s", e_vbox_play+1);
+                       if (e_ext.vbox_display == VBOX_DISPLAY_DETAILED)
+                               UPRINT(strchr(e_vbox_display,'\0'), " (%s)", e_vbox_index_callerid);
+                       set_play_vbox(e_vbox_index_file, 0);
+                       break;
+               } else
+               if (e_vbox_state==VBOX_STATE_PLAY && e_vbox_speed!=1)
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) play call #%d. play speed is different from 1, so we play now with normal speed\n", ea_endpoint->ep_serial, e_vbox_play+1);
+                       /* we set play speed to normal */
+                       e_vbox_speed = 1;
+                       set_play_speed(e_vbox_speed);
+               } else
+               if (e_vbox_state == VBOX_STATE_PLAY)
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) play call #%d. play speed is equals 1, so we pause\n", ea_endpoint->ep_serial, e_vbox_play+1);
+                       /* we pause the current play */
+                       e_vbox_state = VBOX_STATE_PAUSE;
+                       SCPY(e_vbox_display, (char *)((language)?"druecke 2 f. wiedergabe":"press 2 to play"));
+                       set_tone_vbox("pause");
+               } else
+               if (e_vbox_state == VBOX_STATE_PAUSE)
+               {
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) play call #%d. currently pause, so we continue play\n", ea_endpoint->ep_serial, e_vbox_play+1);
+                       /* we continue the current play */
+                       e_vbox_state = VBOX_STATE_PLAY;
+                       SPRINT(e_vbox_display, "#%d %%s", e_vbox_play+1);
+                       if (e_ext.vbox_display == VBOX_DISPLAY_DETAILED)
+                               UPRINT(strchr(e_vbox_display,'\0'), " (%s)", e_vbox_index_callerid);
+                       set_play_vbox(e_vbox_index_file, e_vbox_counter);
+               } else
+               {
+                       /* now we have something else going on, so we announce the call */
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) play call #%d. announcing call during any other state\n", ea_endpoint->ep_serial, e_vbox_play+1);
+                       goto announce_call;
+               }
+               break;
+
+               case '3': /* next */
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) next call is selected.\n", ea_endpoint->ep_serial);
+               if (e_vbox_index_num == 0) /* nothing to play */
+               {
+                       goto no_calls;
+               }
+               e_vbox_play++;
+               if (e_vbox_play >= e_vbox_index_num)
+               {
+                       no_messages:
+                       e_vbox_play = e_vbox_index_num;
+
+                       e_vbox_state = VBOX_STATE_MENU;
+                       SCPY(e_vbox_display, (char *)((language)?"kein weiterer Anruf":"no next call"));
+                       set_tone_vbox("nothing");
+                       break;
+               }
+               /* announce call */
+               goto announce_call;
+               break;
+
+               case '4': /* rewind */
+               if (e_vbox_state==VBOX_STATE_PLAY)
+               {
+                       if (e_vbox_speed >= -1)
+                               e_vbox_speed = -1;
+                       e_vbox_speed = e_vbox_speed * 2;
+                       set_play_speed(e_vbox_speed);
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) rewind speed has been changed to: %d\n", ea_endpoint->ep_serial, e_vbox_speed);
+               } 
+               break;
+
+               case '5': /* stop */
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) stop is pressed, so we hear the menu\n", ea_endpoint->ep_serial);
+               e_vbox_state = VBOX_STATE_MENU;
+               SCPY(e_vbox_display, (char *)((language)?"druecke 2 f. wiedergabe":"press 2 to play"));
+               set_tone_vbox("menu");
+               break;
+
+               case '6': /* wind */
+               if (e_vbox_state==VBOX_STATE_PLAY)
+               {
+                       if (e_vbox_speed <= 1)
+                               e_vbox_speed = 1;
+                       e_vbox_speed = e_vbox_speed * 2;
+                       set_play_speed(e_vbox_speed);
+                       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) wind speed has been changed to: %d\n", ea_endpoint->ep_serial, e_vbox_speed);
+               } 
+               break;
+
+               case '7': /* record announcement */
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) entering the record announcement menu\n", ea_endpoint->ep_serial);
+               record_ask:
+               e_vbox_state = VBOX_STATE_RECORD_ASK;
+               SCPY(e_vbox_display, (char *)((language)?"1=Aufn. 2=Wied. 3=nein":"1=record 2=play 3=back"));
+               set_tone_vbox("record_ask");
+               break;
+
+               case '8': /* store file */
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) entering the store menu\n", ea_endpoint->ep_serial);
+               if (e_vbox_play >= e_vbox_index_num)
+                       goto no_messages;
+               if (e_vbox_index_num == 0) /* nothing to play */
+               {
+                       goto no_calls;
+               }
+               e_vbox_state = VBOX_STATE_STORE_ASK;
+               SCPY(e_vbox_display, (char *)((language)?"speichern 1=ja 3=nein":"store 1=yes 3=back"));
+               set_tone_vbox("store_ask");
+               break;
+
+               case '9': /* delete file */
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) entering the delete menu\n", ea_endpoint->ep_serial);
+               if (e_vbox_play >= e_vbox_index_num)
+                       goto no_messages;
+               if (e_vbox_index_num == 0) /* nothing to play */
+               {
+                       goto no_calls;
+               }
+               e_vbox_state = VBOX_STATE_DELETE_ASK;
+               SCPY(e_vbox_display, (char *)((language)?"loeschen 1=ja 3=nein":"delete 1=yes 3=back"));
+               set_tone_vbox("delete_ask");
+               break;
+
+
+               /* process the menu */
+               case '#':
+               if (e_vbox_menu < 0)
+                       e_vbox_menu = 0;
+               else
+                       e_vbox_menu++;
+               if (vbox_menu[e_vbox_menu].english == NULL)
+                       e_vbox_menu = 0;
+               /* show menu */
+               show_menu:
+               SPRINT(e_vbox_display, "%c: %s", vbox_menu[e_vbox_menu].digit, (language)?vbox_menu[e_vbox_menu].german:vbox_menu[e_vbox_menu].english);
+               break;
+
+               case '0':
+               if (e_vbox_menu < 0) /* only if menu selection is pressed before*/
+               {
+                       /* call if phonenumber is given */
+                       if (e_vbox_index_num)
+                       if (e_vbox_index_callerid[0]!='\0' && !!strcmp(e_vbox_index_callerid,"anonymous") && !!strcmp(e_vbox_index_callerid,"unknown"))
+                       {
+                               set_tone(portlist, "dialing");
+                               SPRINT(e_dialinginfo.number, "extern:%s", e_vbox_index_callerid);
+                               e_extdialing = e_dialinginfo.number;
+                               e_action = NULL;
+                               process_dialing();
+                               return;
+                       }
+                       break;
+               }
+               e_extdialing[0] = vbox_menu[e_vbox_menu].digit;
+               e_extdialing[1] = '\0';
+               e_vbox_menu = -1;
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) executing selected menu:%d\n", e_extdialing[0]);
+               action_dialing_vbox_play(); /* redo this method using the digit */
+               return;
+
+               case '*':
+               if (e_vbox_menu < 0)
+                       e_vbox_menu = 0;
+               else
+                       e_vbox_menu--;
+               if (e_vbox_menu < 0)
+                       while(vbox_menu[e_vbox_menu+1].english) /* jump to the end */
+                               e_vbox_menu++;
+               /* show menu */
+               goto show_menu;
+               break;
+
+               default:
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) unsupported digit '%c'\n", ea_endpoint->ep_serial, e_extdialing);
+       }
+
+       done:
+       /* reset menu after dialing a function */
+       if (e_extdialing[0]!='*' && e_extdialing[0]!='#')
+               e_vbox_menu = -1;
+
+
+       e_extdialing[0] = '\0';
+
+}
+
+
+/*
+ * this handler is called by Epoint::handler(), whenever the action is NUMB_ACTION_VBOX_PLAY
+ */
+void EndpointAppPBX::vbox_handler(void)
+{
+       /* refresh if counter changes */
+       if (e_vbox_state==VBOX_STATE_PLAY || e_vbox_state==VBOX_STATE_RECORD_PLAY)
+       if (e_vbox_counter != e_vbox_counter_last)
+       {
+               e_vbox_counter_last = e_vbox_counter;
+               e_vbox_display_refresh = 1;
+       }
+
+       /* refresh display, if required (include counter) */
+       if (e_vbox_display_refresh && e_ext.vbox_display!=VBOX_DISPLAY_OFF)
+       {
+               char counter[32];
+               struct message *message;
+
+               SPRINT(counter, "%02d:%02d", e_vbox_counter/60, e_vbox_counter%60);
+               if (e_vbox_counter_max)
+                       UPRINT(strchr(counter,'\0'), " of %02d:%02d", e_vbox_counter_max/60, e_vbox_counter_max%60);
+
+               e_vbox_display_refresh = 0;
+               message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY);
+               SPRINT(message->param.notifyinfo.display, e_vbox_display, counter);
+               PDEBUG(DEBUG_EPOINT, "EPOINT(%d) terminal %s pending display:%s\n", ea_endpoint->ep_serial, e_terminal, message->param.notifyinfo.display);
+               message_put(message);
+               logmessage(message);
+       }
+}
+
+
+/*
+ * the audio file has ended
+ * this is called by Endpoint::message_port(), whenever an audio of has been received
+ */
+void EndpointAppPBX::vbox_message_eof(void)
+{
+       char buffer[32];
+       int language = e_ext.vbox_language;
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) terminal %s end of file during state: %d\n", ea_endpoint->ep_serial, e_terminal, e_vbox_state);
+
+       switch(e_vbox_state)
+       {
+               case VBOX_STATE_MENU:
+               case VBOX_STATE_NOTHING:
+               e_vbox_state = VBOX_STATE_MENU;
+               SCPY(e_vbox_display, (char *)((language)?"druecke 2 f. wiedergabe":"press 2 to play"));
+               e_vbox_display_refresh = 1;
+               set_tone_vbox("menu");
+               break;
+
+               case VBOX_STATE_PLAY:
+               if (e_vbox_speed > 0)
+               {
+                       e_vbox_state = VBOX_STATE_MENU;
+                       SCPY(e_vbox_display, (char *)((language)?"druecke 3 f. Naechste":"press 3 for next"));
+                       e_vbox_display_refresh = 1;
+                       set_tone_vbox("menu");
+               } else
+               {
+                       /* if we have endoffile because we were playing backwards, we continue to play forward */
+                       e_vbox_speed = 1;
+                       e_vbox_counter = 1;
+                       set_play_vbox(e_vbox_index_file, e_vbox_counter);
+               }
+               break;
+
+               case VBOX_STATE_PAUSE:
+               SCPY(e_vbox_display, (char *)((language)?"druecke 2 f. weiterspielen":"press 2 to continue"));
+               e_vbox_display_refresh = 1;
+               break;
+
+               case VBOX_STATE_CALLINFO_INTRO:
+               if (e_vbox_index_mday==now_tm->tm_mday && e_vbox_index_mon==now_tm->tm_mon && e_vbox_index_year==now_tm->tm_year)
+                       goto skip_day_month;
+               e_vbox_state = VBOX_STATE_CALLINFO_MONTH; //german day
+               if (e_ext.vbox_language)
+                       /* german starts with day */
+                       SPRINT(buffer, "day_%02d", e_vbox_index_mday);
+               else
+                       /* english starts with month */
+                       SPRINT(buffer, "month_%02d", e_vbox_index_mon+1);
+               set_tone_vbox(buffer);
+               break;
+
+               case VBOX_STATE_CALLINFO_MONTH:
+               e_vbox_state = VBOX_STATE_CALLINFO_DAY; //german month
+               if (e_ext.vbox_language)
+               {
+                       /* done with month, so we send the month*/
+                       SPRINT(buffer, "month_%02d", e_vbox_index_mon+1);
+               } else
+               {
+                       /* done with day, so we send the day */
+                       SPRINT(buffer, "day_%02d", e_vbox_index_mday);
+               }
+               set_tone_vbox(buffer);
+               break;
+
+               case VBOX_STATE_CALLINFO_DAY: //german month
+               skip_day_month:
+               e_vbox_state = VBOX_STATE_CALLINFO_HOUR;
+               if (e_ext.vbox_language)
+               {
+                       if (e_vbox_index_hour == 1)
+                               SCPY(buffer, "number_ein");
+                       else
+                               SPRINT(buffer, "number_%02d", e_vbox_index_hour); /* 1-23 hours */
+               } else
+               {
+                       SPRINT(buffer, "number_%02d", ((e_vbox_index_hour+11)%12)+1); /* 12 hours am/pm */
+               }
+               set_tone_vbox(buffer);
+               break;
+
+               case VBOX_STATE_CALLINFO_HOUR:
+               e_vbox_state = VBOX_STATE_CALLINFO_OCLOCK;
+               if (e_ext.vbox_language)
+               {
+                       set_tone_vbox("oclock");
+               } else
+               {
+                       if (e_vbox_index_hour >= 12)
+                               set_tone_vbox("oclock_pm");
+                       else
+                               set_tone_vbox("oclock_am");
+               }
+               break;
+
+               case VBOX_STATE_CALLINFO_OCLOCK:
+               e_vbox_state = VBOX_STATE_CALLINFO_MIN;
+               if (e_ext.vbox_language)
+               {
+// german says "zwölfuhr und eins"
+//                     if (e_vbox_index_min == 1)
+//                             SCPY(buffer, "number_eine");
+//                     else
+                               SPRINT(buffer, "number_%02d", e_vbox_index_min); /* 1-59 minutes */
+               } else
+               {
+                       SPRINT(buffer, "number_%02d", e_vbox_index_min);
+               }
+               set_tone_vbox(buffer);
+               break;
+
+               case VBOX_STATE_CALLINFO_MIN:
+               if (e_ext.vbox_language)
+                       goto start_digits;
+               e_vbox_state = VBOX_STATE_CALLINFO_MINUTES;
+               if (e_vbox_index_mday == 1)
+                       set_tone_vbox("minute");
+               else
+                       set_tone_vbox("minutes");
+               break;
+
+               case VBOX_STATE_CALLINFO_MINUTES:
+               start_digits:
+               e_vbox_state = VBOX_STATE_CALLINFO_DIGIT;
+               if (e_vbox_index_callerid[0]=='\0' || !strcmp(e_vbox_index_callerid,"anonymous") || !strcmp(e_vbox_index_callerid,"unknown"))
+               {
+                       set_tone_vbox("call_anonymous");
+                       e_vbox_index_callerid_index = strlen(e_vbox_index_callerid);
+               } else
+               {
+                       set_tone_vbox("call_from");
+                       e_vbox_index_callerid_index = 0;
+               }
+               break;
+
+               case VBOX_STATE_CALLINFO_DIGIT:
+               while (e_vbox_index_callerid[e_vbox_index_callerid_index] && (e_vbox_index_callerid[e_vbox_index_callerid_index]<'0' || e_vbox_index_callerid[e_vbox_index_callerid_index]>'9'))
+                       e_vbox_index_callerid_index++;
+               if (e_vbox_index_callerid[e_vbox_index_callerid_index])
+               {
+                       SPRINT(buffer, "number_%02d", e_vbox_index_callerid[e_vbox_index_callerid_index]-'0');
+                       set_tone_vbox(buffer);
+                       e_vbox_index_callerid_index ++;
+               } else
+               {
+                       /* the callinfo is played, so we start with the call */
+                       e_vbox_counter = 0;
+                       e_vbox_counter_last = 0;
+                       e_vbox_counter_max = 0;
+                       e_vbox_speed = 1;
+                       e_vbox_state = VBOX_STATE_PLAY;
+                       SPRINT(e_vbox_display, "#%d %%s", e_vbox_play);
+                       if (e_ext.vbox_display == VBOX_DISPLAY_DETAILED)
+                               UPRINT(strchr(e_vbox_display,'\0'), " (%s)", e_vbox_index_callerid);
+                       e_vbox_display_refresh = 1;
+                       set_play_vbox(e_vbox_index_file, 0);
+               }
+               break;
+
+               case VBOX_STATE_RECORD_ASK:
+               set_tone_vbox("record_ask");
+               e_vbox_display_refresh = 1;
+               break;
+
+               case VBOX_STATE_STORE_ASK:
+               set_tone_vbox("store_ask");
+               e_vbox_display_refresh = 1;
+               break;
+
+               case VBOX_STATE_DELETE_ASK:
+               set_tone_vbox("delete_ask");
+               e_vbox_display_refresh = 1;
+               break;
+
+               case VBOX_STATE_RECORD_PLAY:
+               e_vbox_state = VBOX_STATE_RECORD_ASK;
+               SCPY(e_vbox_display, (char *)((language)?"1=Aufn. 2=Wied. 3=nein":"1=record 2=play 3=no"));
+               e_vbox_display_refresh = 1;
+               set_tone_vbox("record_ask");
+               break;
+
+               case VBOX_STATE_STORE_DONE:
+               case VBOX_STATE_DELETE_DONE:
+               if (e_vbox_index_num == 0) /* nothing to play */
+               {
+                       e_vbox_state = VBOX_STATE_MENU;
+                       SCPY(e_vbox_display, (char *)((language)?"keine Anrufe":"no calls"));
+                       e_vbox_display_refresh = 1;
+                       set_tone_vbox("nothing");
+               } else
+               {
+                       e_vbox_state = VBOX_STATE_MENU;
+                       SCPY(e_vbox_display, (char *)((language)?"druecke 2 f. wiedergabe":"press 2 to play"));
+                       e_vbox_display_refresh = 1;
+                       set_tone_vbox("menu");
+               }
+               break;
+
+               default:
+               PERROR("vbox_message_eof(ep%d): terminal %s unknown state: %d\n", ea_endpoint->ep_serial, e_terminal, e_vbox_state);
+       }
+}
+
+
+
+/*
+ * set the given vbox-tone with full path (without appending)
+ * the tone is played and after eof, a message is received
+ */
+void EndpointAppPBX::set_tone_vbox(char *tone)
+{
+       struct message *message;
+
+       if (tone == NULL)
+               tone = "";
+
+       if (!ea_endpoint->ep_portlist)
+       {
+               PERROR("EPOINT(%d) no portlist\n", ea_endpoint->ep_serial);
+       }
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_VBOX_TONE);
+       SCPY(message->param.tone.dir, (char *)((e_ext.vbox_language)?"vbox_german":"vbox_english"));
+       SCPY(message->param.tone.name, tone);
+       message_put(message);
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) terminal %s set tone '%s'\n", ea_endpoint->ep_serial, e_terminal, tone);
+}
+
+
+/*
+ * set the given recording file
+ * the appendix is removed
+ * the file is played and after eof, a message is received
+ * the current counter value is also received by a message
+ * set the offset in seconds of the current recording
+ */
+void EndpointAppPBX::set_play_vbox(char *file, int offset)
+{
+       char filename[256];
+       struct message *message;
+
+       SPRINT(filename, "%s/%s/%s/vbox/%s", INSTALL_DATA, options.extensions_dir, e_vbox, file);
+       
+       /* remove .wav */
+       if (!strcmp(filename+strlen(filename)-4, ".wav")) /* filename is always more than 4 digits long */
+               filename[strlen(filename)-4] = '\0';
+       else // to not check twice
+       /* remove .isdn */
+       if (!strcmp(filename+strlen(filename)-5, ".isdn")) /* filename is always more than 5 digits long */
+               filename[strlen(filename)-5] = '\0';
+
+       if (!ea_endpoint->ep_portlist)
+       {
+               PERROR("EPOINT(%d) no portlist\n", ea_endpoint->ep_serial);
+       }
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_VBOX_PLAY);
+       SCPY(message->param.play.file, filename);
+       message->param.play.offset = offset;
+       message_put(message);
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) terminal %s set play '%s'\n", ea_endpoint->ep_serial, e_terminal, filename);
+}
+
+
+/*
+ * change speed of the recording file, the default is 1
+ * negative values cause negative speed
+ */
+void EndpointAppPBX::set_play_speed(int speed)
+{
+       struct message *message;
+
+       if (!ea_endpoint->ep_portlist)
+       {
+               PERROR("EPOINT(%d) no portlist\n", ea_endpoint->ep_serial);
+       }
+       message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_VBOX_PLAY_SPEED);
+       message->param.speed = speed;
+       message_put(message);
+
+       PDEBUG(DEBUG_EPOINT, "EPOINT(%d) terminal %s set speed '%d'\n", ea_endpoint->ep_serial, e_terminal, speed);
+}
+
+
+
diff --git a/admin.h b/admin.h
new file mode 100644 (file)
index 0000000..3e7b7ed
--- /dev/null
+++ b/admin.h
@@ -0,0 +1,147 @@
+/*****************************************************************************\
+**                                                                           **
+** PBX4Linux                                                                 **
+**                                                                           **
+**---------------------------------------------------------------------------**
+** Copyright: Andreas Eversberg                                              **
+**                                                                           **
+** Administration tool header file                                           **
+**                                                                           **
+\*****************************************************************************/
+
+#define SOCKET_NAME "/var/run/PBX4Linux.socket"
+
+/* structures that define message between admin-tool and pbx */
+
+enum { /* messages */
+       ADMIN_REQUEST_CMD_INTERFACE,
+       ADMIN_RESPONSE_CMD_INTERFACE,
+       ADMIN_REQUEST_CMD_ROUTE,
+       ADMIN_RESPONSE_CMD_ROUTE,
+       ADMIN_REQUEST_CMD_DIAL,
+       ADMIN_RESPONSE_CMD_DIAL,
+       ADMIN_REQUEST_CMD_RELEASE,
+       ADMIN_RESPONSE_CMD_RELEASE,
+       ADMIN_REQUEST_STATE,
+       ADMIN_RESPONSE_STATE,
+       ADMIN_RESPONSE_S_INTERFACE,
+       ADMIN_RESPONSE_S_PORT,
+       ADMIN_RESPONSE_S_EPOINT,
+       ADMIN_RESPONSE_S_CALL,
+       ADMIN_CALL_SETUP,
+       ADMIN_CALL_SETUP_ACK,
+       ADMIN_CALL_PROCEEDING,
+       ADMIN_CALL_ALERTING,
+       ADMIN_CALL_CONNECT,
+       ADMIN_CALL_DISCONNECT,
+       ADMIN_CALL_RELEASE,
+       ADMIN_CALL_NOTIFY,
+};
+
+struct admin_response_cmd {
+       int             error;          /* error code 0 = ok*/
+       char            message[256];   /* info / response text */
+};
+
+struct admin_response_state {
+       char            version_string[64];
+       struct tm       tm;
+       char            logfile[128];
+       int             interfaces;
+       int             calls;
+       int             epoints;
+       int             ports;
+};
+
+struct admin_response_interface {
+       int             portnum;
+       char            interface_name[32];
+       int             ntmode;
+       int             ptp;
+       int             pri;
+       int             iftype;
+       int             use; /* number of ports that use this interface */
+       int             l1link; /* down(0) or up(1) */
+       int             l2link; /* down(0) or up(1) */
+       int             channels;
+       int             busy[256]; /* if port is idle(0) busy(1) */
+       unsigned long   port[256]; /* current port */
+};
+
+struct admin_response_call {
+       unsigned long   serial; /* call serial number */
+       unsigned long   partyline;
+};
+
+struct admin_response_epoint {
+       unsigned long   serial;
+       unsigned long   call; /* link to call */
+//     int             call_notify; /* if relation notified on hold */
+//     int             call_hold; /* if relation on hold */
+       int             rx_state;
+       int             tx_state;
+       int             state;
+       char            terminal[16];
+       char            callerid[64];
+       char            dialing[64];
+       char            action[32];
+       int             park; /* if parked */
+       int             park_len;
+       unsigned char   park_callid[8];
+       int             crypt; /* crypt state */
+};
+
+struct admin_response_port {
+       unsigned long   serial; /* port serial number */
+       char            name[64]; /* name of port */
+       unsigned long   epoint; /* link to epoint */
+       int             state;
+       int             isdn; /* if port is isdn */
+       int             isdn_chan; /* bchannel number */
+       int             isdn_hold; /* on hold */
+       int             isdn_ces; /* ces to use (>=0)*/
+};
+
+struct admin_call {
+       char            interface[64]; /* name of port */
+       char            callerid[64]; /* use caller id */
+       char            dialing[64]; /* number to dial */
+       int             present; /* presentation */
+       int             cause; /* cause to send */
+       int             location;
+       int             notify;
+       int             bc_capa;
+       int             bc_mode;
+       int             bc_info1;
+       int             hlc;
+       int             exthlc;
+};
+
+struct admin_message {
+       int message; /* type of admin message */
+       union u {
+               struct admin_response_cmd       x;
+               struct admin_response_state     s;
+               struct admin_response_interface i;
+               struct admin_response_port      p;
+               struct admin_response_epoint    e;
+               struct admin_response_call      c;
+               struct admin_call               call;
+       } u;
+};
+
+/* call states */
+enum {
+       ADMIN_STATE_IDLE,
+       ADMIN_STATE_IN_SETUP,
+       ADMIN_STATE_OUT_SETUP,
+       ADMIN_STATE_IN_OVERLAP,
+       ADMIN_STATE_OUT_OVERLAP,
+       ADMIN_STATE_IN_PROCEEDING,
+       ADMIN_STATE_OUT_PROCEEDING,
+       ADMIN_STATE_IN_ALERTING,
+       ADMIN_STATE_OUT_ALERTING,
+       ADMIN_STATE_CONNECT,
+       ADMIN_STATE_IN_DISCONNECT,
+       ADMIN_STATE_OUT_DISCONNECT,
+};
diff --git a/admin_client.c b/admin_client.c
new file mode 100644 (file)
index 0000000..41430f6
--- /dev/null
@@ -0,0 +1,1323 @@
+/*****************************************************************************\
+**                                                                           **
+** PBX4Linux                                                                 **
+**                                                                           **
+**---------------------------------------------------------------------------**
+** Copyright: Andreas Eversberg                                              **
+**                                                                           **
+** Administration tool                                                       **
+**                                                                           **
+\*****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <curses.h>
+#include "save.h"
+#include "call.h"
+#include "callpbx.h"
+#include "admin.h"
+#include "cause.h"
+
+#define LTEE {addch(ACS_LTEE);addch(ACS_HLINE);addch(ACS_HLINE);}
+#define LLCORNER {addch(ACS_LLCORNER);addch(ACS_HLINE);addch(ACS_HLINE);}
+#define VLINE {addch(ACS_VLINE);addstr("  ");}
+#define EMPTY {addstr("   ");}
+//char rotator[] = {'-', '\\', '|', '/'};
+int    lastlines, lastcols;
+int    show_interfaces = 2,
+       show_calls = 1,
+       show_log = 1;
+
+enum {
+       MODE_STATE,
+       MODE_INTERFACE,
+       MODE_ROUTE,
+       MODE_DIAL,
+       MODE_RELEASE,
+       MODE_TESTCALL,
+};
+
+char *text_interfaces[] = {
+       "off",
+       "brief",
+       "active channels",
+       "all channels",
+};
+
+char *text_calls[] = {
+       "off",
+       "brief",
+       "structured",
+};
+
+char   red = 1,
+       green = 2,
+       yellow = 3,
+       blue = 4,
+       mangenta = 5,
+       cyan = 6,
+       white = 7;
+
+#define LOGLINES 128
+char logline[LOGLINES][256];
+unsigned long logcur = 0;
+int logfh = -1;
+char logfile[128];
+
+/*
+ * curses
+ */
+void init_curses(void)
+{
+       /* init curses */
+        initscr(); cbreak(); noecho();
+        start_color();
+        nodelay(stdscr, TRUE);
+        if (COLOR_PAIRS>=8 && COLORS>=8)
+        {
+                init_pair(1,1,0);
+                init_pair(2,2,0);
+                init_pair(3,3,0);
+                init_pair(4,4,0);
+                init_pair(5,5,0);
+                init_pair(6,6,0);
+                init_pair(7,7,0);
+        }
+       lastlines = LINES;
+       lastcols = COLS;
+}
+
+void cleanup_curses(void)
+{
+        endwin();
+}
+
+void color(int color)
+{
+        if (COLOR_PAIRS>=8 && COLORS>=8)
+               attrset(COLOR_PAIR(color));
+}
+
+/*
+ * permanently show current state using ncurses
+ */
+int debug_port(struct admin_message *msg, struct admin_message *m, int line, int i, int vline)
+{
+       char buffer[256];
+
+       color(white);
+       addstr("PORT:");
+       color(yellow);
+       SPRINT(buffer,"%s(%d)", m[i].u.p.name,m[i].u.p.serial);
+       addstr(buffer);
+       color(cyan);
+       addstr(" state=");
+       switch (m[i].u.p.state)
+       {
+               case ADMIN_STATE_IDLE:
+               color(red);
+               addstr("'idle'");
+               break;
+               case ADMIN_STATE_IN_SETUP:
+               color(red);
+               addstr("'in << setup'");
+               break;
+               case ADMIN_STATE_OUT_SETUP:
+               color(red);
+               addstr("'out >> setup'");
+               break;
+               case ADMIN_STATE_IN_OVERLAP:
+               color(yellow);
+               addstr("'in << overlap'");
+               break;
+               case ADMIN_STATE_OUT_OVERLAP:
+               color(yellow);
+               addstr("'out >> overlap'");
+               break;
+               case ADMIN_STATE_IN_PROCEEDING:
+               color(mangenta);
+               addstr("'in << proc'");
+               break;
+               case ADMIN_STATE_OUT_PROCEEDING:
+               color(mangenta);
+               addstr("'out >> proc'");
+               break;
+               case ADMIN_STATE_IN_ALERTING:
+               color(cyan);
+               addstr("'in << alert'");
+               break;
+               case ADMIN_STATE_OUT_ALERTING:
+               color(cyan);
+               addstr("'out >> alert'");
+               break;
+               case ADMIN_STATE_CONNECT:
+               color(white);
+               addstr("'connect'");
+               break;
+               case ADMIN_STATE_IN_DISCONNECT:
+               color(blue);
+               addstr("'in  << disc'");
+               break;
+               case ADMIN_STATE_OUT_DISCONNECT:
+               color(blue);
+               addstr("'out >> disc'");
+               break;
+               default:
+               color(blue);
+               addstr("'--NONE--'");
+       }
+
+       if (m[i].u.p.isdn)
+       {       
+               color(cyan);
+               addstr(" bchannel=");
+               color(white);
+               SPRINT(buffer,"%d", m[i].u.p.isdn_chan);
+               addstr(buffer);
+               if (m[i].u.p.isdn_ces >= 0)
+               {
+                       color(cyan);
+                       addstr(" ces=");
+                       color(yellow);
+                       SPRINT(buffer, "%d", m[i].u.p.isdn_ces);
+                       addstr(buffer);
+               }
+               if (m[i].u.p.isdn_hold)
+               {
+                       color(red);
+                       addstr(" hold");
+               }
+       }
+
+       return(line);
+}
+int debug_epoint(struct admin_message *msg, struct admin_message *m, int line, int i, int vline)
+{
+       unsigned long epoint = m[i].u.e.serial;
+       char buffer[256];
+       unsigned char c;
+       int j, jj;
+       int ltee;
+
+       color(white);
+       SPRINT(buffer,"EPOINT(%d)", epoint);
+       addstr(buffer);
+       color(cyan);
+       addstr(" state=");
+       switch (m[i].u.e.state)
+       {
+               case ADMIN_STATE_IDLE:
+               color(red);
+               addstr("'idle'");
+               break;
+               case ADMIN_STATE_IN_SETUP:
+               color(red);
+               addstr("'in << setup'");
+               break;
+               case ADMIN_STATE_OUT_SETUP:
+               color(red);
+               addstr("'out >> setup'");
+               break;
+               case ADMIN_STATE_IN_OVERLAP:
+               color(yellow);
+               addstr("'in << overlap'");
+               break;
+               case ADMIN_STATE_OUT_OVERLAP:
+               color(yellow);
+               addstr("'out >> overlap'");
+               break;
+               case ADMIN_STATE_IN_PROCEEDING:
+               color(mangenta);
+               addstr("'in << proc'");
+               break;
+               case ADMIN_STATE_OUT_PROCEEDING:
+               color(mangenta);
+               addstr("'out >> proc'");
+               break;
+               case ADMIN_STATE_IN_ALERTING:
+               color(cyan);
+               addstr("'in << alert'");
+               break;
+               case ADMIN_STATE_OUT_ALERTING:
+               color(cyan);
+               addstr("'out >> alert'");
+               break;
+               case ADMIN_STATE_CONNECT:
+               color(white);
+               addstr("'connect'");
+               break;
+               case ADMIN_STATE_IN_DISCONNECT:
+               color(blue);
+               addstr("'in  << disc'");
+               break;
+               case ADMIN_STATE_OUT_DISCONNECT:
+               color(blue);
+               addstr("'out >> disc'");
+               break;
+               default:
+               color(blue);
+               addstr("'--NONE--'");
+       }
+       if (m[i].u.e.terminal[0])
+       {
+               color(cyan);
+               addstr(" terminal=");
+               color(green);
+               addstr(m[i].u.e.terminal);
+       }
+       color(white);
+       SPRINT(buffer, " %s", m[i].u.e.callerid);
+       addstr(buffer);
+       color(cyan);
+       addstr("->");
+       color(white);
+       addstr(m[i].u.e.dialing);
+       if (m[i].u.e.action[0])
+       {
+               color(cyan);
+               addstr(" action=");
+               color(yellow);
+               addstr(m[i].u.e.action);
+       }
+       if (m[i].u.e.park)
+       {
+               color(cyan);
+               addstr(" park="); /* 9 digits */
+               color(green);
+               UCPY(buffer, "\""); /* 9 digits */
+               j = 0;
+               jj = m[i].u.e.park_len;
+               while(j < jj)
+               {
+                       c = m[i].u.e.park_callid[j];
+                       if (c >= 32 && c < 127 && c != '[')
+                       {
+                               SCCAT(buffer, c);
+                       } else
+                               UPRINT(buffer+strlen(buffer), "[%02x]", c);
+                       j++;
+               }
+               SCAT(buffer, "\"");
+               addstr(buffer);
+       } else
+       {
+               color(red);
+               switch(m[i].u.e.rx_state)
+               {
+                       case NOTIFY_STATE_SUSPEND:
+                       addstr(" in=suspend");
+                       break;
+                       case NOTIFY_STATE_HOLD:
+                       addstr(" in=hold");
+                       break;
+                       case NOTIFY_STATE_CONFERENCE:
+                       addstr(" in=conference");
+                       break;
+               }
+               switch(m[i].u.e.tx_state)
+               {
+                       case NOTIFY_STATE_SUSPEND:
+                       addstr(" out=suspend");
+                       break;
+                       case NOTIFY_STATE_HOLD:
+                       addstr(" out=hold");
+                       break;
+                       case NOTIFY_STATE_CONFERENCE:
+                       addstr(" out=conference");
+                       break;
+               }
+       }
+       if (m[i].u.e.crypt)
+       {
+               color(cyan);
+               addstr(" crypt=");
+               if (m[i].u.e.crypt) /* crypt on */
+               {
+                       color(green);
+                       addstr("active");
+               } else
+               {
+                       color(yellow);
+                       addstr("pending");
+               }
+       }
+       /* loop all related ports */
+       ltee = 0;
+       j = msg->u.s.interfaces+msg->u.s.calls+msg->u.s.epoints;
+       jj = j + msg->u.s.ports;
+       while(j < jj)
+       {
+               if (m[j].u.p.epoint == epoint)
+               {
+                       color(cyan);
+                       move(++line>1?line:1, 1);
+                       if (vline)
+                               VLINE
+                       else
+                               EMPTY
+                       move(line>1?line:1, 5);
+                       LTEE
+                       ltee = line;
+                       move(line>1?line:1, 8);
+                       if (line+2 >= LINES) break;
+                       line = debug_port(msg, m, line, j, vline);
+                       if (line+2 >= LINES) break;
+               }
+               j++;
+       }
+       if (ltee)
+       {
+               color(cyan);
+               move(ltee>1?line:1, 5);
+               LLCORNER
+       }
+
+       return(line);
+}
+int debug_call(struct admin_message *msg, struct admin_message *m, int line, int i)
+{
+       unsigned long   call = m[i].u.c.serial;
+       char            buffer[256];
+       int             j, jj;
+
+       color(white);
+       SPRINT(buffer,"CALL(%d)", call);
+       addstr(buffer);
+       if (m[i].u.c.partyline)
+       {
+               color(cyan);
+               addstr(" partyline=");
+               color(white);
+               SPRINT(buffer, "%d\n", m[i].u.c.partyline);
+               addstr(buffer);
+       }
+       /* find number of epoints */
+       j = msg->u.s.interfaces+msg->u.s.calls;
+       jj = j + msg->u.s.epoints;
+       i = 0;
+       while(j < jj)
+       {
+               if (m[j].u.e.call == call)
+                       i++;
+               j++;
+       }
+       /* loop all related endpoints */
+       j = msg->u.s.interfaces+msg->u.s.calls;
+       jj = j + msg->u.s.epoints;
+       while(j < jj)
+       {
+               if (m[j].u.e.call == call)
+               {
+                       i--;
+                       move(++line>1?line:1, 1);
+                       color(cyan);
+         &