diff options
Diffstat (limited to '1.4.23-rc4/channels')
54 files changed, 0 insertions, 94470 deletions
diff --git a/1.4.23-rc4/channels/DialTone.h b/1.4.23-rc4/channels/DialTone.h deleted file mode 100644 index 098ed44d7..000000000 --- a/1.4.23-rc4/channels/DialTone.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 8-bit raw data - * - * Source: DialTone.ulaw - * - * Copyright (C) 1999, Mark Spencer - * - * Distributed under the terms of the GNU General Public License - * - */ - -static unsigned char DialTone[] = { -0xff, 0xab, 0x9d, 0x96, 0x91, 0x90, 0x91, 0x96, 0x9c, 0xaa, -0xd9, 0x2f, 0x1f, 0x19, 0x15, 0x14, 0x15, 0x19, 0x1f, 0x2c, -0x4e, 0xb9, 0xa7, 0x9f, 0x9c, 0x9b, 0x9c, 0x9f, 0xa7, 0xb3, -0xcf, 0x47, 0x34, 0x2d, 0x2a, 0x2a, 0x2c, 0x31, 0x3a, 0x47, -0x5f, 0xe4, 0xd8, 0xd9, 0xe9, 0x64, 0x4f, 0x49, 0x46, 0x49, -0x58, 0xde, 0xc2, 0xb5, 0xad, 0xa8, 0xa6, 0xa6, 0xa9, 0xaf, -0xbf, 0x56, 0x32, 0x26, 0x1e, 0x1b, 0x19, 0x1a, 0x1d, 0x24, -0x33, 0xdd, 0xad, 0x9f, 0x98, 0x94, 0x92, 0x93, 0x97, 0x9e, -0xac, 0xf8, 0x2c, 0x1d, 0x16, 0x11, 0xf, 0x10, 0x15, 0x1b, -0x29, 0x55, 0xae, 0x9e, 0x97, 0x92, 0x90, 0x91, 0x95, 0x9c, -0xa8, 0xca, 0x35, 0x22, 0x1a, 0x16, 0x15, 0x16, 0x19, 0x1f, -0x2b, 0x47, 0xbe, 0xab, 0xa2, 0x9e, 0x9d, 0x9e, 0xa2, 0xa9, -0xb4, 0xcc, 0x4f, 0x3a, 0x32, 0x2f, 0x2f, 0x32, 0x39, 0x41, -0x4f, 0x67, 0xf5, 0xf5, 0x67, 0x51, 0x46, 0x3e, 0x3b, 0x3b, -0x3f, 0x4d, 0xe2, 0xbe, 0xb0, 0xa9, 0xa4, 0xa1, 0xa1, 0xa5, -0xab, 0xba, 0x64, 0x33, 0x25, 0x1d, 0x19, 0x17, 0x18, 0x1b, -0x20, 0x2e, 0x72, 0xae, 0x9f, 0x98, 0x94, 0x91, 0x92, 0x96, -0x9c, 0xa9, 0xd4, 0x2f, 0x1e, 0x17, 0x11, 0xf, 0x10, 0x14, -0x1a, 0x26, 0x48, 0xb2, 0x9f, 0x98, 0x93, 0x91, 0x92, 0x95, -0x9b, 0xa7, 0xc1, 0x3a, 0x25, 0x1c, 0x18, 0x16, 0x17, 0x1a, -0x1f, 0x2b, 0x42, 0xc6, 0xae, 0xa6, 0xa0, 0x9f, 0xa0, 0xa5, -0xab, 0xb6, 0xcb, 0x5a, 0x40, 0x39, 0x36, 0x37, 0x3b, 0x43, -0x4e, 0x60, 0x7b, 0x7c, 0x60, 0x4e, 0x41, 0x3a, 0x34, 0x32, -0x33, 0x39, 0x45, 0xed, 0xbd, 0xae, 0xa6, 0xa0, 0x9e, 0x9e, -0xa0, 0xa8, 0xb4, 0xef, 0x34, 0x24, 0x1c, 0x18, 0x16, 0x16, -0x19, 0x1e, 0x2b, 0x54, 0xb1, 0x9f, 0x98, 0x93, 0x91, 0x91, -0x95, 0x9b, 0xa6, 0xc6, 0x33, 0x1f, 0x17, 0x12, 0xf, 0x10, -0x13, 0x1a, 0x24, 0x3e, 0xb8, 0xa2, 0x99, 0x94, 0x92, 0x92, -0x96, 0x9b, 0xa6, 0xbc, 0x40, 0x29, 0x1e, 0x1a, 0x18, 0x19, -0x1b, 0x20, 0x2b, 0x3f, 0xcf, 0xb3, 0xa9, 0xa5, 0xa3, 0xa4, -0xa8, 0xae, 0xb9, 0xcc, 0x67, 0x49, 0x40, 0x3f, 0x42, 0x4a, -0x59, 0x79, 0xe5, 0xe4, 0x7a, 0x54, 0x43, 0x39, 0x31, 0x2d, -0x2c, 0x2d, 0x32, 0x3e, 0x71, 0xbd, 0xad, 0xa4, 0x9e, 0x9c, -0x9c, 0x9e, 0xa4, 0xaf, 0xd7, 0x36, 0x24, 0x1c, 0x17, 0x15, -0x15, 0x17, 0x1d, 0x28, 0x47, 0xb5, 0xa1, 0x98, 0x93, 0x90, -0x90, 0x93, 0x99, 0xa4, 0xbd, 0x38, 0x21, 0x18, 0x13, 0x10, -0x10, 0x13, 0x19, 0x22, 0x39, 0xbe, 0xa5, 0x9b, 0x96, 0x93, -0x93, 0x96, 0x9b, 0xa5, 0xb9, 0x4a, 0x2c, 0x20, 0x1c, 0x1a, -0x1a, 0x1d, 0x22, 0x2c, 0x3d, 0xdc, 0xb8, 0xad, 0xa9, 0xa8, -0xa9, 0xac, 0xb2, 0xbd, 0xce, 0x78, 0x54, 0x4d, 0x4f, 0x5a, -0xff, 0xda, 0xcf, 0xcd, 0xd4, 0xf8, 0x4e, 0x3d, 0x32, 0x2c, -0x29, 0x28, 0x29, 0x2d, 0x38, 0x5c, 0xbd, 0xac, 0xa2, 0x9d, -0x9a, 0x9a, 0x9c, 0xa0, 0xac, 0xca, 0x39, 0x25, 0x1b, 0x16, -0x13, 0x13, 0x16, 0x1b, 0x25, 0x3e, 0xb9, 0xa2, 0x99, 0x93, -0x90, 0x90, 0x93, 0x98, 0xa1, 0xb8, 0x3d, 0x24, 0x19, 0x13, -0x10, 0x10, 0x13, 0x18, 0x21, 0x35, 0xc7, 0xa8, 0x9d, 0x97, -0x95, 0x95, 0x97, 0x9c, 0xa4, 0xb6, 0x57, 0x2f, 0x24, 0x1e, -0x1c, 0x1c, 0x1e, 0x24, 0x2d, 0x3d, 0xf1, 0xbe, 0xb2, 0xad, -0xac, 0xad, 0xb1, 0xb9, 0xc3, 0xd4, 0xfa, 0x64, 0x65, 0xf9, -0xd9, 0xca, 0xc2, 0xbf, 0xc0, 0xc9, 0xe7, 0x4c, 0x39, 0x2e, -0x28, 0x24, 0x23, 0x25, 0x29, 0x33, 0x4f, 0xbf, 0xab, 0xa0, -0x9b, 0x99, 0x98, 0x9a, 0x9e, 0xa9, 0xc0, 0x3c, 0x26, 0x1b, -0x16, 0x12, 0x12, 0x14, 0x19, 0x22, 0x38, 0xbe, 0xa4, 0x9a, -0x93, 0x90, 0x8f, 0x92, 0x97, 0x9f, 0xb3, 0x46, 0x26, 0x1b, -0x15, 0x11, 0x11, 0x13, 0x18, 0x1f, 0x31, 0xd4, 0xab, 0x9e, -0x99, 0x96, 0x96, 0x98, 0x9c, 0xa4, 0xb4, 0x6f, 0x34, 0x28, -0x20, 0x1e, 0x1e, 0x20, 0x26, 0x2e, 0x3d, 0x6d, 0xc5, 0xb9, -0xb3, 0xb2, 0xb4, 0xba, 0xc1, 0xcd, 0xe0, 0xfc, 0xfb, 0xe0, -0xce, 0xc3, 0xbb, 0xb7, 0xb6, 0xb9, 0xc0, 0xda, 0x4b, 0x36, -0x2b, 0x25, 0x20, 0x1f, 0x20, 0x26, 0x2e, 0x46, 0xc2, 0xab, -0x9f, 0x9a, 0x97, 0x96, 0x98, 0x9c, 0xa5, 0xba, 0x41, 0x27, -0x1b, 0x15, 0x12, 0x11, 0x13, 0x18, 0x1f, 0x32, 0xc8, 0xa6, -0x9a, 0x94, 0x90, 0x8f, 0x91, 0x97, 0x9e, 0xaf, 0x54, 0x29, -0x1c, 0x16, 0x12, 0x11, 0x14, 0x18, 0x1f, 0x2e, 0xf2, 0xae, -0xa0, 0x9b, 0x98, 0x97, 0x99, 0x9d, 0xa5, 0xb3, 0xe4, 0x3a, -0x2b, 0x25, 0x21, 0x21, 0x24, 0x29, 0x30, 0x3e, 0x62, 0xcd, -0xbf, 0xbb, 0xbb, 0xbe, 0xc6, 0xd1, 0xe7, 0x76, 0x75, 0xe7, -0xcf, 0xc1, 0xb9, 0xb2, 0xaf, 0xaf, 0xb2, 0xba, 0xcf, 0x4c, -0x34, 0x29, 0x22, 0x1e, 0x1d, 0x1e, 0x22, 0x2b, 0x3e, 0xc7, -0xab, 0x9f, 0x99, 0x96, 0x95, 0x96, 0x9a, 0xa2, 0xb5, 0x4a, -0x28, 0x1c, 0x15, 0x11, 0x10, 0x12, 0x17, 0x1e, 0x2e, 0xd5, -0xa9, 0x9b, 0x95, 0x90, 0x8f, 0x91, 0x96, 0x9d, 0xac, 0x78, -0x2c, 0x1e, 0x17, 0x13, 0x12, 0x14, 0x18, 0x1f, 0x2d, 0x5d, -0xb3, 0xa4, 0x9d, 0x9a, 0x99, 0x9b, 0x9e, 0xa6, 0xb2, 0xd6, -0x3f, 0x2f, 0x29, 0x26, 0x26, 0x28, 0x2d, 0x35, 0x42, 0x5e, -0xd8, 0xca, 0xc6, 0xc9, 0xcf, 0xe4, 0x69, 0x59, 0x58, 0x64, -0xdf, 0xc7, 0xba, 0xb1, 0xac, 0xaa, 0xaa, 0xad, 0xb4, 0xc7, -0x4f, 0x33, 0x27, 0x1f, 0x1c, 0x1b, 0x1c, 0x1f, 0x27, 0x39, -0xce, 0xac, 0x9f, 0x99, 0x95, 0x94, 0x95, 0x99, 0x9f, 0xaf, -0x59, 0x2a, 0x1c, 0x16, 0x11, 0x10, 0x11, 0x16, 0x1d, 0x2b, -0xff, 0xab, 0x9d, 0x96, 0x91, 0x90, 0x91, 0x96, 0x9c, 0xaa, -0xd9, 0x2f, 0x1f, 0x19, 0x15, 0x14, 0x15, 0x19, 0x1f, 0x2c, -0x4e, 0xb9, 0xa7, 0x9f, 0x9c, 0x9b, 0x9c, 0x9f, 0xa7, 0xb3, -0xcf, 0x47, 0x34, 0x2d, 0x2a, 0x2a, 0x2c, 0x31, 0x3a, 0x47, -0x5f, 0xe4, 0xd8, 0xd9, 0xe9, 0x64, 0x4f, 0x49, 0x46, 0x49, -0x58, 0xde, 0xc2, 0xb5, 0xad, 0xa8, 0xa6, 0xa6, 0xa9, 0xaf, -0xbf, 0x56, 0x32, 0x26, 0x1e, 0x1b, 0x19, 0x1a, 0x1d, 0x24, -0x33, 0xdd, 0xad, 0x9f, 0x98, 0x94, 0x92, 0x93, 0x97, 0x9e, -0xac, 0xf8, 0x2c, 0x1d, 0x16, 0x11, 0xf, 0x10, 0x15, 0x1b, -0x29, 0x55, 0xae, 0x9e, 0x97, 0x92, 0x90, 0x91, 0x95, 0x9c, -0xa8, 0xca, 0x35, 0x22, 0x1a, 0x16, 0x15, 0x16, 0x19, 0x1f, -0x2b, 0x47, 0xbe, 0xab, 0xa2, 0x9e, 0x9d, 0x9e, 0xa2, 0xa9, -0xb4, 0xcc, 0x4f, 0x3a, 0x32, 0x2f, 0x2f, 0x32, 0x39, 0x41, -0x4f, 0x67, 0xf5, 0xf5, 0x67, 0x51, 0x46, 0x3e, 0x3b, 0x3b, -0x3f, 0x4d, 0xe2, 0xbe, 0xb0, 0xa9, 0xa4, 0xa1, 0xa1, 0xa5, -0xab, 0xba, 0x64, 0x33, 0x25, 0x1d, 0x19, 0x17, 0x18, 0x1b, -0x20, 0x2e, 0x72, 0xae, 0x9f, 0x98, 0x94, 0x91, 0x92, 0x96, -0x9c, 0xa9, 0xd4, 0x2f, 0x1e, 0x17, 0x11, 0xf, 0x10, 0x14, -0x1a, 0x26, 0x48, 0xb2, 0x9f, 0x98, 0x93, 0x91, 0x92, 0x95, -0x9b, 0xa7, 0xc1, 0x3a, 0x25, 0x1c, 0x18, 0x16, 0x17, 0x1a, -0x1f, 0x2b, 0x42, 0xc6, 0xae, 0xa6, 0xa0, 0x9f, 0xa0, 0xa5, -0xab, 0xb6, 0xcb, 0x5a, 0x40, 0x39, 0x36, 0x37, 0x3b, 0x43, -0x4e, 0x60, 0x7b, 0x7c, 0x60, 0x4e, 0x41, 0x3a, 0x34, 0x32, -0x33, 0x39, 0x45, 0xed, 0xbd, 0xae, 0xa6, 0xa0, 0x9e, 0x9e, -0xa0, 0xa8, 0xb4, 0xef, 0x34, 0x24, 0x1c, 0x18, 0x16, 0x16, -0x19, 0x1e, 0x2b, 0x54, 0xb1, 0x9f, 0x98, 0x93, 0x91, 0x91, -0x95, 0x9b, 0xa6, 0xc6, 0x33, 0x1f, 0x17, 0x12, 0xf, 0x10, -0x13, 0x1a, 0x24, 0x3e, 0xb8, 0xa2, 0x99, 0x94, 0x92, 0x92, -0x96, 0x9b, 0xa6, 0xbc, 0x40, 0x29, 0x1e, 0x1a, 0x18, 0x19, -0x1b, 0x20, 0x2b, 0x3f, 0xcf, 0xb3, 0xa9, 0xa5, 0xa3, 0xa4, -0xa8, 0xae, 0xb9, 0xcc, 0x67, 0x49, 0x40, 0x3f, 0x42, 0x4a, -0x59, 0x79, 0xe5, 0xe4, 0x7a, 0x54, 0x43, 0x39, 0x31, 0x2d, -0x2c, 0x2d, 0x32, 0x3e, 0x71, 0xbd, 0xad, 0xa4, 0x9e, 0x9c, -0x9c, 0x9e, 0xa4, 0xaf, 0xd7, 0x36, 0x24, 0x1c, 0x17, 0x15, -0x15, 0x17, 0x1d, 0x28, 0x47, 0xb5, 0xa1, 0x98, 0x93, 0x90, -0x90, 0x93, 0x99, 0xa4, 0xbd, 0x38, 0x21, 0x18, 0x13, 0x10, -0x10, 0x13, 0x19, 0x22, 0x39, 0xbe, 0xa5, 0x9b, 0x96, 0x93, -0x93, 0x96, 0x9b, 0xa5, 0xb9, 0x4a, 0x2c, 0x20, 0x1c, 0x1a, -0x1a, 0x1d, 0x22, 0x2c, 0x3d, 0xdc, 0xb8, 0xad, 0xa9, 0xa8, -0xa9, 0xac, 0xb2, 0xbd, 0xce, 0x78, 0x54, 0x4d, 0x4f, 0x5a, -0xff, 0xda, 0xcf, 0xcd, 0xd4, 0xf8, 0x4e, 0x3d, 0x32, 0x2c, -0x29, 0x28, 0x29, 0x2d, 0x38, 0x5c, 0xbd, 0xac, 0xa2, 0x9d, -0x9a, 0x9a, 0x9c, 0xa0, 0xac, 0xca, 0x39, 0x25, 0x1b, 0x16, -0x13, 0x13, 0x16, 0x1b, 0x25, 0x3e, 0xb9, 0xa2, 0x99, 0x93, -0x90, 0x90, 0x93, 0x98, 0xa1, 0xb8, 0x3d, 0x24, 0x19, 0x13, -0x10, 0x10, 0x13, 0x18, 0x21, 0x35, 0xc7, 0xa8, 0x9d, 0x97, -0x95, 0x95, 0x97, 0x9c, 0xa4, 0xb6, 0x57, 0x2f, 0x24, 0x1e, -0x1c, 0x1c, 0x1e, 0x24, 0x2d, 0x3d, 0xf1, 0xbe, 0xb2, 0xad, -0xac, 0xad, 0xb1, 0xb9, 0xc3, 0xd4, 0xfa, 0x64, 0x65, 0xf9, -0xd9, 0xca, 0xc2, 0xbf, 0xc0, 0xc9, 0xe7, 0x4c, 0x39, 0x2e, -0x28, 0x24, 0x23, 0x25, 0x29, 0x33, 0x4f, 0xbf, 0xab, 0xa0, -0x9b, 0x99, 0x98, 0x9a, 0x9e, 0xa9, 0xc0, 0x3c, 0x26, 0x1b, -0x16, 0x12, 0x12, 0x14, 0x19, 0x22, 0x38, 0xbe, 0xa4, 0x9a, -0x93, 0x90, 0x8f, 0x92, 0x97, 0x9f, 0xb3, 0x46, 0x26, 0x1b, -0x15, 0x11, 0x11, 0x13, 0x18, 0x1f, 0x31, 0xd4, 0xab, 0x9e, -0x99, 0x96, 0x96, 0x98, 0x9c, 0xa4, 0xb4, 0x6f, 0x34, 0x28, -0x20, 0x1e, 0x1e, 0x20, 0x26, 0x2e, 0x3d, 0x6d, 0xc5, 0xb9, -0xb3, 0xb2, 0xb4, 0xba, 0xc1, 0xcd, 0xe0, 0xfc, 0xfb, 0xe0, -0xce, 0xc3, 0xbb, 0xb7, 0xb6, 0xb9, 0xc0, 0xda, 0x4b, 0x36, -0x2b, 0x25, 0x20, 0x1f, 0x20, 0x26, 0x2e, 0x46, 0xc2, 0xab, -0x9f, 0x9a, 0x97, 0x96, 0x98, 0x9c, 0xa5, 0xba, 0x41, 0x27, -0x1b, 0x15, 0x12, 0x11, 0x13, 0x18, 0x1f, 0x32, 0xc8, 0xa6, -0x9a, 0x94, 0x90, 0x8f, 0x91, 0x97, 0x9e, 0xaf, 0x54, 0x29, -0x1c, 0x16, 0x12, 0x11, 0x14, 0x18, 0x1f, 0x2e, 0xf2, 0xae, -0xa0, 0x9b, 0x98, 0x97, 0x99, 0x9d, 0xa5, 0xb3, 0xe4, 0x3a, -0x2b, 0x25, 0x21, 0x21, 0x24, 0x29, 0x30, 0x3e, 0x62, 0xcd, -0xbf, 0xbb, 0xbb, 0xbe, 0xc6, 0xd1, 0xe7, 0x76, 0x75, 0xe7, -0xcf, 0xc1, 0xb9, 0xb2, 0xaf, 0xaf, 0xb2, 0xba, 0xcf, 0x4c, -0x34, 0x29, 0x22, 0x1e, 0x1d, 0x1e, 0x22, 0x2b, 0x3e, 0xc7, -0xab, 0x9f, 0x99, 0x96, 0x95, 0x96, 0x9a, 0xa2, 0xb5, 0x4a, -0x28, 0x1c, 0x15, 0x11, 0x10, 0x12, 0x17, 0x1e, 0x2e, 0xd5, -0xa9, 0x9b, 0x95, 0x90, 0x8f, 0x91, 0x96, 0x9d, 0xac, 0x78, -0x2c, 0x1e, 0x17, 0x13, 0x12, 0x14, 0x18, 0x1f, 0x2d, 0x5d, -0xb3, 0xa4, 0x9d, 0x9a, 0x99, 0x9b, 0x9e, 0xa6, 0xb2, 0xd6, -0x3f, 0x2f, 0x29, 0x26, 0x26, 0x28, 0x2d, 0x35, 0x42, 0x5e, -0xd8, 0xca, 0xc6, 0xc9, 0xcf, 0xe4, 0x69, 0x59, 0x58, 0x64, -0xdf, 0xc7, 0xba, 0xb1, 0xac, 0xaa, 0xaa, 0xad, 0xb4, 0xc7, -0x4f, 0x33, 0x27, 0x1f, 0x1c, 0x1b, 0x1c, 0x1f, 0x27, 0x39, -0xce, 0xac, 0x9f, 0x99, 0x95, 0x94, 0x95, 0x99, 0x9f, 0xaf, -0x59, 0x2a, 0x1c, 0x16, 0x11, 0x10, 0x11, 0x16, 0x1d, 0x2b, -0xff, 0xab, 0x9d, 0x96, 0x91, 0x90, 0x91, 0x96, 0x9c, 0xaa, -0xd9, 0x2f, 0x1f, 0x19, 0x15, 0x14, 0x15, 0x19, 0x1f, 0x2c, -0x4e, 0xb9, 0xa7, 0x9f, 0x9c, 0x9b, 0x9c, 0x9f, 0xa7, 0xb3, -0xcf, 0x47, 0x34, 0x2d, 0x2a, 0x2a, 0x2c, 0x31, 0x3a, 0x47, -0x5f, 0xe4, 0xd8, 0xd9, 0xe9, 0x64, 0x4f, 0x49, 0x46, 0x49, -0x58, 0xde, 0xc2, 0xb5, 0xad, 0xa8, 0xa6, 0xa6, 0xa9, 0xaf, -0xbf, 0x56, 0x32, 0x26, 0x1e, 0x1b, 0x19, 0x1a, 0x1d, 0x24, -0x33, 0xdd, 0xad, 0x9f, 0x98, 0x94, 0x92, 0x93, 0x97, 0x9e, -0xac, 0xf8, 0x2c, 0x1d, 0x16, 0x11, 0xf, 0x10, 0x15, 0x1b, -0x29, 0x55, 0xae, 0x9e, 0x97, 0x92, 0x90, 0x91, 0x95, 0x9c, -0xa8, 0xca, 0x35, 0x22, 0x1a, 0x16, 0x15, 0x16, 0x19, 0x1f, -0x2b, 0x47, 0xbe, 0xab, 0xa2, 0x9e, 0x9d, 0x9e, 0xa2, 0xa9, -0xb4, 0xcc, 0x4f, 0x3a, 0x32, 0x2f, 0x2f, 0x32, 0x39, 0x41, -0x4f, 0x67, 0xf5, 0xf5, 0x67, 0x51, 0x46, 0x3e, 0x3b, 0x3b, -0x3f, 0x4d, 0xe2, 0xbe, 0xb0, 0xa9, 0xa4, 0xa1, 0xa1, 0xa5, -0xab, 0xba, 0x64, 0x33, 0x25, 0x1d, 0x19, 0x17, 0x18, 0x1b, -0x20, 0x2e, 0x72, 0xae, 0x9f, 0x98, 0x94, 0x91, 0x92, 0x96, -0x9c, 0xa9, 0xd4, 0x2f, 0x1e, 0x17, 0x11, 0xf, 0x10, 0x14, -0x1a, 0x26, 0x48, 0xb2, 0x9f, 0x98, 0x93, 0x91, 0x92, 0x95, -0x9b, 0xa7, 0xc1, 0x3a, 0x25, 0x1c, 0x18, 0x16, 0x17, 0x1a, -0x1f, 0x2b, 0x42, 0xc6, 0xae, 0xa6, 0xa0, 0x9f, 0xa0, 0xa5, -0xab, 0xb6, 0xcb, 0x5a, 0x40, 0x39, 0x36, 0x37, 0x3b, 0x43, -0x4e, 0x60, 0x7b, 0x7c, 0x60, 0x4e, 0x41, 0x3a, 0x34, 0x32, -0x33, 0x39, 0x45, 0xed, 0xbd, 0xae, 0xa6, 0xa0, 0x9e, 0x9e, -0xa0, 0xa8, 0xb4, 0xef, 0x34, 0x24, 0x1c, 0x18, 0x16, 0x16, -0x19, 0x1e, 0x2b, 0x54, 0xb1, 0x9f, 0x98, 0x93, 0x91, 0x91, -0x95, 0x9b, 0xa6, 0xc6, 0x33, 0x1f, 0x17, 0x12, 0xf, 0x10, -0x13, 0x1a, 0x24, 0x3e, 0xb8, 0xa2, 0x99, 0x94, 0x92, 0x92, -0x96, 0x9b, 0xa6, 0xbc, 0x40, 0x29, 0x1e, 0x1a, 0x18, 0x19, -0x1b, 0x20, 0x2b, 0x3f, 0xcf, 0xb3, 0xa9, 0xa5, 0xa3, 0xa4, -0xa8, 0xae, 0xb9, 0xcc, 0x67, 0x49, 0x40, 0x3f, 0x42, 0x4a, -0x59, 0x79, 0xe5, 0xe4, 0x7a, 0x54, 0x43, 0x39, 0x31, 0x2d, -0x2c, 0x2d, 0x32, 0x3e, 0x71, 0xbd, 0xad, 0xa4, 0x9e, 0x9c, -0x9c, 0x9e, 0xa4, 0xaf, 0xd7, 0x36, 0x24, 0x1c, 0x17, 0x15, -0x15, 0x17, 0x1d, 0x28, 0x47, 0xb5, 0xa1, 0x98, 0x93, 0x90, -0x90, 0x93, 0x99, 0xa4, 0xbd, 0x38, 0x21, 0x18, 0x13, 0x10, -0x10, 0x13, 0x19, 0x22, 0x39, 0xbe, 0xa5, 0x9b, 0x96, 0x93, -0x93, 0x96, 0x9b, 0xa5, 0xb9, 0x4a, 0x2c, 0x20, 0x1c, 0x1a, -0x1a, 0x1d, 0x22, 0x2c, 0x3d, 0xdc, 0xb8, 0xad, 0xa9, 0xa8, -0xa9, 0xac, 0xb2, 0xbd, 0xce, 0x78, 0x54, 0x4d, 0x4f, 0x5a, -0xff, 0xda, 0xcf, 0xcd, 0xd4, 0xf8, 0x4e, 0x3d, 0x32, 0x2c, -0x29, 0x28, 0x29, 0x2d, 0x38, 0x5c, 0xbd, 0xac, 0xa2, 0x9d, -0x9a, 0x9a, 0x9c, 0xa0, 0xac, 0xca, 0x39, 0x25, 0x1b, 0x16, -0x13, 0x13, 0x16, 0x1b, 0x25, 0x3e, 0xb9, 0xa2, 0x99, 0x93, -0x90, 0x90, 0x93, 0x98, 0xa1, 0xb8, 0x3d, 0x24, 0x19, 0x13, -0x10, 0x10, 0x13, 0x18, 0x21, 0x35, 0xc7, 0xa8, 0x9d, 0x97, -0x95, 0x95, 0x97, 0x9c, 0xa4, 0xb6, 0x57, 0x2f, 0x24, 0x1e, -0x1c, 0x1c, 0x1e, 0x24, 0x2d, 0x3d, 0xf1, 0xbe, 0xb2, 0xad, -0xac, 0xad, 0xb1, 0xb9, 0xc3, 0xd4, 0xfa, 0x64, 0x65, 0xf9, -0xd9, 0xca, 0xc2, 0xbf, 0xc0, 0xc9, 0xe7, 0x4c, 0x39, 0x2e, -0x28, 0x24, 0x23, 0x25, 0x29, 0x33, 0x4f, 0xbf, 0xab, 0xa0, -0x9b, 0x99, 0x98, 0x9a, 0x9e, 0xa9, 0xc0, 0x3c, 0x26, 0x1b, -0x16, 0x12, 0x12, 0x14, 0x19, 0x22, 0x38, 0xbe, 0xa4, 0x9a, -0x93, 0x90, 0x8f, 0x92, 0x97, 0x9f, 0xb3, 0x46, 0x26, 0x1b, -0x15, 0x11, 0x11, 0x13, 0x18, 0x1f, 0x31, 0xd4, 0xab, 0x9e, -0x99, 0x96, 0x96, 0x98, 0x9c, 0xa4, 0xb4, 0x6f, 0x34, 0x28, -0x20, 0x1e, 0x1e, 0x20, 0x26, 0x2e, 0x3d, 0x6d, 0xc5, 0xb9, -0xb3, 0xb2, 0xb4, 0xba, 0xc1, 0xcd, 0xe0, 0xfc, 0xfb, 0xe0, -0xce, 0xc3, 0xbb, 0xb7, 0xb6, 0xb9, 0xc0, 0xda, 0x4b, 0x36, -0x2b, 0x25, 0x20, 0x1f, 0x20, 0x26, 0x2e, 0x46, 0xc2, 0xab, -0x9f, 0x9a, 0x97, 0x96, 0x98, 0x9c, 0xa5, 0xba, 0x41, 0x27, -0x1b, 0x15, 0x12, 0x11, 0x13, 0x18, 0x1f, 0x32, 0xc8, 0xa6, -0x9a, 0x94, 0x90, 0x8f, 0x91, 0x97, 0x9e, 0xaf, 0x54, 0x29, -0x1c, 0x16, 0x12, 0x11, 0x14, 0x18, 0x1f, 0x2e, 0xf2, 0xae, -0xa0, 0x9b, 0x98, 0x97, 0x99, 0x9d, 0xa5, 0xb3, 0xe4, 0x3a, -0x2b, 0x25, 0x21, 0x21, 0x24, 0x29, 0x30, 0x3e, 0x62, 0xcd, -0xbf, 0xbb, 0xbb, 0xbe, 0xc6, 0xd1, 0xe7, 0x76, 0x75, 0xe7, -0xcf, 0xc1, 0xb9, 0xb2, 0xaf, 0xaf, 0xb2, 0xba, 0xcf, 0x4c, -0x34, 0x29, 0x22, 0x1e, 0x1d, 0x1e, 0x22, 0x2b, 0x3e, 0xc7, -0xab, 0x9f, 0x99, 0x96, 0x95, 0x96, 0x9a, 0xa2, 0xb5, 0x4a, -0x28, 0x1c, 0x15, 0x11, 0x10, 0x12, 0x17, 0x1e, 0x2e, 0xd5, -0xa9, 0x9b, 0x95, 0x90, 0x8f, 0x91, 0x96, 0x9d, 0xac, 0x78, -0x2c, 0x1e, 0x17, 0x13, 0x12, 0x14, 0x18, 0x1f, 0x2d, 0x5d, -0xb3, 0xa4, 0x9d, 0x9a, 0x99, 0x9b, 0x9e, 0xa6, 0xb2, 0xd6, -0x3f, 0x2f, 0x29, 0x26, 0x26, 0x28, 0x2d, 0x35, 0x42, 0x5e, -0xd8, 0xca, 0xc6, 0xc9, 0xcf, 0xe4, 0x69, 0x59, 0x58, 0x64, -0xdf, 0xc7, 0xba, 0xb1, 0xac, 0xaa, 0xaa, 0xad, 0xb4, 0xc7, -0x4f, 0x33, 0x27, 0x1f, 0x1c, 0x1b, 0x1c, 0x1f, 0x27, 0x39, -0xce, 0xac, 0x9f, 0x99, 0x95, 0x94, 0x95, 0x99, 0x9f, 0xaf, -0x59, 0x2a, 0x1c, 0x16, 0x11, 0x10, 0x11, 0x16, 0x1d, 0x2b }; diff --git a/1.4.23-rc4/channels/Makefile b/1.4.23-rc4/channels/Makefile deleted file mode 100644 index 8f067f6b0..000000000 --- a/1.4.23-rc4/channels/Makefile +++ /dev/null @@ -1,123 +0,0 @@ -# -# Asterisk -- A telephony toolkit for Linux. -# -# Makefile for channel drivers -# -# Copyright (C) 1999-2006, Digium, Inc. -# -# This program is free software, distributed under the terms of -# the GNU General Public License -# - --include ../menuselect.makeopts ../menuselect.makedeps - -MENUSELECT_CATEGORY=CHANNELS -MENUSELECT_DESCRIPTION=Channel Drivers - -ALL_C_MODS:=$(patsubst %.c,%,$(wildcard chan_*.c)) -ALL_CC_MODS:=$(patsubst %.cc,%,$(wildcard chan_*.cc)) - -C_MODS:=$(filter-out $(MENUSELECT_CHANNELS),$(ALL_C_MODS)) -CC_MODS:=$(filter-out $(MENUSELECT_CHANNELS),$(ALL_CC_MODS)) - -ifeq ($(OSARCH),OpenBSD) - PTLIB=-lpt_OpenBSD_x86_r - H323LIB=-lh323_OpenBSD_x86_r -endif - -ifeq ($(OSARCH),linux-gnu) - PTLIB=-lpt_linux_x86_r - H323LIB=-lh323_linux_x86_r - CHANH323LIB=-ldl -endif - -ifeq ($(OSARCH),FreeBSD) - PTLIB=-lpt_FreeBSD_x86_r - H323LIB=-lh323_FreeBSD_x86_r - CHANH323LIB=-pthread -endif - -ifeq ($(OSARCH),NetBSD) - PTLIB=-lpt_NetBSD_x86_r - H323LIB=-lh323_NetBSD_x86_r -endif - -ifeq ($(wildcard h323/libchanh323.a),) - CC_MODS:=$(filter-out chan_h323,$(CC_MODS)) -endif - -ifndef OPENH323DIR - OPENH323DIR=$(HOME)/openh323 -endif - -ifndef PWLIBDIR - PWLIBDIR=$(HOME)/pwlib -endif - -LOADABLE_MODS:=$(C_MODS) $(CC_MODS) - -ifneq ($(findstring channels,$(MENUSELECT_EMBED)),) - EMBEDDED_MODS:=$(LOADABLE_MODS) - LOADABLE_MODS:= -endif - -all: _all - -include $(ASTTOPDIR)/Makefile.moddir_rules - -clean:: - rm -f gentone - $(MAKE) -C misdn clean - -ifneq ($(wildcard h323/Makefile.ast),) - include h323/Makefile.ast -H323LDFLAGS+=-Wl,--version-script=h323/noexport.map -clean:: - if [ -f h323/Makefile ]; then $(MAKE) -C h323 clean; fi -else -h323/libchanh323.a h323/Makefile.ast: - $(CMD_PREFIX) $(MAKE) -C h323 - $(CMD_PREFIX) rm -f ../main/asterisk - $(CMD_PREFIX) echo "***************************************************************" - $(CMD_PREFIX) echo - $(CMD_PREFIX) echo "********** Re-run 'make' to pick up H.323 parameters **********" - $(CMD_PREFIX) echo - $(CMD_PREFIX) echo "***************************************************************" - $(CMD_PREFIX) exit 1 -endif - -dist-clean:: - rm -f h323/Makefile - -gentone: gentone.c - $(ECHO_PREFIX) echo " [LD] $^ -> $@" - $(CMD_PREFIX) $(HOST_CC) $(STATIC_BUILD) -o $@ $(HOST_CFLAGS) $(HOST_LDFLAGS) $^ $(LIBS) -gentone: LIBS+=-lm - -busy_tone.h: - ./gentone busy_tone 480 620 - -ring_tone.h: - ./gentone ring_tone 440 480 - -$(if $(filter chan_iax2,$(EMBEDDED_MODS)),modules.link,chan_iax2.so): iax2-parser.o iax2-provision.o - -ifeq ($(OSARCH),linux-gnu) -chan_h323.so: chan_h323.o h323/libchanh323.a h323/Makefile.ast - $(ECHO_PREFIX) echo " [LD] $^ -> $@" - $(CMD_PREFIX) $(CXX) $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) $(H323LDFLAGS) -o $@ $< h323/libchanh323.a $(H323LDLIBS) -else -chan_h323.so: chan_h323.o h323/libchanh323.a - $(ECHO_PREFIX) echo " [LD] $^ -> $@" - $(CMD_PREFIX) $(CXX) $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) -o $@ $< h323/libchanh323.a $(CHANH323LIB) -L$(PWLIBDIR)/lib $(PTLIB) -L$(OPENH323DIR)/lib $(H323LIB) -L/usr/lib -lcrypto -lssl -lexpat -endif - -chan_misdn.o: ASTCFLAGS+=-Imisdn - -misdn_config.o: ASTCFLAGS+=-Imisdn - -misdn/isdn_lib.o: ASTCFLAGS+=-Wno-strict-aliasing - -misdn_config.o misdn/isdn_lib.o misdn/isdn_msg_parser.o: ASTCFLAGS+=$(MENUSELECT_OPTS_chan_misdn:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_chan_misdn),$(value $(dep)_INCLUDE)) - -$(if $(filter chan_misdn,$(EMBEDDED_MODS)),modules.link,chan_misdn.so): chan_misdn.o misdn_config.o misdn/isdn_lib.o misdn/isdn_msg_parser.o diff --git a/1.4.23-rc4/channels/answer.h b/1.4.23-rc4/channels/answer.h deleted file mode 100644 index 4168c5012..000000000 --- a/1.4.23-rc4/channels/answer.h +++ /dev/null @@ -1,237 +0,0 @@ -/*!\file - * \brief Signed 16-bit audio data - * - * Source: answer.raw - * - * Copyright (C) 1999-2005, Digium, Inc. - * - * Distributed under the terms of the GNU General Public License - * - */ - -static signed short answer[] = { -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 0x19b7, 0x0245, 0xeee5, 0xb875, 0xd9a4, 0x6018, 0x660a, 0xc3c6, -0x8741, 0xff55, 0x4c2e, 0x2146, 0xfed2, 0xf079, 0xcbd4, 0xe561, 0x3c41, 0x3166, -0xd425, 0xdc59, 0x2748, 0x087d, 0xc72b, 0xfe3a, 0x4681, 0x14c6, 0xcf45, 0xdd38, -0xf8dd, 0x0a39, 0x3a5a, 0x32b9, 0xbfec, 0x957f, 0x15a3, 0x70f4, 0x1d95, 0xbfc4, -0xd367, 0xfda0, 0x0dc0, 0x29eb, 0x1fc2, 0xd684, 0xcab1, 0x19c7, 0x29ef, 0xe679, -0xe9d0, 0x2b82, 0x151a, 0xca9f, 0xdb68, 0x1f4a, 0x271c, 0x0e2a, 0xfb32, 0xd1b2, -0xc8ff, 0x2382, 0x6380, 0x0a52, 0xa118, 0xccbf, 0x2ddc, 0x33fd, 0x0964, 0xf2a4, -0xdd81, 0xe092, 0x1a00, 0x325c, 0xf5e3, 0xd6a1, 0x0b6c, 0x1c75, 0xe4f8, 0xe07c, -0x2082, 0x2b3e, 0xf445, 0xdaa9, 0xea13, 0xff3c, 0x245c, 0x35c1, 0xf308, 0xab53, -0xdf59, 0x4698, 0x3f3b, 0xe7f7, 0xca84, 0xed4d, 0x0c3f, 0x1e94, 0x1c2d, 0xf06f, -0xd4df, 0xff34, 0x23d8, 0x001e, 0xe3f1, 0x0b15, 0x2113, 0xf3fd, 0xd768, 0xf9a0, -0x1d31, 0x1c6e, 0x0797, 0xe3a0, 0xce6c, 0xfd7b, 0x422a, 0x2c4c, 0xd364, 0xbf42, -0x0278, 0x303e, 0x1c51, 0xf737, 0xe25a, 0xe75f, 0x0a8f, 0x22ab, 0x05f4, 0xe3f9, -0xf8c4, 0x1705, 0x0162, 0xe49f, 0xfb8b, 0x1e2b, 0x13ac, 0xf044, 0xe07b, 0xf01a, -0x1567, 0x2cbf, 0x0b75, 0xd01b, 0xd206, 0x1563, 0x38d7, 0x0f2e, 0xdb32, 0xdc30, -0x023b, 0x1e44, 0x16eb, 0xf5f7, 0xe425, 0xfa33, 0x14d5, 0x0968, 0xeff2, 0xf762, -0x1137, 0x0e59, 0xf13a, 0xe651, 0xff41, 0x1d60, 0x18fd, 0xf1e6, 0xd75f, 0xf097, -0x20ec, 0x27fa, 0xfba4, 0xd5b8, 0xe68e, 0x1657, 0x2518, 0x04f6, 0xe5a3, 0xe976, -0x0578, 0x18fa, 0x0a92, 0xec0a, 0xef2a, 0x111f, 0x12f4, 0xeec3, 0xe95e, 0x0d3a, -0x18fd, 0xff72, 0xeefc, 0xf114, 0xfaaa, 0x14ee, 0x21db, 0xf56e, 0xcb49, 0xf621, -0x3323, 0x1947, 0xe017, 0xe7e9, 0x0819, 0x0707, 0x084c, 0x0f57, 0xf152, 0xdf92, -0x104a, 0x28eb, 0xedcc, 0xd4ad, 0x1415, 0x296d, 0xed9a, 0xdf57, 0x0cc2, 0x0d95, -0xf7b5, 0x0deb, 0x0b34, 0xd713, 0xea08, 0x38d6, 0x216d, 0xc727, 0xdc32, 0x2cd2, -0x1822, 0xe2d5, 0xfeb3, 0x106c, 0xe6e5, 0xf81e, 0x2fe8, 0x01af, 0xc180, 0x037a, -0x42d8, 0xf88d, 0xc344, 0x0a4f, 0x2c4e, 0xf19d, 0xebeb, 0x162c, 0xf9e9, 0xde93, -0x1b56, 0x2c60, 0xd8aa, 0xce3e, 0x2a41, 0x2eeb, 0xdab1, 0xde32, 0x1c32, 0x0aba, -0xeabe, 0x1008, 0x136d, 0xda2f, 0xec3b, 0x31dd, 0x1130, 0xca79, 0xf5b8, 0x3423, -0x0274, 0xd27d, 0x035e, 0x1e68, 0xf641, 0xf904, 0x1691, 0xef7d, 0xd57a, 0x1c3b, -0x3c23, 0xe881, 0xc274, 0x0af5, 0x2962, 0xfa34, 0xf676, 0x0f71, 0xefcc, 0xe01f, -0x19e7, 0x276f, 0xe694, 0xe134, 0x1c3a, 0x0e8b, 0xd8e7, 0xfa81, 0x2f8b, 0x07c5, -0xd904, 0xf6fa, 0x0ca5, 0xf9a2, 0x0dc7, 0x2623, 0xec54, 0xbe23, 0x02b6, 0x4296, -0x10cd, 0xda61, 0xf11c, 0x0103, 0xf41c, 0x10b4, 0x2a03, 0xf63c, 0xce1a, 0xfdbd, -0x1fb4, 0xfc51, 0xf727, 0x1c8a, 0x04ff, 0xcf41, 0xec05, 0x2913, 0x1ce8, 0xf70c, -0xf744, 0xede8, 0xdd77, 0x0d99, 0x43f1, 0x119c, 0xc14f, 0xd60e, 0x17cb, 0x1e19, -0x0d4e, 0x0c95, 0xeed1, 0xcdf4, 0xf7a5, 0x331f, 0x1cd0, 0xeb17, 0xf082, 0xfb19, -0xe899, 0xfdeb, 0x323c, 0x2036, 0xdad3, 0xd134, 0xfd03, 0x1345, 0x1c10, 0x2239, -0xf656, 0xbc22, 0xdc3f, 0x3392, 0x3d59, 0xfd77, 0xdb4d, 0xe23f, 0xedbe, 0x0f7e, -0x35cc, 0x1947, 0xd5dc, 0xd1bf, 0x035d, 0x16fc, 0x1174, 0x1675, 0x0249, 0xd2d4, -0xd851, 0x184d, 0x32fe, 0x0f91, 0xee14, 0xe1e6, 0xdf9b, 0x016b, 0x3668, 0x2b2b, -0xe20c, 0xc554, 0xf257, 0x1c05, 0x1fc5, 0x14f0, 0xf891, 0xd41c, 0xdf83, 0x1865, -0x2de1, 0x0b16, 0xed58, 0xea0c, 0xea79, 0xfbd9, 0x22af, 0x2732, 0xf62f, 0xd389, -0xe7d9, 0x0b39, 0x1cdc, 0x1de3, 0x038a, 0xd809, 0xd5f7, 0x0b55, 0x305e, 0x1910, -0xf02e, 0xe089, 0xe7c7, 0x0195, 0x2265, 0x21da, 0xf743, 0xd8f2, 0xe978, 0x09a1, -0x190a, 0x17c5, 0x045a, 0xe46d, 0xdd06, 0xffb2, 0x2293, 0x1cfe, 0xfd4d, 0xe4f9, -0xe310, 0xfaf1, 0x1d22, 0x2376, 0x0113, 0xde3a, 0xe21b, 0x0204, 0x1ba1, 0x1bd6, -0x0333, 0xe563, 0xe104, 0xfd51, 0x1bc1, 0x1ccf, 0x0285, 0xe757, 0xe35e, 0xfaf2, -0x185d, 0x1d46, 0x06b7, 0xec13, 0xe108, 0xef6e, 0x121d, 0x2a17, 0x16a6, 0xe32c, -0xc9a9, 0xf070, 0x2f48, 0x3788, 0xfa4e, 0xc32a, 0xd9c2, 0x1fa1, 0x36fe, 0x07fa, -0xd9e4, 0xe577, 0x0e5e, 0x1755, 0xfb53, 0xed71, 0x0540, 0x19e0, 0x0301, 0xdc97, -0xe391, 0x1937, 0x367c, 0x0bc9, 0xca4c, 0xc96b, 0x105d, 0x461f, 0x2416, 0xd481, -0xbc97, 0xf8b7, 0x39af, 0x2ec9, 0xecc6, 0xcb50, 0xeee3, 0x1ffe, 0x1e8e, 0xf700, -0xe66a, 0xff58, 0x149f, 0x02e5, 0xe792, 0xf2d8, 0x1a4d, 0x225a, 0xf642, 0xce7f, -0xe6a6, 0x25e2, 0x38f5, 0x01d0, 0xc50f, 0xd243, 0x19bd, 0x3fc6, 0x14f0, 0xd2d7, -0xcdb6, 0x069a, 0x2ffe, 0x1847, 0xe6f8, 0xdf0a, 0x0337, 0x1a90, 0x067a, 0xeb5b, -0xf541, 0x143b, 0x14f2, 0xf092, 0xdc02, 0xfb91, 0x28a3, 0x2274, 0xeaa8, 0xc9e7, -0xef48, 0x2d01, 0x322e, 0xf6d2, 0xc7cb, 0xe13b, 0x1fda, 0x3217, 0x0458, 0xd690, -0xe2bf, 0x11c4, 0x21d5, 0x0291, 0xe5c8, 0xf3a9, 0x12ba, 0x11aa, 0xf22b, 0xe627, -0x03ec, 0x219a, 0x1036, 0xe2f2, 0xd93f, 0x059c, 0x2ed6, 0x1b75, 0xe227, 0xce55, -0xfb19, 0x2de0, 0x2477, 0xed08, 0xd148, 0xf307, 0x21d4, 0x2002, 0xf543, 0xdeac, -0xf7f9, 0x18a9, 0x11d6, 0xf0ef, 0xe8e4, 0x05ea, 0x1ba5, 0x0727, 0xe448, 0xe748, -0x100e, 0x265e, 0x07fc, 0xdbae, 0xde78, 0x0efa, 0x2ce0, 0x0f94, 0xddf1, 0xd9ea, -0x0797, 0x28f6, 0x12eb, 0xe60c, 0xdf46, 0x0469, 0x1fbb, 0x0ced, 0xe9f6, 0xe95f, -0x09fe, 0x1ab9, 0x02cb, 0xe5a4, 0xef2a, 0x1327, 0x1d7b, 0xfd07, 0xde3d, 0xed9c, -0x17e5, 0x22e7, 0xfe3a, 0xdb38, 0xe9b9, 0x161a, 0x2416, 0x0175, 0xde3d, 0xe9de, -0x1294, 0x1fc9, 0x00ea, 0xe2a7, 0xeee2, 0x1298, 0x1a7d, 0xfc1d, 0xe3bb, 0xf47a, -0x1642, 0x185e, 0xf727, 0xe1af, 0xf709, 0x19c3, 0x18e7, 0xf50d, 0xe010, 0xf75b, -0x1a9c, 0x18d8, 0xf4c5, 0xe0c9, 0xf865, 0x1a1c, 0x16d5, 0xf3a6, 0xe257, 0xfaf2, -0x1a44, 0x14d5, 0xf34f, 0xe4b6, 0xfc77, 0x17d5, 0x0ff8, 0xf133, 0xe8b7, 0x0344, -0x1a37, 0x0ad5, 0xe95e, 0xe61a, 0x08a5, 0x227e, 0x0e33, 0xe4a7, 0xdd70, 0x03b0, -0x25f4, 0x17b2, 0xec0a, 0xdb4e, 0xf898, 0x1ba3, 0x18f6, 0xf973, 0xe87f, 0xf77a, -0x0b93, 0x096c, 0xfb0e, 0xfb03, 0x0896, 0x0940, 0xf51d, 0xe904, 0xfdc7, 0x1dda, -0x1bf9, 0xf29b, 0xd37f, 0xea1b, 0x1f37, 0x3175, 0x07eb, 0xd3f7, 0xd46b, 0x077d, -0x2eeb, 0x1e67, 0xeeae, 0xd8c7, 0xef85, 0x1119, 0x18d3, 0x088e, 0xf953, 0xf5ad, -0xf556, 0xf63d, 0x0234, 0x167a, 0x19a1, 0xfbf9, 0xd873, 0xdd4b, 0x0f06, 0x3748, -0x21e6, 0xe181, 0xc032, 0xe79a, 0x2bec, 0x3e76, 0x0b1b, 0xce41, 0xcb23, 0xff96, -0x2d79, 0x26d1, 0xfcc7, 0xdf8a, 0xe525, 0xfd83, 0x10f1, 0x16d7, 0x0f50, 0xfaea, -0xe3f1, 0xe20f, 0x0158, 0x27d9, 0x2866, 0xf96f, 0xcb34, 0xd563, 0x11d6, 0x3d25, -0x2424, 0xe254, 0xc2c9, 0xe7cd, 0x248d, 0x34f5, 0x0c42, 0xdcd0, 0xd827, 0xfa65, -0x19eb, 0x1b50, 0x0721, 0xf396, 0xeb9c, 0xefde, 0x0016, 0x1594, 0x1cc1, 0x0658, -0xe22b, 0xd852, 0xfb3e, 0x2923, 0x2c78, 0xfc87, 0xcdb5, 0xd69c, 0x0e3c, 0x3527, -0x201f, 0xe993, 0xcf9e, 0xeb21, 0x183f, 0x25ea, 0x0c93, 0xed4d, 0xe5f9, 0xf548, -0x07fb, 0x117c, 0x0ff2, 0x0398, 0xf08c, 0xe628, 0xf489, 0x143b, 0x2419, 0x0ccf, -0xe2cc, 0xd5a6, 0xf861, 0x2615, 0x2a1b, 0xfeb4, 0xd543, 0xdc53, 0x09b4, 0x2901, -0x19ff, 0xf24a, 0xde86, 0xeec4, 0x0b7b, 0x1733, 0x0d0a, 0xfc24, 0xf1bb, 0xf110, -0xfa03, 0x0a0f, 0x15d4, 0x0e21, 0xf435, 0xe17e, 0xee90, 0x1225, 0x2527, 0x0efa, -0xe61f, 0xd916, 0xf7b8, 0x1f50, 0x2326, 0x0099, 0xe01e, 0xe473, 0x0491, 0x1b37, -0x1360, 0xfb17, 0xecd9, 0xf20d, 0x0051, 0x0aec, 0x0d4a, 0x073d, 0xfa5a, 0xeeb8, -0xf165, 0x0516, 0x17dc, 0x12da, 0xf71b, 0xe213, 0xed85, 0x0eef, 0x20c8, 0x0e09, -0xebcc, 0xe0d4, 0xf848, 0x1637, 0x19d6, 0x026b, 0xec09, 0xed00, 0xff9b, 0x0e5a, -0x0d6b, 0x026c, 0xf865, 0xf4da, 0xf888, 0x025a, 0x0cbb, 0x0d53, 0xff96, 0xeefa, -0xee80, 0x021c, 0x15d6, 0x126a, 0xf9c1, 0xe724, 0xf017, 0x0aa1, 0x18b6, 0x0b4e, -0xf2d7, 0xea91, 0xf957, 0x0cac, 0x1061, 0x03f4, 0xf6ad, 0xf476, 0xfbdf, 0x0489, -0x08b1, 0x06df, 0xffcf, 0xf766, 0xf537, 0xfddf, 0x0ad4, 0x0e15, 0x01da, 0xf205, -0xf0a0, 0x0082, 0x1066, 0x0e41, 0xfc71, 0xef1b, 0xf4ad, 0x05cd, 0x0f32, 0x07ed, -0xf9c8, 0xf401, 0xfa93, 0x04af, 0x088c, 0x04a7, 0xfe15, 0xf9f1, 0xfa64, 0xff1e, -0x0539, 0x078c, 0x02af, 0xfa1a, 0xf69d, 0xfd09, 0x075b, 0x0a3d, 0x01f2, 0xf761, -0xf642, 0xffa7, 0x08f3, 0x0830, 0xff05, 0xf7db, 0xf9bc, 0x0174, 0x068b, 0x04b2, -0xfeff, 0xfb39, 0xfc1a, 000000, 0x0371, 0x03d7, 0x00fe, 0xfd37, 0xfbe0, 0xfe78, -0x02af, 0x044a, 0x0180, 0xfd43, 0xfc00, 0xfed1, 0x02aa, 0x0346, 0x00dd, 0xfde0, -0xfbfe, 0x0114, 0x0987, 0x04bc, 0xf49d, 0xf23a, 0x06ab, 0x162e, 0x0544, 0xe76b, -0xea25, 0x1015, 0x2474, 0x0431, 0xd7d3, 0xe1ec, 0x1923, 0x2df5, 0x01cd, 0xd386, -0xe3d9, 0x1b9d, 0x2c62, 0xfeb8, 0xd31a, 0xe6ba, 0x1dbd, 0x2abb, 0xfbab, 0xd2ed, -0xe9ab, 0x1fa7, 0x28ef, 0xf8b3, 0xd2f5, 0xeca5, 0x2160, 0x26fd, 0xf5d7, 0xd334, -0xefa1, 0x22e5, 0x24ea, 0xf31b, 0xd3a9, 0xf29f, 0x2435, 0x22b6, 0xf07e, 0xd44e, -0xf59b, 0x2551, 0x2067, 0xee08, 0xd527, 0xf88e, 0x2639, 0x1e00, 0xebb6, 0xd62d, -0xfb77, 0x26eb, 0x1b85, 0xe98b, 0xd75f, 0xfe51, 0x276b, 0x18f9, 0xe78e, 0xd8b9, -0x011a, 0x27b6, 0x1660, 0xe5bb, 0xda3a, 0x03cc, 0x27cf, 0x13bd, 0xe415, 0xdbdf, -0x066a, 0x27b7, 0x1117, 0xe29e, 0xdda5, 0x08ec, 0x276e, 0x0e6d, 0xe154, 0xdf89, -0x0b52, 0x26f6, 0x0bc7, 0xe039, 0xe185, 0x0d96, 0x2653, 0x0924, 0xdf4e, 0xe399, -0x0fb9, 0x2584, 0x068b, 0xde93, 0xe5c0, 0x11b8, 0x248e, 0x03fd, 0xde08, 0xe7f8, -0x1390, 0x2372, 0x0180, 0xddaa, 0xea3c, 0x1544, 0x2231, 0xff12, 0xdd7a, 0xec89, -0x16cf, 0x20d0, 0xfcb9, 0xdd77, 0xeedb, 0x1831, 0x1f52, 0xfa77, 0xdd9f, 0xf132, -0x1969, 0x1db7, 0xf850, 0xddf1, 0xf385, 0x1a75, 0x1c06, 0xf645, 0xde6b, 0xf5d7, -0x1b5b, 0x1a3f, 0xf457, 0xdf0d, 0xf820, 0x1c13, 0x1867, 0xf288, 0xdfd2, 0xfa5f, -0x1ca1, 0x167f, 0xf0db, 0xe0ba, 0xfc92, 0x1d06, 0x148b, 0xef50, 0xe1c1, 0xfeb5, -0x1d43, 0x1290, 0xede9, 0xe2e6, 0x00c6, 0x1d58, 0x108e, 0xeca7, 0xe426, 0x02c4, -0x1d45, 0x0e8a, 0xeb8a, 0xe57f, 0x04a9, 0x1d0e, 0x0c87, 0xea92, 0xe6ec, 0x0677, -0x1cb2, 0x0a87, 0xe9be, 0xe86e, 0x082a, 0x1c34, 0x088b, 0xe912, 0xe9fe, 0x09c1, -0x1b95, 0x069c, 0xe88c, 0xeb9c, 0x0b3a, 0x1ad9, 0x04b6, 0xe82a, 0xed43, 0x0c96, -0x1a00, 0x02df, 0xe7eb, 0xeef3, 0x0dd0, 0x190d, 0x0116, 0xe7d0, 0xf0a8, 0x0eec, -0x1804, 0xff61, 0xe7d8, 0xf25d, 0x0fe6, 0x16e3, 0xfdc0, 0xe800, 0xf412, 0x10bf, -0x15b1, 0xfc36, 0xe848, 0xf5c5, 0x1176, 0x146e, 0xfac2, 0xe8ad, 0xf771, 0x120d, -0x1320, 0xf969, 0xe92e, 0xf913, 0x1282, 0x11c4, 0xf828, 0xe9cb, 0xfaac, 0x12d8, -0x1062, 0xf703, 0xea7e, 0xfc38, 0x130e, 0x0efa, 0xf5fb, 0xeb49, 0xfdb5, 0x1325, -0x0d8e, 0xf50e, 0xec26, 0xff20, 0x131e, 0x0c21, 0xf43f, 0xed15, 0x007a, 0x12fa, -0x0ab6, 0xf38d, 0xee15, 0x01be, 0x12bd, 0x094f, 0xf2f9, 0xef22, 0x02ef, 0x1265, -0x07f0, 0xf283, 0xf037, 0x0408, 0x11f6, 0x0699, 0xf226, 0xf156, 0x050a, 0x1170, -0x054b, 0xf1e8, 0xf27a, 0x05f4, 0x10d8, 0x040c, 0xf1c5, 0xf3a3, 0x06c2, 0x102c, -0x02da, 0xf1bc, 0xf4cc, 0x0779, 0x0f71, 0x01b7, 0xf1cc, 0xf5f5, 0x0815, 0x0ea7, -0x00a8, 0xf1f4, 0xf719, 0x0899, 0x0dd2, 0xffab, 0xf233, 0xf839, 0x0902, 0x0cf4, -0xfec0, 0xf288, 0xf950, 0x0952, 0x0c0e, 0xfdec, 0xf2ee, 0xfa5d, 0x0989, 0x0b23, -0xfd2d, 0xf368, 0xfb62, 0x09a7, 0x0a35, 0xfc85, 0xf3f1, 0xfc58, 0x09af, 0x0946, -0xfbf2, 0xf488, 0xfd3f, 0x09a1, 0x0859, 0xfb77, 0xf52c, 0xfe17, 0x097d, 0x076f, -0xfb14, 0xf5d8, 0xfede, 0x0945, 0x068a, 0xfac6, 0xf68d, 0xff93, 0x08fb, 0x05ad, -0xfa8e, 0xf747, 0x0034, 0x08a1, 0x04da, 0xfa6f, 0xf805, 0x00c2, 0x0836, 0x0410, -0xfa63, 0xf8c6, 0x013c, 0x07bf, 0x0354, 0xfa6c, 0xf985, 0x01a1, 0x073b, 0x02a4, -0xfa8a, 0xfa43, 0x01f1, 0x06af, 0x0204, 0xfab9, 0xfafc, 0x022c, 0x0619, 0x0175, -0xfafa, 0xfbae, 0x0252, 0x057f, 0x00f6, 0xfb4b, 0xfc5a, 0x0263, 0x04e0, 0x008b, -0xfbaa, 0xfcfa, 0x0262, 0x0440, 0x0032, 0xfc16, 0xfd90, 0x024b, 0x03a0, 0xffec, -0xfc8c, 0xfe19, 0x0225, 0x0301, 0xffb9, 0xfd0c, 0xfe93, 0x01ea, 0x0267, 0xff9c, -0xfd95, 0xfefe, 0x01a0, 0x01d3, 0xff90, 0xfe22, 0xff5a, 0x0147, 0x0145, 0xff99, -0xfeb3, 0xffa1, 0x00e0, 0x00c3, 0xffb6, 0xff46, 0xffd9, 0x006d, 0x004b, 0xffe5, -0xffda, 0xfffc, 000000, 0xfffe, 000000, 0xffff, 000000, 0xffff, 0xffff, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000 }; diff --git a/1.4.23-rc4/channels/busy_tone.h b/1.4.23-rc4/channels/busy_tone.h deleted file mode 100644 index 6e5db8e47..000000000 --- a/1.4.23-rc4/channels/busy_tone.h +++ /dev/null @@ -1,55 +0,0 @@ -/* busy.h: Generated from frequencies 480 and 620 - by gentone. 400 samples */ -static short busy[400] = { - 0, 13697, 24766, 31109, 31585, 26222, 16198, 3569, - -9162, -19575, -25812, -26935, -23069, -15322, -5493, 4339, - 12277, 16985, 17934, 15440, 10519, 4585, -908, -4827, - -6592, -6269, -4489, -2220, -467, 30, -983, -3203, - -5839, -7844, -8215, -6301, -2035, 3975, 10543, 16141, - 19260, 18787, 14322, 6338, -3845, -14296, -22858, -27611, - -27309, -21691, -11585, 1213, 14285, 25068, 31388, 31915, - 26457, 16010, 2568, -11282, -22885, -30054, -31509, -27120, - -17908, -5805, 6760, 17379, 24147, 26028, 23020, 16094, - 6931, -2478, -10279, -15136, -16474, -14538, -10253, -4949, - 0, 3515, 5052, 4688, 3045, 1069, -268, -272, - 1269, 3996, 7067, 9381, 9889, 7910, 3365, -3123, - -10320, -16622, -20424, -20510, -16384, -8448, 2006, 13026, - 22383, 28040, 28613, 23696, 13996, 1232, -12193, -23670, - -30918, -32459, -27935, -18190, -5103, 8795, 20838, 28764, - 31164, 27753, 19395, 7893, -4412, -15136, -22342, -24909, - -22717, -16609, -8143, 780, 8361, 13272, 14909, 13455, - 9758, 5067, 678, -2387, -3624, -3133, -1538, 224, - 1209, 751, -1315, -4580, -8145, -10848, -11585, -9628, - -4878, 2038, 9844, 16867, 21403, 22124, 18429, 10638, - 0, -11524, -21643, -28211, -29702, -25561, -16364, -3737, - 9946, 22044, 30180, 32733, 29182, 20210, 7573, -6269, - -18655, -27259, -30558, -28117, -20645, -9807, 2148, 12878, - 20426, 23599, 22173, 16865, 9117, 731, -6552, -11426, - -13269, -12216, -9050, -4941, -1118, 1460, 2335, 1635, - 0, -1635, -2335, -1460, 1118, 4941, 9050, 12216, - 13269, 11426, 6552, -731, -9117, -16865, -22173, -23599, - -20426, -12878, -2148, 9807, 20645, 28117, 30558, 27259, - 18655, 6269, -7573, -20210, -29182, -32733, -30180, -22044, - -9946, 3737, 16364, 25561, 29702, 28211, 21643, 11524, - 0, -10638, -18429, -22124, -21403, -16867, -9844, -2038, - 4878, 9628, 11585, 10848, 8145, 4580, 1315, -751, - -1209, -224, 1538, 3133, 3624, 2387, -678, -5067, - -9758, -13455, -14909, -13272, -8361, -780, 8143, 16609, - 22717, 24909, 22342, 15136, 4412, -7893, -19395, -27753, - -31164, -28764, -20838, -8795, 5103, 18190, 27935, 32459, - 30918, 23670, 12193, -1232, -13996, -23696, -28613, -28040, - -22383, -13026, -2006, 8448, 16384, 20510, 20424, 16622, - 10320, 3123, -3365, -7910, -9889, -9381, -7067, -3996, - -1269, 272, 268, -1069, -3045, -4688, -5052, -3515, - 0, 4949, 10253, 14538, 16474, 15136, 10279, 2478, - -6931, -16094, -23020, -26028, -24147, -17379, -6760, 5805, - 17908, 27120, 31509, 30054, 22885, 11282, -2568, -16010, - -26457, -31915, -31388, -25068, -14285, -1213, 11585, 21691, - 27309, 27611, 22858, 14296, 3845, -6338, -14322, -18787, - -19260, -16141, -10543, -3975, 2035, 6301, 8215, 7844, - 5839, 3203, 983, -30, 467, 2220, 4489, 6269, - 6592, 4827, 908, -4585, -10519, -15440, -17934, -16985, - -12277, -4339, 5493, 15322, 23069, 26935, 25812, 19575, - 9162, -3569, -16198, -26222, -31585, -31109, -24766, -13697, - -}; diff --git a/1.4.23-rc4/channels/chan_agent.c b/1.4.23-rc4/channels/chan_agent.c deleted file mode 100644 index b12983dfc..000000000 --- a/1.4.23-rc4/channels/chan_agent.c +++ /dev/null @@ -1,2870 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - - -/*! \file - * - * \brief Implementation of Agents (proxy channel) - * - * \author Mark Spencer <markster@digium.com> - * - * This file is the implementation of Agents modules. - * It is a dynamic module that is loaded by Asterisk. - * \par See also - * \arg \ref Config_agent - * - * \ingroup channel_drivers - */ -/*** MODULEINFO - <depend>chan_local</depend> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <sys/socket.h> -#include <stdlib.h> -#include <fcntl.h> -#include <netdb.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/signal.h> - -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/lock.h" -#include "asterisk/sched.h" -#include "asterisk/io.h" -#include "asterisk/rtp.h" -#include "asterisk/acl.h" -#include "asterisk/callerid.h" -#include "asterisk/file.h" -#include "asterisk/cli.h" -#include "asterisk/app.h" -#include "asterisk/musiconhold.h" -#include "asterisk/manager.h" -#include "asterisk/features.h" -#include "asterisk/utils.h" -#include "asterisk/causes.h" -#include "asterisk/astdb.h" -#include "asterisk/devicestate.h" -#include "asterisk/monitor.h" -#include "asterisk/stringfields.h" - -static const char tdesc[] = "Call Agent Proxy Channel"; -static const char config[] = "agents.conf"; - -static const char app[] = "AgentLogin"; -static const char app2[] = "AgentCallbackLogin"; -static const char app3[] = "AgentMonitorOutgoing"; - -static const char synopsis[] = "Call agent login"; -static const char synopsis2[] = "Call agent callback login"; -static const char synopsis3[] = "Record agent's outgoing call"; - -static const char descrip[] = -" AgentLogin([AgentNo][|options]):\n" -"Asks the agent to login to the system. Always returns -1. While\n" -"logged in, the agent can receive calls and will hear a 'beep'\n" -"when a new call comes in. The agent can dump the call by pressing\n" -"the star key.\n" -"The option string may contain zero or more of the following characters:\n" -" 's' -- silent login - do not announce the login ok segment after agent logged in/off\n"; - -static const char descrip2[] = -" AgentCallbackLogin([AgentNo][|[options][|[exten]@context]]):\n" -"Asks the agent to login to the system with callback.\n" -"The agent's callback extension is called (optionally with the specified\n" -"context).\n" -"The option string may contain zero or more of the following characters:\n" -" 's' -- silent login - do not announce the login ok segment agent logged in/off\n"; - -static const char descrip3[] = -" AgentMonitorOutgoing([options]):\n" -"Tries to figure out the id of the agent who is placing outgoing call based on\n" -"comparison of the callerid of the current interface and the global variable \n" -"placed by the AgentCallbackLogin application. That's why it should be used only\n" -"with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n" -"instead of Monitor application. That have to be configured in the agents.conf file.\n" -"\nReturn value:\n" -"Normally the app returns 0 unless the options are passed. Also if the callerid or\n" -"the agentid are not specified it'll look for n+101 priority.\n" -"\nOptions:\n" -" 'd' - make the app return -1 if there is an error condition and there is\n" -" no extension n+101\n" -" 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n" -" 'n' - don't generate the warnings when there is no callerid or the\n" -" agentid is not known.\n" -" It's handy if you want to have one context for agent and non-agent calls.\n"; - -static const char mandescr_agents[] = -"Description: Will list info about all possible agents.\n" -"Variables: NONE\n"; - -static const char mandescr_agent_logoff[] = -"Description: Sets an agent as no longer logged in.\n" -"Variables: (Names marked with * are required)\n" -" *Agent: Agent ID of the agent to log off\n" -" Soft: Set to 'true' to not hangup existing calls\n"; - -static const char mandescr_agent_callback_login[] = -"Description: Sets an agent as logged in with callback.\n" -"Variables: (Names marked with * are required)\n" -" *Agent: Agent ID of the agent to login\n" -" *Exten: Extension to use for callback\n" -" Context: Context to use for callback\n" -" AckCall: Set to 'true' to require an acknowledgement by '#' when agent is called back\n" -" WrapupTime: the minimum amount of time after disconnecting before the caller can receive a new call\n"; - -static char moh[80] = "default"; - -#define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */ -#define AST_MAX_BUF 256 -#define AST_MAX_FILENAME_LEN 256 - -static const char pa_family[] = "Agents"; /*!< Persistent Agents astdb family */ -#define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */ - -static int persistent_agents = 0; /*!< queues.conf [general] option */ -static void dump_agents(void); - -static ast_group_t group; -static int autologoff; -static int wrapuptime; -static int ackcall; -static int endcall; -static int multiplelogin = 1; -static int autologoffunavail = 0; - -static int maxlogintries = 3; -static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye"; - -static int recordagentcalls = 0; -static char recordformat[AST_MAX_BUF] = ""; -static char recordformatext[AST_MAX_BUF] = ""; -static char urlprefix[AST_MAX_BUF] = ""; -static char savecallsin[AST_MAX_BUF] = ""; -static int updatecdr = 0; -static char beep[AST_MAX_BUF] = "beep"; - -#define GETAGENTBYCALLERID "AGENTBYCALLERID" - -/*! \brief Structure representing an agent. */ -struct agent_pvt { - ast_mutex_t lock; /*!< Channel private lock */ - int dead; /*!< Poised for destruction? */ - int pending; /*!< Not a real agent -- just pending a match */ - int abouttograb; /*!< About to grab */ - int autologoff; /*!< Auto timeout time */ - int ackcall; /*!< ackcall */ - int deferlogoff; /*!< Defer logoff to hangup */ - time_t loginstart; /*!< When agent first logged in (0 when logged off) */ - time_t start; /*!< When call started */ - struct timeval lastdisc; /*!< When last disconnected */ - int wrapuptime; /*!< Wrapup time in ms */ - ast_group_t group; /*!< Group memberships */ - int acknowledged; /*!< Acknowledged */ - char moh[80]; /*!< Which music on hold */ - char agent[AST_MAX_AGENT]; /*!< Agent ID */ - char password[AST_MAX_AGENT]; /*!< Password for Agent login */ - char name[AST_MAX_AGENT]; - int inherited_devicestate; /*!< Does the underlying channel have a devicestate to pass? */ - ast_mutex_t app_lock; /**< Synchronization between owning applications */ - int app_lock_flag; - ast_cond_t app_complete_cond; - volatile int app_sleep_cond; /**< Sleep condition for the login app */ - struct ast_channel *owner; /**< Agent */ - char loginchan[80]; /**< channel they logged in from */ - char logincallerid[80]; /**< Caller ID they had when they logged in */ - struct ast_channel *chan; /**< Channel we use */ - AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */ -}; - -static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */ - -#define CHECK_FORMATS(ast, p) do { \ - if (p->chan) {\ - if (ast->nativeformats != p->chan->nativeformats) { \ - ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \ - /* Native formats changed, reset things */ \ - ast->nativeformats = p->chan->nativeformats; \ - ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\ - ast_set_read_format(ast, ast->readformat); \ - ast_set_write_format(ast, ast->writeformat); \ - } \ - if (p->chan->readformat != ast->rawreadformat && !p->chan->generator) \ - ast_set_read_format(p->chan, ast->rawreadformat); \ - if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \ - ast_set_write_format(p->chan, ast->rawwriteformat); \ - } \ -} while(0) - -/*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things - properly for a timingfd XXX This might need more work if agents were logged in as agents or other - totally impractical combinations XXX */ - -#define CLEANUP(ast, p) do { \ - int x; \ - if (p->chan) { \ - for (x=0;x<AST_MAX_FDS;x++) {\ - if (x != AST_TIMING_FD) \ - ast->fds[x] = p->chan->fds[x]; \ - } \ - ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \ - } \ -} while(0) - -/*--- Forward declarations */ -static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause); -static int agent_devicestate(void *data); -static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand); -static int agent_digit_begin(struct ast_channel *ast, char digit); -static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int agent_call(struct ast_channel *ast, char *dest, int timeout); -static int agent_hangup(struct ast_channel *ast); -static int agent_answer(struct ast_channel *ast); -static struct ast_frame *agent_read(struct ast_channel *ast); -static int agent_write(struct ast_channel *ast, struct ast_frame *f); -static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen); -static int agent_sendtext(struct ast_channel *ast, const char *text); -static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); -static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge); -static void set_agentbycallerid(const char *callerid, const char *agent); -static struct ast_channel* agent_get_base_channel(struct ast_channel *chan); -static int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base); - -/*! \brief Channel interface description for PBX integration */ -static const struct ast_channel_tech agent_tech = { - .type = "Agent", - .description = tdesc, - .capabilities = -1, - .requester = agent_request, - .devicestate = agent_devicestate, - .send_digit_begin = agent_digit_begin, - .send_digit_end = agent_digit_end, - .call = agent_call, - .hangup = agent_hangup, - .answer = agent_answer, - .read = agent_read, - .write = agent_write, - .write_video = agent_write, - .send_html = agent_sendhtml, - .send_text = agent_sendtext, - .exception = agent_read, - .indicate = agent_indicate, - .fixup = agent_fixup, - .bridged_channel = agent_bridgedchannel, - .get_base_channel = agent_get_base_channel, - .set_base_channel = agent_set_base_channel, -}; - -static int agent_devicestate_cb(const char *dev, int state, void *data) -{ - int res, i; - struct agent_pvt *p; - char basename[AST_CHANNEL_NAME], *tmp; - - /* Skip Agent status */ - if (!strncasecmp(dev, "Agent/", 6)) { - return 0; - } - - /* Try to be safe, but don't deadlock */ - for (i = 0; i < 10; i++) { - if ((res = AST_LIST_TRYLOCK(&agents)) == 0) { - break; - } - } - if (res) { - return -1; - } - - AST_LIST_TRAVERSE(&agents, p, list) { - ast_mutex_lock(&p->lock); - if (p->chan) { - ast_copy_string(basename, p->chan->name, sizeof(basename)); - if ((tmp = strrchr(basename, '-'))) { - *tmp = '\0'; - } - if (strcasecmp(p->chan->name, dev) == 0 || strcasecmp(basename, dev) == 0) { - p->inherited_devicestate = state; - ast_device_state_changed("Agent/%s", p->agent); - } - } - ast_mutex_unlock(&p->lock); - } - AST_LIST_UNLOCK(&agents); - return 0; -} - -/*! - * Adds an agent to the global list of agents. - * - * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith" - * \param pending If it is pending or not. - * @return The just created agent. - * \sa agent_pvt, agents. - */ -static struct agent_pvt *add_agent(char *agent, int pending) -{ - char *parse; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(agt); - AST_APP_ARG(password); - AST_APP_ARG(name); - ); - char *password = NULL; - char *name = NULL; - char *agt = NULL; - struct agent_pvt *p; - - parse = ast_strdupa(agent); - - /* Extract username (agt), password and name from agent (args). */ - AST_NONSTANDARD_APP_ARGS(args, parse, ','); - - if(args.argc == 0) { - ast_log(LOG_WARNING, "A blank agent line!\n"); - return NULL; - } - - if(ast_strlen_zero(args.agt) ) { - ast_log(LOG_WARNING, "An agent line with no agentid!\n"); - return NULL; - } else - agt = args.agt; - - if(!ast_strlen_zero(args.password)) { - password = args.password; - while (*password && *password < 33) password++; - } - if(!ast_strlen_zero(args.name)) { - name = args.name; - while (*name && *name < 33) name++; - } - - /* Are we searching for the agent here ? To see if it exists already ? */ - AST_LIST_TRAVERSE(&agents, p, list) { - if (!pending && !strcmp(p->agent, agt)) - break; - } - if (!p) { - // Build the agent. - if (!(p = ast_calloc(1, sizeof(*p)))) - return NULL; - ast_copy_string(p->agent, agt, sizeof(p->agent)); - ast_mutex_init(&p->lock); - ast_mutex_init(&p->app_lock); - ast_cond_init(&p->app_complete_cond, NULL); - p->app_lock_flag = 0; - p->app_sleep_cond = 1; - p->group = group; - p->pending = pending; - p->inherited_devicestate = -1; - AST_LIST_INSERT_TAIL(&agents, p, list); - } - - ast_copy_string(p->password, password ? password : "", sizeof(p->password)); - ast_copy_string(p->name, name ? name : "", sizeof(p->name)); - ast_copy_string(p->moh, moh, sizeof(p->moh)); - p->ackcall = ackcall; - p->autologoff = autologoff; - - /* If someone reduces the wrapuptime and reloads, we want it - * to change the wrapuptime immediately on all calls */ - if (p->wrapuptime > wrapuptime) { - struct timeval now = ast_tvnow(); - /* XXX check what is this exactly */ - - /* We won't be pedantic and check the tv_usec val */ - if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) { - p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000; - p->lastdisc.tv_usec = now.tv_usec; - } - } - p->wrapuptime = wrapuptime; - - if (pending) - p->dead = 1; - else - p->dead = 0; - return p; -} - -/*! - * Deletes an agent after doing some clean up. - * Further documentation: How safe is this function ? What state should the agent be to be cleaned. - * \param p Agent to be deleted. - * \returns Always 0. - */ -static int agent_cleanup(struct agent_pvt *p) -{ - struct ast_channel *chan = p->owner; - p->owner = NULL; - chan->tech_pvt = NULL; - p->app_sleep_cond = 1; - /* Release ownership of the agent to other threads (presumably running the login app). */ - p->app_lock_flag = 0; - ast_cond_signal(&p->app_complete_cond); - if (chan) - ast_channel_free(chan); - if (p->dead) { - ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); - ast_cond_destroy(&p->app_complete_cond); - free(p); - } - return 0; -} - -static int check_availability(struct agent_pvt *newlyavailable, int needlock); - -static int agent_answer(struct ast_channel *ast) -{ - ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n"); - return -1; -} - -static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock) -{ - char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer; - char filename[AST_MAX_BUF]; - int res = -1; - if (!p) - return -1; - if (!ast->monitor) { - snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid); - /* substitute . for - */ - if ((pointer = strchr(filename, '.'))) - *pointer = '-'; - snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename); - ast_monitor_start(ast, recordformat, tmp, needlock); - ast_monitor_setjoinfiles(ast, 1); - snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext); -#if 0 - ast_verbose("name is %s, link is %s\n",tmp, tmp2); -#endif - if (!ast->cdr) - ast->cdr = ast_cdr_alloc(); - ast_cdr_setuserfield(ast, tmp2); - res = 0; - } else - ast_log(LOG_ERROR, "Recording already started on that call.\n"); - return res; -} - -static int agent_start_monitoring(struct ast_channel *ast, int needlock) -{ - return __agent_start_monitoring(ast, ast->tech_pvt, needlock); -} - -static struct ast_frame *agent_read(struct ast_channel *ast) -{ - struct agent_pvt *p = ast->tech_pvt; - struct ast_frame *f = NULL; - static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; - const char *status; - ast_mutex_lock(&p->lock); - CHECK_FORMATS(ast, p); - if (p->chan) { - ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION); - p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno; - f = ast_read(p->chan); - } else - f = &ast_null_frame; - if (!f) { - /* If there's a channel, hang it up (if it's on a callback) make it NULL */ - if (p->chan) { - p->chan->_bridge = NULL; - /* Note that we don't hangup if it's not a callback because Asterisk will do it - for us when the PBX instance that called login finishes */ - if (!ast_strlen_zero(p->loginchan)) { - if (p->chan) - ast_log(LOG_DEBUG, "Bridge on '%s' being cleared (2)\n", p->chan->name); - if (p->owner->_state != AST_STATE_UP) { - int howlong = time(NULL) - p->start; - if (p->autologoff && howlong > p->autologoff) { - long logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong); - agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff"); - if (persistent_agents) - dump_agents(); - } - } - status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS"); - if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) { - long logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name); - agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail"); - } - ast_hangup(p->chan); - if (p->wrapuptime && p->acknowledged) - p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); - } - p->chan = NULL; - p->inherited_devicestate = -1; - ast_device_state_changed("Agent/%s", p->agent); - p->acknowledged = 0; - } - } else { - /* if acknowledgement is not required, and the channel is up, we may have missed - an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */ - if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP)) - p->acknowledged = 1; - switch (f->frametype) { - case AST_FRAME_CONTROL: - if (f->subclass == AST_CONTROL_ANSWER) { - if (p->ackcall) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s answered, waiting for '#' to acknowledge\n", p->chan->name); - /* Don't pass answer along */ - ast_frfree(f); - f = &ast_null_frame; - } else { - p->acknowledged = 1; - /* Use the builtin answer frame for the - recording start check below. */ - ast_frfree(f); - f = &answer_frame; - } - } - break; - case AST_FRAME_DTMF_BEGIN: - /*ignore DTMF begin's as it can cause issues with queue announce files*/ - if((!p->acknowledged && f->subclass == '#') || (f->subclass == '*' && endcall)){ - ast_frfree(f); - f = &ast_null_frame; - } - break; - case AST_FRAME_DTMF_END: - if (!p->acknowledged && (f->subclass == '#')) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name); - p->acknowledged = 1; - ast_frfree(f); - f = &answer_frame; - } else if (f->subclass == '*' && endcall) { - /* terminates call */ - ast_frfree(f); - f = NULL; - } - break; - case AST_FRAME_VOICE: - case AST_FRAME_VIDEO: - /* don't pass voice or video until the call is acknowledged */ - if (!p->acknowledged) { - ast_frfree(f); - f = &ast_null_frame; - } - default: - /* pass everything else on through */ - break; - } - } - - CLEANUP(ast,p); - if (p->chan && !p->chan->_bridge) { - if (strcasecmp(p->chan->tech->type, "Local")) { - p->chan->_bridge = ast; - if (p->chan) - ast_log(LOG_DEBUG, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name); - } - } - ast_mutex_unlock(&p->lock); - if (recordagentcalls && f == &answer_frame) - agent_start_monitoring(ast,0); - return f; -} - -static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen) -{ - struct agent_pvt *p = ast->tech_pvt; - int res = -1; - ast_mutex_lock(&p->lock); - if (p->chan) - res = ast_channel_sendhtml(p->chan, subclass, data, datalen); - ast_mutex_unlock(&p->lock); - return res; -} - -static int agent_sendtext(struct ast_channel *ast, const char *text) -{ - struct agent_pvt *p = ast->tech_pvt; - int res = -1; - ast_mutex_lock(&p->lock); - if (p->chan) - res = ast_sendtext(p->chan, text); - ast_mutex_unlock(&p->lock); - return res; -} - -static int agent_write(struct ast_channel *ast, struct ast_frame *f) -{ - struct agent_pvt *p = ast->tech_pvt; - int res = -1; - CHECK_FORMATS(ast, p); - ast_mutex_lock(&p->lock); - if (!p->chan) - res = 0; - else { - if ((f->frametype != AST_FRAME_VOICE) || - (f->frametype != AST_FRAME_VIDEO) || - (f->subclass == p->chan->writeformat)) { - res = ast_write(p->chan, f); - } else { - ast_log(LOG_DEBUG, "Dropping one incompatible %s frame on '%s' to '%s'\n", - f->frametype == AST_FRAME_VOICE ? "audio" : "video", - ast->name, p->chan->name); - res = 0; - } - } - CLEANUP(ast, p); - ast_mutex_unlock(&p->lock); - return res; -} - -static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct agent_pvt *p = newchan->tech_pvt; - ast_mutex_lock(&p->lock); - if (p->owner != oldchan) { - ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner); - ast_mutex_unlock(&p->lock); - return -1; - } - p->owner = newchan; - ast_mutex_unlock(&p->lock); - return 0; -} - -static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen) -{ - struct agent_pvt *p = ast->tech_pvt; - int res = -1; - ast_mutex_lock(&p->lock); - if (p->chan && !ast_check_hangup(p->chan)) { - while (ast_channel_trylock(p->chan)) { - ast_channel_unlock(ast); - usleep(1); - ast_channel_lock(ast); - } - res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1; - ast_channel_unlock(p->chan); - } else - res = 0; - ast_mutex_unlock(&p->lock); - return res; -} - -static int agent_digit_begin(struct ast_channel *ast, char digit) -{ - struct agent_pvt *p = ast->tech_pvt; - ast_mutex_lock(&p->lock); - if (p->chan) { - ast_senddigit_begin(p->chan, digit); - } - ast_mutex_unlock(&p->lock); - return 0; -} - -static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ - struct agent_pvt *p = ast->tech_pvt; - ast_mutex_lock(&p->lock); - if (p->chan) { - ast_senddigit_end(p->chan, digit, duration); - } - ast_mutex_unlock(&p->lock); - return 0; -} - -static int agent_call(struct ast_channel *ast, char *dest, int timeout) -{ - struct agent_pvt *p = ast->tech_pvt; - int res = -1; - int newstate=0; - ast_mutex_lock(&p->lock); - p->acknowledged = 0; - if (!p->chan) { - if (p->pending) { - ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n"); - newstate = AST_STATE_DIALING; - res = 0; - } else { - ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n"); - res = -1; - } - ast_mutex_unlock(&p->lock); - if (newstate) - ast_setstate(ast, newstate); - return res; - } else if (!ast_strlen_zero(p->loginchan)) { - time(&p->start); - /* Call on this agent */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name); - ast_set_callerid(p->chan, - ast->cid.cid_num, ast->cid.cid_name, NULL); - ast_channel_inherit_variables(ast, p->chan); - res = ast_call(p->chan, p->loginchan, 0); - CLEANUP(ast,p); - ast_mutex_unlock(&p->lock); - return res; - } - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language); - res = ast_streamfile(p->chan, beep, p->chan->language); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res); - if (!res) { - res = ast_waitstream(p->chan, ""); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res); - } - if (!res) { - res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats)); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Set read format, result '%d'\n", res); - if (res) - ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats))); - } else { - /* Agent hung-up */ - p->chan = NULL; - p->inherited_devicestate = -1; - ast_device_state_changed("Agent/%s", p->agent); - } - - if (!res) { - res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats)); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Set write format, result '%d'\n", res); - if (res) - ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats))); - } - if(!res) { - /* Call is immediately up, or might need ack */ - if (p->ackcall > 1) - newstate = AST_STATE_RINGING; - else { - newstate = AST_STATE_UP; - if (recordagentcalls) - agent_start_monitoring(ast, 0); - p->acknowledged = 1; - } - res = 0; - } - CLEANUP(ast, p); - ast_mutex_unlock(&p->lock); - if (newstate) - ast_setstate(ast, newstate); - return res; -} - -/*! \brief store/clear the global variable that stores agentid based on the callerid */ -static void set_agentbycallerid(const char *callerid, const char *agent) -{ - char buf[AST_MAX_BUF]; - - /* if there is no Caller ID, nothing to do */ - if (ast_strlen_zero(callerid)) - return; - - snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid); - pbx_builtin_setvar_helper(NULL, buf, agent); -} - -/*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */ -struct ast_channel* agent_get_base_channel(struct ast_channel *chan) -{ - struct agent_pvt *p = NULL; - struct ast_channel *base = chan; - - /* chan is locked by the calling function */ - if (!chan || !chan->tech_pvt) { - ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) with a tech_pvt (0x%ld) to get a base channel.\n", (long)chan, (chan)?(long)chan->tech_pvt:(long)NULL); - return NULL; - } - p = chan->tech_pvt; - if (p->chan) - base = p->chan; - return base; -} - -int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base) -{ - struct agent_pvt *p = NULL; - - if (!chan || !base) { - ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan, (long)base); - return -1; - } - p = chan->tech_pvt; - if (!p) { - ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan->name); - return -1; - } - p->chan = base; - return 0; -} - -static int agent_hangup(struct ast_channel *ast) -{ - struct agent_pvt *p = ast->tech_pvt; - int howlong = 0; - const char *status; - ast_mutex_lock(&p->lock); - p->owner = NULL; - ast->tech_pvt = NULL; - p->app_sleep_cond = 1; - p->acknowledged = 0; - - /* if they really are hung up then set start to 0 so the test - * later if we're called on an already downed channel - * doesn't cause an agent to be logged out like when - * agent_request() is followed immediately by agent_hangup() - * as in apps/app_chanisavail.c:chanavail_exec() - */ - - if (option_debug) - ast_log(LOG_DEBUG, "Hangup called for state %s\n", ast_state2str(ast->_state)); - if (p->start && (ast->_state != AST_STATE_UP)) { - howlong = time(NULL) - p->start; - p->start = 0; - } else if (ast->_state == AST_STATE_RESERVED) - howlong = 0; - else - p->start = 0; - if (p->chan) { - p->chan->_bridge = NULL; - /* If they're dead, go ahead and hang up on the agent now */ - if (!ast_strlen_zero(p->loginchan)) { - /* Store last disconnect time */ - if (p->wrapuptime) - p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); - else - p->lastdisc = ast_tv(0,0); - if (p->chan) { - status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS"); - if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) { - long logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name); - agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail"); - } - /* Recognize the hangup and pass it along immediately */ - ast_hangup(p->chan); - p->chan = NULL; - p->inherited_devicestate = -1; - ast_device_state_changed("Agent/%s", p->agent); - } - ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff); - if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) { - long logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - if (!p->deferlogoff) - ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong); - p->deferlogoff = 0; - agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff"); - if (persistent_agents) - dump_agents(); - } - } else if (p->dead) { - ast_channel_lock(p->chan); - ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); - ast_channel_unlock(p->chan); - } else if (p->loginstart) { - ast_channel_lock(p->chan); - ast_indicate_data(p->chan, AST_CONTROL_HOLD, - S_OR(p->moh, NULL), - !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0); - ast_channel_unlock(p->chan); - } - } - ast_mutex_unlock(&p->lock); - - /* Only register a device state change if the agent is still logged in */ - if (!p->loginstart) { - p->loginchan[0] = '\0'; - p->logincallerid[0] = '\0'; - if (persistent_agents) - dump_agents(); - } else { - ast_device_state_changed("Agent/%s", p->agent); - } - - if (p->pending) { - AST_LIST_LOCK(&agents); - AST_LIST_REMOVE(&agents, p, list); - AST_LIST_UNLOCK(&agents); - } - if (p->abouttograb) { - /* Let the "about to grab" thread know this isn't valid anymore, and let it - kill it later */ - p->abouttograb = 0; - } else if (p->dead) { - ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); - ast_cond_destroy(&p->app_complete_cond); - free(p); - } else { - if (p->chan) { - /* Not dead -- check availability now */ - ast_mutex_lock(&p->lock); - /* Store last disconnect time */ - p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); - ast_mutex_unlock(&p->lock); - } - /* Release ownership of the agent to other threads (presumably running the login app). */ - if (ast_strlen_zero(p->loginchan)) { - p->app_lock_flag = 0; - ast_cond_signal(&p->app_complete_cond); - } - } - return 0; -} - -static int agent_cont_sleep( void *data ) -{ - struct agent_pvt *p; - int res; - - p = (struct agent_pvt *)data; - - ast_mutex_lock(&p->lock); - res = p->app_sleep_cond; - if (p->lastdisc.tv_sec) { - if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) - res = 1; - } - ast_mutex_unlock(&p->lock); - - if(option_debug > 4 && !res ) - ast_log(LOG_DEBUG, "agent_cont_sleep() returning %d\n", res ); - - return res; -} - -static int agent_ack_sleep(void *data) -{ - struct agent_pvt *p; - int res=0; - int to = 1000; - struct ast_frame *f; - - /* Wait a second and look for something */ - - p = (struct agent_pvt *) data; - if (!p->chan) - return -1; - - for(;;) { - to = ast_waitfor(p->chan, to); - if (to < 0) - return -1; - if (!to) - return 0; - f = ast_read(p->chan); - if (!f) - return -1; - if (f->frametype == AST_FRAME_DTMF) - res = f->subclass; - else - res = 0; - ast_frfree(f); - ast_mutex_lock(&p->lock); - if (!p->app_sleep_cond) { - ast_mutex_unlock(&p->lock); - return 0; - } else if (res == '#') { - ast_mutex_unlock(&p->lock); - return 1; - } - ast_mutex_unlock(&p->lock); - res = 0; - } - return res; -} - -static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge) -{ - struct agent_pvt *p = bridge->tech_pvt; - struct ast_channel *ret = NULL; - - if (p) { - if (chan == p->chan) - ret = bridge->_bridge; - else if (chan == bridge->_bridge) - ret = p->chan; - } - - if (option_debug) - ast_log(LOG_DEBUG, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>"); - return ret; -} - -/*! \brief Create new agent channel */ -static struct ast_channel *agent_new(struct agent_pvt *p, int state) -{ - struct ast_channel *tmp; - int alreadylocked; -#if 0 - if (!p->chan) { - ast_log(LOG_WARNING, "No channel? :(\n"); - return NULL; - } -#endif - if (p->pending) - tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/P%s-%d", p->agent, (int) ast_random() & 0xffff); - else - tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent); - if (!tmp) { - ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n"); - return NULL; - } - - tmp->tech = &agent_tech; - if (p->chan) { - tmp->nativeformats = p->chan->nativeformats; - tmp->writeformat = p->chan->writeformat; - tmp->rawwriteformat = p->chan->writeformat; - tmp->readformat = p->chan->readformat; - tmp->rawreadformat = p->chan->readformat; - ast_string_field_set(tmp, language, p->chan->language); - ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context)); - ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten)); - /* XXX Is this really all we copy form the originating channel?? */ - } else { - tmp->nativeformats = AST_FORMAT_SLINEAR; - tmp->writeformat = AST_FORMAT_SLINEAR; - tmp->rawwriteformat = AST_FORMAT_SLINEAR; - tmp->readformat = AST_FORMAT_SLINEAR; - tmp->rawreadformat = AST_FORMAT_SLINEAR; - } - /* Safe, agentlock already held */ - tmp->tech_pvt = p; - p->owner = tmp; - /* XXX: this needs fixing */ -#if 0 - ast_atomic_fetchadd_int(&__mod_desc->usecnt, +1); -#endif - ast_update_use_count(); - tmp->priority = 1; - /* Wake up and wait for other applications (by definition the login app) - * to release this channel). Takes ownership of the agent channel - * to this thread only. - * For signalling the other thread, ast_queue_frame is used until we - * can safely use signals for this purpose. The pselect() needs to be - * implemented in the kernel for this. - */ - p->app_sleep_cond = 0; - - alreadylocked = p->app_lock_flag; - p->app_lock_flag = 1; - - if(ast_strlen_zero(p->loginchan) && alreadylocked) { - if (p->chan) { - ast_queue_frame(p->chan, &ast_null_frame); - ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ - p->app_lock_flag = 1; - ast_mutex_lock(&p->lock); - } else { - ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n"); - p->owner = NULL; - tmp->tech_pvt = NULL; - p->app_sleep_cond = 1; - ast_channel_free( tmp ); - ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ - p->app_lock_flag = 0; - ast_cond_signal(&p->app_complete_cond); - return NULL; - } - } else if (!ast_strlen_zero(p->loginchan)) { - if (p->chan) - ast_queue_frame(p->chan, &ast_null_frame); - if (!p->chan) { - ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n"); - p->owner = NULL; - tmp->tech_pvt = NULL; - p->app_sleep_cond = 1; - ast_channel_free( tmp ); - ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ - return NULL; - } - } - if (p->chan) - ast_indicate(p->chan, AST_CONTROL_UNHOLD); - return tmp; -} - - -/*! - * Read configuration data. The file named agents.conf. - * - * \returns Always 0, or so it seems. - */ -static int read_agent_config(void) -{ - struct ast_config *cfg; - struct ast_config *ucfg; - struct ast_variable *v; - struct agent_pvt *p; - const char *general_val; - const char *catname; - const char *hasagent; - int genhasagent; - - group = 0; - autologoff = 0; - wrapuptime = 0; - ackcall = 0; - endcall = 1; - cfg = ast_config_load(config); - if (!cfg) { - ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n"); - return 0; - } - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - p->dead = 1; - } - strcpy(moh, "default"); - /* set the default recording values */ - recordagentcalls = 0; - strcpy(recordformat, "wav"); - strcpy(recordformatext, "wav"); - urlprefix[0] = '\0'; - savecallsin[0] = '\0'; - - /* Read in [general] section for persistence */ - if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents"))) - persistent_agents = ast_true(general_val); - multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin")); - - /* Read in the [agents] section */ - v = ast_variable_browse(cfg, "agents"); - while(v) { - /* Create the interface list */ - if (!strcasecmp(v->name, "agent")) { - add_agent(v->value, 0); - } else if (!strcasecmp(v->name, "group")) { - group = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "autologoff")) { - autologoff = atoi(v->value); - if (autologoff < 0) - autologoff = 0; - } else if (!strcasecmp(v->name, "ackcall")) { - if (!strcasecmp(v->value, "always")) - ackcall = 2; - else if (ast_true(v->value)) - ackcall = 1; - else - ackcall = 0; - } else if (!strcasecmp(v->name, "endcall")) { - endcall = ast_true(v->value); - } else if (!strcasecmp(v->name, "wrapuptime")) { - wrapuptime = atoi(v->value); - if (wrapuptime < 0) - wrapuptime = 0; - } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) { - maxlogintries = atoi(v->value); - if (maxlogintries < 0) - maxlogintries = 0; - } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) { - strcpy(agentgoodbye,v->value); - } else if (!strcasecmp(v->name, "musiconhold")) { - ast_copy_string(moh, v->value, sizeof(moh)); - } else if (!strcasecmp(v->name, "updatecdr")) { - if (ast_true(v->value)) - updatecdr = 1; - else - updatecdr = 0; - } else if (!strcasecmp(v->name, "autologoffunavail")) { - if (ast_true(v->value)) - autologoffunavail = 1; - else - autologoffunavail = 0; - } else if (!strcasecmp(v->name, "recordagentcalls")) { - recordagentcalls = ast_true(v->value); - } else if (!strcasecmp(v->name, "recordformat")) { - ast_copy_string(recordformat, v->value, sizeof(recordformat)); - if (!strcasecmp(v->value, "wav49")) - strcpy(recordformatext, "WAV"); - else - ast_copy_string(recordformatext, v->value, sizeof(recordformatext)); - } else if (!strcasecmp(v->name, "urlprefix")) { - ast_copy_string(urlprefix, v->value, sizeof(urlprefix)); - if (urlprefix[strlen(urlprefix) - 1] != '/') - strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1); - } else if (!strcasecmp(v->name, "savecallsin")) { - if (v->value[0] == '/') - ast_copy_string(savecallsin, v->value, sizeof(savecallsin)); - else - snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value); - if (savecallsin[strlen(savecallsin) - 1] != '/') - strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1); - } else if (!strcasecmp(v->name, "custom_beep")) { - ast_copy_string(beep, v->value, sizeof(beep)); - } - v = v->next; - } - if ((ucfg = ast_config_load("users.conf"))) { - genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent")); - catname = ast_category_browse(ucfg, NULL); - while(catname) { - if (strcasecmp(catname, "general")) { - hasagent = ast_variable_retrieve(ucfg, catname, "hasagent"); - if (ast_true(hasagent) || (!hasagent && genhasagent)) { - char tmp[256]; - const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname"); - const char *secret = ast_variable_retrieve(ucfg, catname, "secret"); - if (!fullname) - fullname = ""; - if (!secret) - secret = ""; - snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname); - add_agent(tmp, 0); - } - } - catname = ast_category_browse(ucfg, catname); - } - ast_config_destroy(ucfg); - } - AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) { - if (p->dead) { - AST_LIST_REMOVE_CURRENT(&agents, list); - /* Destroy if appropriate */ - if (!p->owner) { - if (!p->chan) { - ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); - ast_cond_destroy(&p->app_complete_cond); - free(p); - } else { - /* Cause them to hang up */ - ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); - } - } - } - } - AST_LIST_TRAVERSE_SAFE_END - AST_LIST_UNLOCK(&agents); - ast_config_destroy(cfg); - return 1; -} - -static int check_availability(struct agent_pvt *newlyavailable, int needlock) -{ - struct ast_channel *chan=NULL, *parent=NULL; - struct agent_pvt *p; - int res; - - if (option_debug) - ast_log(LOG_DEBUG, "Checking availability of '%s'\n", newlyavailable->agent); - if (needlock) - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - if (p == newlyavailable) { - continue; - } - ast_mutex_lock(&p->lock); - if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) { - if (option_debug) - ast_log(LOG_DEBUG, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent); - /* We found a pending call, time to merge */ - chan = agent_new(newlyavailable, AST_STATE_DOWN); - parent = p->owner; - p->abouttograb = 1; - ast_mutex_unlock(&p->lock); - break; - } - ast_mutex_unlock(&p->lock); - } - if (needlock) - AST_LIST_UNLOCK(&agents); - if (parent && chan) { - if (newlyavailable->ackcall > 1) { - /* Don't do beep here */ - res = 0; - } else { - if (option_debug > 2) - ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language); - res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language); - if (option_debug > 2) - ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res); - if (!res) { - res = ast_waitstream(newlyavailable->chan, ""); - ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res); - } - } - if (!res) { - /* Note -- parent may have disappeared */ - if (p->abouttograb) { - newlyavailable->acknowledged = 1; - /* Safe -- agent lock already held */ - ast_setstate(parent, AST_STATE_UP); - ast_setstate(chan, AST_STATE_UP); - ast_copy_string(parent->context, chan->context, sizeof(parent->context)); - /* Go ahead and mark the channel as a zombie so that masquerade will - destroy it for us, and we need not call ast_hangup */ - ast_mutex_lock(&parent->lock); - ast_set_flag(chan, AST_FLAG_ZOMBIE); - ast_channel_masquerade(parent, chan); - ast_mutex_unlock(&parent->lock); - p->abouttograb = 0; - } else { - if (option_debug) - ast_log(LOG_DEBUG, "Sneaky, parent disappeared in the mean time...\n"); - agent_cleanup(newlyavailable); - } - } else { - if (option_debug) - ast_log(LOG_DEBUG, "Ugh... Agent hung up at exactly the wrong time\n"); - agent_cleanup(newlyavailable); - } - } - return 0; -} - -static int check_beep(struct agent_pvt *newlyavailable, int needlock) -{ - struct agent_pvt *p; - int res=0; - - ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent); - if (needlock) - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - if (p == newlyavailable) { - continue; - } - ast_mutex_lock(&p->lock); - if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) { - if (option_debug) - ast_log(LOG_DEBUG, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent); - ast_mutex_unlock(&p->lock); - break; - } - ast_mutex_unlock(&p->lock); - } - if (needlock) - AST_LIST_UNLOCK(&agents); - if (p) { - ast_mutex_unlock(&newlyavailable->lock); - if (option_debug > 2) - ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language); - res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language); - if (option_debug > 2) - ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res); - if (!res) { - res = ast_waitstream(newlyavailable->chan, ""); - if (option_debug) - ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res); - } - ast_mutex_lock(&newlyavailable->lock); - } - return res; -} - -/* return 1 if multiple login is fine, 0 if it is not and we find a match, -1 if multiplelogin is not allowed and we don't find a match. */ -static int allow_multiple_login(char *chan, char *context) -{ - struct agent_pvt *p; - char loginchan[80]; - - if (multiplelogin) { - return 1; - } - if (!chan) { - return 0; - } - - snprintf(loginchan, sizeof(loginchan), "%s@%s", chan, S_OR(context, "default")); - - AST_LIST_TRAVERSE(&agents, p, list) { - if(!strcasecmp(loginchan, p->loginchan)) - return 0; - } - return -1; -} - -/*! \brief Part of the Asterisk PBX interface */ -static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause) -{ - struct agent_pvt *p; - struct ast_channel *chan = NULL; - char *s; - ast_group_t groupmatch; - int groupoff; - int waitforagent=0; - int hasagent = 0; - struct timeval tv; - - s = data; - if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) { - groupmatch = (1 << groupoff); - } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) { - groupmatch = (1 << groupoff); - waitforagent = 1; - } else - groupmatch = 0; - - /* Check actual logged in agents first */ - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - ast_mutex_lock(&p->lock); - if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) && - ast_strlen_zero(p->loginchan)) { - if (p->chan) - hasagent++; - tv = ast_tvnow(); - if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) { - p->lastdisc = ast_tv(0, 0); - /* Agent must be registered, but not have any active call, and not be in a waiting state */ - if (!p->owner && p->chan) { - /* Fixed agent */ - chan = agent_new(p, AST_STATE_DOWN); - } - if (chan) { - ast_mutex_unlock(&p->lock); - break; - } - } - } - ast_mutex_unlock(&p->lock); - } - if (!p) { - AST_LIST_TRAVERSE(&agents, p, list) { - ast_mutex_lock(&p->lock); - if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) { - if (p->chan || !ast_strlen_zero(p->loginchan)) - hasagent++; - tv = ast_tvnow(); -#if 0 - ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec); -#endif - if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) { - p->lastdisc = ast_tv(0, 0); - /* Agent must be registered, but not have any active call, and not be in a waiting state */ - if (!p->owner && p->chan) { - /* Could still get a fixed agent */ - chan = agent_new(p, AST_STATE_DOWN); - } else if (!p->owner && !ast_strlen_zero(p->loginchan)) { - /* Adjustable agent */ - p->chan = ast_request("Local", format, p->loginchan, cause); - if (p->chan) - chan = agent_new(p, AST_STATE_DOWN); - } - if (chan) { - ast_mutex_unlock(&p->lock); - break; - } - } - } - ast_mutex_unlock(&p->lock); - } - } - - if (!chan && waitforagent) { - /* No agent available -- but we're requesting to wait for one. - Allocate a place holder */ - if (hasagent) { - if (option_debug) - ast_log(LOG_DEBUG, "Creating place holder for '%s'\n", s); - p = add_agent(data, 1); - p->group = groupmatch; - chan = agent_new(p, AST_STATE_DOWN); - if (!chan) - ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n"); - } else - ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s); - } - *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED; - AST_LIST_UNLOCK(&agents); - return chan; -} - -static force_inline int powerof(unsigned int d) -{ - int x = ffs(d); - - if (x) - return x - 1; - - return 0; -} - -/*! - * Lists agents and their status to the Manager API. - * It is registered on load_module() and it gets called by the manager backend. - * \param s - * \param m - * \returns - * \sa action_agent_logoff(), action_agent_callback_login(), load_module(). - */ -static int action_agents(struct mansession *s, const struct message *m) -{ - const char *id = astman_get_header(m,"ActionID"); - char idText[256] = ""; - char chanbuf[256]; - struct agent_pvt *p; - char *username = NULL; - char *loginChan = NULL; - char *talkingtoChan = NULL; - char *status = NULL; - - if (!ast_strlen_zero(id)) - snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id); - astman_send_ack(s, m, "Agents will follow"); - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - ast_mutex_lock(&p->lock); - - /* Status Values: - AGENT_LOGGEDOFF - Agent isn't logged in - AGENT_IDLE - Agent is logged in, and waiting for call - AGENT_ONCALL - Agent is logged in, and on a call - AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */ - - username = S_OR(p->name, "None"); - - /* Set a default status. It 'should' get changed. */ - status = "AGENT_UNKNOWN"; - - if (!ast_strlen_zero(p->loginchan) && !p->chan) { - loginChan = p->loginchan; - talkingtoChan = "n/a"; - status = "AGENT_IDLE"; - if (p->acknowledged) { - snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan); - loginChan = chanbuf; - } - } else if (p->chan) { - loginChan = ast_strdupa(p->chan->name); - if (p->owner && p->owner->_bridge) { - if (ast_bridged_channel(p->owner)) { - talkingtoChan = ast_strdupa(S_OR(ast_bridged_channel(p->owner)->cid.cid_num, "")); - } else { - talkingtoChan = "n/a"; - } - status = "AGENT_ONCALL"; - } else { - talkingtoChan = "n/a"; - status = "AGENT_IDLE"; - } - } else { - loginChan = "n/a"; - talkingtoChan = "n/a"; - status = "AGENT_LOGGEDOFF"; - } - - astman_append(s, "Event: Agents\r\n" - "Agent: %s\r\n" - "Name: %s\r\n" - "Status: %s\r\n" - "LoggedInChan: %s\r\n" - "LoggedInTime: %d\r\n" - "TalkingTo: %s\r\n" - "%s" - "\r\n", - p->agent, username, status, loginChan, (int)p->loginstart, talkingtoChan, idText); - ast_mutex_unlock(&p->lock); - } - AST_LIST_UNLOCK(&agents); - astman_append(s, "Event: AgentsComplete\r\n" - "%s" - "\r\n",idText); - return 0; -} - -static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand) -{ - char *tmp = NULL; - char agent[AST_MAX_AGENT]; - - if (!ast_strlen_zero(logcommand)) - tmp = logcommand; - else - tmp = ast_strdupa(""); - - snprintf(agent, sizeof(agent), "Agent/%s", p->agent); - - if (!ast_strlen_zero(uniqueid)) { - manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff", - "Agent: %s\r\n" - "Reason: %s\r\n" - "Loginchan: %s\r\n" - "Logintime: %ld\r\n" - "Uniqueid: %s\r\n", - p->agent, tmp, loginchan, logintime, uniqueid); - } else { - manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff", - "Agent: %s\r\n" - "Reason: %s\r\n" - "Loginchan: %s\r\n" - "Logintime: %ld\r\n", - p->agent, tmp, loginchan, logintime); - } - - ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp); - set_agentbycallerid(p->logincallerid, NULL); - p->loginchan[0] ='\0'; - p->logincallerid[0] = '\0'; - p->inherited_devicestate = -1; - ast_device_state_changed("Agent/%s", p->agent); - if (persistent_agents) - dump_agents(); - -} - -static int agent_logoff(const char *agent, int soft) -{ - struct agent_pvt *p; - long logintime; - int ret = -1; /* Return -1 if no agent if found */ - - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - if (!strcasecmp(p->agent, agent)) { - ret = 0; - if (p->owner || p->chan) { - if (!soft) { - ast_mutex_lock(&p->lock); - - while (p->owner && ast_channel_trylock(p->owner)) { - DEADLOCK_AVOIDANCE(&p->lock); - } - if (p->owner) { - ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT); - ast_channel_unlock(p->owner); - } - - while (p->chan && ast_channel_trylock(p->chan)) { - DEADLOCK_AVOIDANCE(&p->lock); - } - if (p->chan) { - ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); - ast_channel_unlock(p->chan); - } - - ast_mutex_unlock(&p->lock); - } else - p->deferlogoff = 1; - } else { - logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff"); - } - break; - } - } - AST_LIST_UNLOCK(&agents); - - return ret; -} - -static int agent_logoff_cmd(int fd, int argc, char **argv) -{ - int ret; - char *agent; - - if (argc < 3 || argc > 4) - return RESULT_SHOWUSAGE; - if (argc == 4 && strcasecmp(argv[3], "soft")) - return RESULT_SHOWUSAGE; - - agent = argv[2] + 6; - ret = agent_logoff(agent, argc == 4); - if (ret == 0) - ast_cli(fd, "Logging out %s\n", agent); - - return RESULT_SUCCESS; -} - -/*! - * Sets an agent as no longer logged in in the Manager API. - * It is registered on load_module() and it gets called by the manager backend. - * \param s - * \param m - * \returns - * \sa action_agents(), action_agent_callback_login(), load_module(). - */ -static int action_agent_logoff(struct mansession *s, const struct message *m) -{ - const char *agent = astman_get_header(m, "Agent"); - const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */ - int soft; - int ret; /* return value of agent_logoff */ - - if (ast_strlen_zero(agent)) { - astman_send_error(s, m, "No agent specified"); - return 0; - } - - soft = ast_true(soft_s) ? 1 : 0; - ret = agent_logoff(agent, soft); - if (ret == 0) - astman_send_ack(s, m, "Agent logged out"); - else - astman_send_error(s, m, "No such agent"); - - return 0; -} - -static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state) -{ - char *ret = NULL; - - if (pos == 2) { - struct agent_pvt *p; - char name[AST_MAX_AGENT]; - int which = 0, len = strlen(word); - - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - snprintf(name, sizeof(name), "Agent/%s", p->agent); - if (!strncasecmp(word, name, len) && ++which > state) { - ret = ast_strdup(name); - break; - } - } - AST_LIST_UNLOCK(&agents); - } else if (pos == 3 && state == 0) - return ast_strdup("soft"); - - return ret; -} - -/*! - * Show agents in cli. - */ -static int agents_show(int fd, int argc, char **argv) -{ - struct agent_pvt *p; - char username[AST_MAX_BUF]; - char location[AST_MAX_BUF] = ""; - char talkingto[AST_MAX_BUF] = ""; - char moh[AST_MAX_BUF]; - int count_agents = 0; /*!< Number of agents configured */ - int online_agents = 0; /*!< Number of online agents */ - int offline_agents = 0; /*!< Number of offline agents */ - if (argc != 2) - return RESULT_SHOWUSAGE; - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - ast_mutex_lock(&p->lock); - if (p->pending) { - if (p->group) - ast_cli(fd, "-- Pending call to group %d\n", powerof(p->group)); - else - ast_cli(fd, "-- Pending call to agent %s\n", p->agent); - } else { - if (!ast_strlen_zero(p->name)) - snprintf(username, sizeof(username), "(%s) ", p->name); - else - username[0] = '\0'; - if (p->chan) { - snprintf(location, sizeof(location), "logged in on %s", p->chan->name); - if (p->owner && ast_bridged_channel(p->owner)) - snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name); - else - strcpy(talkingto, " is idle"); - online_agents++; - } else if (!ast_strlen_zero(p->loginchan)) { - if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec)) - snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan); - else - snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan); - talkingto[0] = '\0'; - online_agents++; - if (p->acknowledged) - strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1); - } else { - strcpy(location, "not logged in"); - talkingto[0] = '\0'; - offline_agents++; - } - if (!ast_strlen_zero(p->moh)) - snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh); - ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, - username, location, talkingto, moh); - count_agents++; - } - ast_mutex_unlock(&p->lock); - } - AST_LIST_UNLOCK(&agents); - if ( !count_agents ) - ast_cli(fd, "No Agents are configured in %s\n",config); - else - ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents); - ast_cli(fd, "\n"); - - return RESULT_SUCCESS; -} - - -static int agents_show_online(int fd, int argc, char **argv) -{ - struct agent_pvt *p; - char username[AST_MAX_BUF]; - char location[AST_MAX_BUF] = ""; - char talkingto[AST_MAX_BUF] = ""; - char moh[AST_MAX_BUF]; - int count_agents = 0; /* Number of agents configured */ - int online_agents = 0; /* Number of online agents */ - int agent_status = 0; /* 0 means offline, 1 means online */ - if (argc != 3) - return RESULT_SHOWUSAGE; - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - agent_status = 0; /* reset it to offline */ - ast_mutex_lock(&p->lock); - if (!ast_strlen_zero(p->name)) - snprintf(username, sizeof(username), "(%s) ", p->name); - else - username[0] = '\0'; - if (p->chan) { - snprintf(location, sizeof(location), "logged in on %s", p->chan->name); - if (p->owner && ast_bridged_channel(p->owner)) - snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name); - else - strcpy(talkingto, " is idle"); - agent_status = 1; - online_agents++; - } else if (!ast_strlen_zero(p->loginchan)) { - snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan); - talkingto[0] = '\0'; - agent_status = 1; - online_agents++; - if (p->acknowledged) - strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1); - } - if (!ast_strlen_zero(p->moh)) - snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh); - if (agent_status) - ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh); - count_agents++; - ast_mutex_unlock(&p->lock); - } - AST_LIST_UNLOCK(&agents); - if (!count_agents) - ast_cli(fd, "No Agents are configured in %s\n", config); - else - ast_cli(fd, "%d agents online\n", online_agents); - ast_cli(fd, "\n"); - return RESULT_SUCCESS; -} - - - -static char show_agents_usage[] = -"Usage: agent show\n" -" Provides summary information on agents.\n"; - -static char show_agents_online_usage[] = -"Usage: agent show online\n" -" Provides a list of all online agents.\n"; - -static char agent_logoff_usage[] = -"Usage: agent logoff <channel> [soft]\n" -" Sets an agent as no longer logged in.\n" -" If 'soft' is specified, do not hangup existing calls.\n"; - -static struct ast_cli_entry cli_show_agents_deprecated = { - { "show", "agents", NULL }, - agents_show, NULL, - NULL, NULL }; - -static struct ast_cli_entry cli_show_agents_online_deprecated = { - { "show", "agents", "online" }, - agents_show_online, NULL, - NULL, NULL }; - -static struct ast_cli_entry cli_agents[] = { - { { "agent", "show", NULL }, - agents_show, "Show status of agents", - show_agents_usage, NULL, &cli_show_agents_deprecated }, - - { { "agent", "show", "online" }, - agents_show_online, "Show all online agents", - show_agents_online_usage, NULL, &cli_show_agents_online_deprecated }, - - { { "agent", "logoff", NULL }, - agent_logoff_cmd, "Sets an agent offline", - agent_logoff_usage, complete_agent_logoff_cmd }, -}; - -/*! - * \brief Log in agent application. - * - * \param chan - * \param data - * \param callbackmode non-zero for AgentCallbackLogin - */ -static int __login_exec(struct ast_channel *chan, void *data, int callbackmode) -{ - int res=0; - int tries = 0; - int max_login_tries = maxlogintries; - struct agent_pvt *p; - struct ast_module_user *u; - int login_state = 0; - char user[AST_MAX_AGENT] = ""; - char pass[AST_MAX_AGENT]; - char agent[AST_MAX_AGENT] = ""; - char xpass[AST_MAX_AGENT] = ""; - char *errmsg; - char *parse; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(agent_id); - AST_APP_ARG(options); - AST_APP_ARG(extension); - ); - const char *tmpoptions = NULL; - char *context = NULL; - int play_announcement = 1; - char agent_goodbye[AST_MAX_FILENAME_LEN]; - int update_cdr = updatecdr; - char *filename = "agent-loginok"; - char tmpchan[AST_MAX_BUF] = ""; - - u = ast_module_user_add(chan); - - parse = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(args, parse); - - ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye)); - - ast_channel_lock(chan); - /* Set Channel Specific Login Overrides */ - if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) { - max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES")); - if (max_login_tries < 0) - max_login_tries = 0; - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name); - } - if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) { - if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) - update_cdr = 1; - else - update_cdr = 0; - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name); - } - if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) { - strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE")); - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name); - } - ast_channel_unlock(chan); - /* End Channel Specific Login Overrides */ - - if (callbackmode && args.extension) { - parse = args.extension; - args.extension = strsep(&parse, "@"); - context = parse; - } - - if (!ast_strlen_zero(args.options)) { - if (strchr(args.options, 's')) { - play_announcement = 0; - } - } - - if (chan->_state != AST_STATE_UP) - res = ast_answer(chan); - if (!res) { - if (!ast_strlen_zero(args.agent_id)) - ast_copy_string(user, args.agent_id, AST_MAX_AGENT); - else - res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0); - } - while (!res && (max_login_tries==0 || tries < max_login_tries)) { - tries++; - /* Check for password */ - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - if (!strcmp(p->agent, user) && !p->pending) - ast_copy_string(xpass, p->password, sizeof(xpass)); - } - AST_LIST_UNLOCK(&agents); - if (!res) { - if (!ast_strlen_zero(xpass)) - res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0); - else - pass[0] = '\0'; - } - errmsg = "agent-incorrect"; - -#if 0 - ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass); -#endif - - /* Check again for accuracy */ - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - int unlock_channel = 1; - ast_channel_lock(chan); - ast_mutex_lock(&p->lock); - if (!strcmp(p->agent, user) && - !strcmp(p->password, pass) && !p->pending) { - login_state = 1; /* Successful Login */ - - /* Ensure we can't be gotten until we're done */ - gettimeofday(&p->lastdisc, NULL); - p->lastdisc.tv_sec++; - - /* Set Channel Specific Agent Overrides */ - if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) { - if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always")) - p->ackcall = 2; - else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) - p->ackcall = 1; - else - p->ackcall = 0; - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent); - } else { - p->ackcall = ackcall; - } - if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) { - p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF")); - if (p->autologoff < 0) - p->autologoff = 0; - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent); - } else { - p->autologoff = autologoff; - } - if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) { - p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME")); - if (p->wrapuptime < 0) - p->wrapuptime = 0; - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent); - } else { - p->wrapuptime = wrapuptime; - } - ast_channel_unlock(chan); - unlock_channel = 0; - /* End Channel Specific Agent Overrides */ - if (!p->chan) { - char last_loginchan[80] = ""; - long logintime; - snprintf(agent, sizeof(agent), "Agent/%s", p->agent); - - if (callbackmode) { - int pos = 0; - /* Retrieve login chan */ - for (;;) { - if (!ast_strlen_zero(args.extension)) { - ast_copy_string(tmpchan, args.extension, sizeof(tmpchan)); - res = 0; - } else - res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0); - if (ast_strlen_zero(tmpchan) ) - break; - if(ast_exists_extension(chan, S_OR(context,"default"), tmpchan,1, NULL) ) { - if(!allow_multiple_login(tmpchan,context) ) { - args.extension = NULL; - pos = 0; - } else - break; - } - if (args.extension) { - ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", args.extension, p->agent); - args.extension = NULL; - pos = 0; - } else { - ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, S_OR(context, "default"), p->agent); - res = ast_streamfile(chan, "invalid", chan->language); - if (!res) - res = ast_waitstream(chan, AST_DIGIT_ANY); - if (res > 0) { - tmpchan[0] = res; - tmpchan[1] = '\0'; - pos = 1; - } else { - tmpchan[0] = '\0'; - pos = 0; - } - } - } - args.extension = tmpchan; - if (!res) { - set_agentbycallerid(p->logincallerid, NULL); - if (!ast_strlen_zero(context) && !ast_strlen_zero(tmpchan)) - snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context); - else { - ast_copy_string(last_loginchan, p->loginchan, sizeof(last_loginchan)); - ast_copy_string(p->loginchan, tmpchan, sizeof(p->loginchan)); - } - p->acknowledged = 0; - if (ast_strlen_zero(p->loginchan)) { - login_state = 2; - filename = "agent-loggedoff"; - } else { - if (chan->cid.cid_num) { - ast_copy_string(p->logincallerid, chan->cid.cid_num, sizeof(p->logincallerid)); - set_agentbycallerid(p->logincallerid, p->agent); - } else - p->logincallerid[0] = '\0'; - } - - if(update_cdr && chan->cdr) - snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent); - - } - } else { - p->loginchan[0] = '\0'; - p->logincallerid[0] = '\0'; - p->acknowledged = 0; - } - ast_mutex_unlock(&p->lock); - AST_LIST_UNLOCK(&agents); - if( !res && play_announcement==1 ) - res = ast_streamfile(chan, filename, chan->language); - if (!res) - ast_waitstream(chan, ""); - AST_LIST_LOCK(&agents); - ast_mutex_lock(&p->lock); - if (!res) { - res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats)); - if (res) - ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats)); - } - if (!res) { - res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats)); - if (res) - ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats)); - } - /* Check once more just in case */ - if (p->chan) - res = -1; - if (callbackmode && !res) { - /* Just say goodbye and be done with it */ - if (!ast_strlen_zero(p->loginchan)) { - if (p->loginstart == 0) - time(&p->loginstart); - manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin", - "Agent: %s\r\n" - "Loginchan: %s\r\n" - "Uniqueid: %s\r\n", - p->agent, p->loginchan, chan->uniqueid); - ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan); - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan); - ast_device_state_changed("Agent/%s", p->agent); - if (persistent_agents) - dump_agents(); - } else { - logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - - agent_logoff_maintenance(p, last_loginchan, logintime, chan->uniqueid, NULL); - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged out\n", p->agent); - } - AST_LIST_UNLOCK(&agents); - if (!res) - res = ast_safe_sleep(chan, 500); - ast_mutex_unlock(&p->lock); - } else if (!res) { - ast_indicate_data(chan, AST_CONTROL_HOLD, - S_OR(p->moh, NULL), - !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0); - if (p->loginstart == 0) - time(&p->loginstart); - manager_event(EVENT_FLAG_AGENT, "Agentlogin", - "Agent: %s\r\n" - "Channel: %s\r\n" - "Uniqueid: %s\r\n", - p->agent, chan->name, chan->uniqueid); - if (update_cdr && chan->cdr) - snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent); - ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name); - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged in (format %s/%s)\n", p->agent, - ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat)); - /* Login this channel and wait for it to go away */ - p->chan = chan; - if (p->ackcall > 1) - check_beep(p, 0); - else - check_availability(p, 0); - ast_mutex_unlock(&p->lock); - AST_LIST_UNLOCK(&agents); - ast_device_state_changed("Agent/%s", p->agent); - while (res >= 0) { - ast_mutex_lock(&p->lock); - if (p->deferlogoff && p->chan) { - ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); - p->deferlogoff = 0; - } - if (p->chan != chan) - res = -1; - ast_mutex_unlock(&p->lock); - /* Yield here so other interested threads can kick in. */ - sched_yield(); - if (res) - break; - - AST_LIST_LOCK(&agents); - ast_mutex_lock(&p->lock); - if (p->lastdisc.tv_sec) { - if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) { - if (option_debug) - ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent); - p->lastdisc = ast_tv(0, 0); - ast_device_state_changed("Agent/%s", p->agent); - if (p->ackcall > 1) - check_beep(p, 0); - else - check_availability(p, 0); - } - } - ast_mutex_unlock(&p->lock); - AST_LIST_UNLOCK(&agents); - /* Synchronize channel ownership between call to agent and itself. */ - ast_mutex_lock(&p->app_lock); - if (p->app_lock_flag == 1) { - ast_cond_wait(&p->app_complete_cond, &p->app_lock); - } - ast_mutex_unlock(&p->app_lock); - ast_mutex_lock(&p->lock); - ast_mutex_unlock(&p->lock); - if (p->ackcall > 1) - res = agent_ack_sleep(p); - else - res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p ); - if ((p->ackcall > 1) && (res == 1)) { - AST_LIST_LOCK(&agents); - ast_mutex_lock(&p->lock); - check_availability(p, 0); - ast_mutex_unlock(&p->lock); - AST_LIST_UNLOCK(&agents); - res = 0; - } - sched_yield(); - } - ast_mutex_lock(&p->lock); - if (res && p->owner) - ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n"); - /* Log us off if appropriate */ - if (p->chan == chan) { - p->chan = NULL; - p->inherited_devicestate = -1; - } - p->acknowledged = 0; - logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - ast_mutex_unlock(&p->lock); - manager_event(EVENT_FLAG_AGENT, "Agentlogoff", - "Agent: %s\r\n" - "Logintime: %ld\r\n" - "Uniqueid: %s\r\n", - p->agent, logintime, chan->uniqueid); - ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime); - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged out\n", p->agent); - /* If there is no owner, go ahead and kill it now */ - ast_device_state_changed("Agent/%s", p->agent); - if (p->dead && !p->owner) { - ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); - ast_cond_destroy(&p->app_complete_cond); - free(p); - } - } - else { - ast_mutex_unlock(&p->lock); - p = NULL; - } - res = -1; - } else { - ast_mutex_unlock(&p->lock); - errmsg = "agent-alreadyon"; - p = NULL; - } - break; - } - ast_mutex_unlock(&p->lock); - if (unlock_channel) { - ast_channel_unlock(chan); - } - } - if (!p) - AST_LIST_UNLOCK(&agents); - - if (!res && (max_login_tries==0 || tries < max_login_tries)) - res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0); - } - - if (!res) - res = ast_safe_sleep(chan, 500); - - /* AgentLogin() exit */ - if (!callbackmode) { - ast_module_user_remove(u); - return -1; - } else { /* AgentCallbackLogin() exit*/ - /* Set variables */ - if (login_state > 0) { - pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user); - if (login_state==1) { - pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on"); - pbx_builtin_setvar_helper(chan, "AGENTEXTEN", args.extension); - } else - pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off"); - } else { - pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail"); - } - if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) { - ast_module_user_remove(u); - return 0; - } - /* Do we need to play agent-goodbye now that we will be hanging up? */ - if (play_announcement) { - if (!res) - res = ast_safe_sleep(chan, 1000); - res = ast_streamfile(chan, agent_goodbye, chan->language); - if (!res) - res = ast_waitstream(chan, ""); - if (!res) - res = ast_safe_sleep(chan, 1000); - } - } - - ast_module_user_remove(u); - - /* We should never get here if next priority exists when in callbackmode */ - return -1; -} - -/*! - * Called by the AgentLogin application (from the dial plan). - * - * \param chan - * \param data - * \returns - * \sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module(). - */ -static int login_exec(struct ast_channel *chan, void *data) -{ - return __login_exec(chan, data, 0); -} - -static void callback_deprecated(void) -{ - static int depwarning = 0; - - if (!depwarning) { - depwarning = 1; - - ast_log(LOG_WARNING, "AgentCallbackLogin is deprecated and will be removed in a future release.\n"); - ast_log(LOG_WARNING, "See doc/queues-with-callback-members.txt for an example of how to achieve\n"); - ast_log(LOG_WARNING, "the same functionality using only dialplan logic.\n"); - } -} - -/*! - * Called by the AgentCallbackLogin application (from the dial plan). - * - * \param chan - * \param data - * \returns - * \sa login_exec(), agentmonitoroutgoing_exec(), load_module(). - */ -static int callback_exec(struct ast_channel *chan, void *data) -{ - callback_deprecated(); - - return __login_exec(chan, data, 1); -} - -/*! - * Sets an agent as logged in by callback in the Manager API. - * It is registered on load_module() and it gets called by the manager backend. - * \param s - * \param m - * \returns - * \sa action_agents(), action_agent_logoff(), load_module(). - */ -static int action_agent_callback_login(struct mansession *s, const struct message *m) -{ - const char *agent = astman_get_header(m, "Agent"); - const char *exten = astman_get_header(m, "Exten"); - const char *context = astman_get_header(m, "Context"); - const char *wrapuptime_s = astman_get_header(m, "WrapupTime"); - const char *ackcall_s = astman_get_header(m, "AckCall"); - struct agent_pvt *p; - int login_state = 0; - - callback_deprecated(); - - if (ast_strlen_zero(agent)) { - astman_send_error(s, m, "No agent specified"); - return 0; - } - - if (ast_strlen_zero(exten)) { - astman_send_error(s, m, "No extension specified"); - return 0; - } - - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - if (strcmp(p->agent, agent) || p->pending) - continue; - if (p->chan) { - login_state = 2; /* already logged in (and on the phone)*/ - break; - } - ast_mutex_lock(&p->lock); - login_state = 1; /* Successful Login */ - - if (ast_strlen_zero(context)) - ast_copy_string(p->loginchan, exten, sizeof(p->loginchan)); - else - snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", exten, context); - - if (!ast_strlen_zero(wrapuptime_s)) { - p->wrapuptime = atoi(wrapuptime_s); - if (p->wrapuptime < 0) - p->wrapuptime = 0; - } - - if (!strcasecmp(ackcall_s, "always")) - p->ackcall = 2; - else if (ast_true(ackcall_s)) - p->ackcall = 1; - else - p->ackcall = 0; - - if (p->loginstart == 0) - time(&p->loginstart); - manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin", - "Agent: %s\r\n" - "Loginchan: %s\r\n", - p->agent, p->loginchan); - ast_queue_log("NONE", "NONE", agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan); - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan); - ast_device_state_changed("Agent/%s", p->agent); - ast_mutex_unlock(&p->lock); - if (persistent_agents) - dump_agents(); - } - AST_LIST_UNLOCK(&agents); - - if (login_state == 1) - astman_send_ack(s, m, "Agent logged in"); - else if (login_state == 0) - astman_send_error(s, m, "No such agent"); - else if (login_state == 2) - astman_send_error(s, m, "Agent already logged in"); - - return 0; -} - -/*! - * \brief Called by the AgentMonitorOutgoing application (from the dial plan). - * - * \param chan - * \param data - * \returns - * \sa login_exec(), callback_login_exec(), load_module(). - */ -static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data) -{ - int exitifnoagentid = 0; - int nowarnings = 0; - int changeoutgoing = 0; - int res = 0; - char agent[AST_MAX_AGENT]; - - if (data) { - if (strchr(data, 'd')) - exitifnoagentid = 1; - if (strchr(data, 'n')) - nowarnings = 1; - if (strchr(data, 'c')) - changeoutgoing = 1; - } - if (chan->cid.cid_num) { - const char *tmp; - char agentvar[AST_MAX_BUF]; - snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num); - if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) { - struct agent_pvt *p; - ast_copy_string(agent, tmp, sizeof(agent)); - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - if (!strcasecmp(p->agent, tmp)) { - if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent); - __agent_start_monitoring(chan, p, 1); - break; - } - } - AST_LIST_UNLOCK(&agents); - - } else { - res = -1; - if (!nowarnings) - ast_log(LOG_WARNING, "Couldn't find the global variable %s, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n", agentvar); - } - } else { - res = -1; - if (!nowarnings) - ast_log(LOG_WARNING, "There is no callerid on that call, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n"); - } - /* check if there is n + 101 priority */ - /*! \todo XXX Needs to check option priorityjump etc etc */ - if (res) { - if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) { - chan->priority+=100; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan->priority); - } else if (exitifnoagentid) - return res; - } - return 0; -} - -/*! - * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence - */ -static void dump_agents(void) -{ - struct agent_pvt *cur_agent = NULL; - char buf[256]; - - AST_LIST_TRAVERSE(&agents, cur_agent, list) { - if (cur_agent->chan) - continue; - - if (!ast_strlen_zero(cur_agent->loginchan)) { - snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid); - if (ast_db_put(pa_family, cur_agent->agent, buf)) - ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf); - else if (option_debug) - ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan); - } else { - /* Delete - no agent or there is an error */ - ast_db_del(pa_family, cur_agent->agent); - } - } -} - -/*! - * \brief Reload the persistent agents from astdb. - */ -static void reload_agents(void) -{ - char *agent_num; - struct ast_db_entry *db_tree; - struct ast_db_entry *entry; - struct agent_pvt *cur_agent; - char agent_data[256]; - char *parse; - char *agent_chan; - char *agent_callerid; - - db_tree = ast_db_gettree(pa_family, NULL); - - AST_LIST_LOCK(&agents); - for (entry = db_tree; entry; entry = entry->next) { - agent_num = entry->key + strlen(pa_family) + 2; - AST_LIST_TRAVERSE(&agents, cur_agent, list) { - ast_mutex_lock(&cur_agent->lock); - if (strcmp(agent_num, cur_agent->agent) == 0) - break; - ast_mutex_unlock(&cur_agent->lock); - } - if (!cur_agent) { - ast_db_del(pa_family, agent_num); - continue; - } else - ast_mutex_unlock(&cur_agent->lock); - if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) { - if (option_debug) - ast_log(LOG_DEBUG, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data); - parse = agent_data; - agent_chan = strsep(&parse, ";"); - agent_callerid = strsep(&parse, ";"); - ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan)); - if (agent_callerid) { - ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid)); - set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent); - } else - cur_agent->logincallerid[0] = '\0'; - if (cur_agent->loginstart == 0) - time(&cur_agent->loginstart); - ast_device_state_changed("Agent/%s", cur_agent->agent); - } - } - AST_LIST_UNLOCK(&agents); - if (db_tree) { - ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n"); - ast_db_freetree(db_tree); - } -} - -/*! \brief Part of PBX channel interface */ -static int agent_devicestate(void *data) -{ - struct agent_pvt *p; - char *s; - ast_group_t groupmatch; - int groupoff; - int waitforagent=0; - int res = AST_DEVICE_INVALID; - - s = data; - if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) - groupmatch = (1 << groupoff); - else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) { - groupmatch = (1 << groupoff); - waitforagent = 1; - } else - groupmatch = 0; - - /* Check actual logged in agents first */ - AST_LIST_LOCK(&agents); - AST_LIST_TRAVERSE(&agents, p, list) { - ast_mutex_lock(&p->lock); - if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) { - if (p->owner) { - if (res != AST_DEVICE_INUSE) - res = AST_DEVICE_BUSY; - } else if (p->inherited_devicestate > -1) { - res = p->inherited_devicestate; - } else { - if (res == AST_DEVICE_BUSY) - res = AST_DEVICE_INUSE; - if (p->chan || !ast_strlen_zero(p->loginchan)) { - if (res == AST_DEVICE_INVALID) - res = AST_DEVICE_UNKNOWN; - } else if (res == AST_DEVICE_INVALID) - res = AST_DEVICE_UNAVAILABLE; - } - if (!strcmp(data, p->agent)) { - ast_mutex_unlock(&p->lock); - break; - } - } - ast_mutex_unlock(&p->lock); - } - AST_LIST_UNLOCK(&agents); - return res; -} - -/*! - * \note This function expects the agent list to be locked - */ -static struct agent_pvt *find_agent(char *agentid) -{ - struct agent_pvt *cur; - - AST_LIST_TRAVERSE(&agents, cur, list) { - if (!strcmp(cur->agent, agentid)) - break; - } - - return cur; -} - -static int function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) -{ - char *parse; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(agentid); - AST_APP_ARG(item); - ); - char *tmp; - struct agent_pvt *agent; - - buf[0] = '\0'; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n"); - return -1; - } - - parse = ast_strdupa(data); - - AST_NONSTANDARD_APP_ARGS(args, parse, ':'); - if (!args.item) - args.item = "status"; - - AST_LIST_LOCK(&agents); - - if (!(agent = find_agent(args.agentid))) { - AST_LIST_UNLOCK(&agents); - ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid); - return -1; - } - - if (!strcasecmp(args.item, "status")) { - char *status = "LOGGEDOUT"; - if (agent->chan || !ast_strlen_zero(agent->loginchan)) - status = "LOGGEDIN"; - ast_copy_string(buf, status, len); - } else if (!strcasecmp(args.item, "password")) - ast_copy_string(buf, agent->password, len); - else if (!strcasecmp(args.item, "name")) - ast_copy_string(buf, agent->name, len); - else if (!strcasecmp(args.item, "mohclass")) - ast_copy_string(buf, agent->moh, len); - else if (!strcasecmp(args.item, "channel")) { - if (agent->chan) { - ast_copy_string(buf, agent->chan->name, len); - tmp = strrchr(buf, '-'); - if (tmp) - *tmp = '\0'; - } - } else if (!strcasecmp(args.item, "exten")) - ast_copy_string(buf, agent->loginchan, len); - - AST_LIST_UNLOCK(&agents); - - return 0; -} - -struct ast_custom_function agent_function = { - .name = "AGENT", - .synopsis = "Gets information about an Agent", - .syntax = "AGENT(<agentid>[:item])", - .read = function_agent, - .desc = "The valid items to retrieve are:\n" - "- status (default) The status of the agent\n" - " LOGGEDIN | LOGGEDOUT\n" - "- password The password of the agent\n" - "- name The name of the agent\n" - "- mohclass MusicOnHold class\n" - "- exten The callback extension for the Agent (AgentCallbackLogin)\n" - "- channel The name of the active channel for the Agent (AgentLogin)\n" -}; - - -/*! - * \brief Initialize the Agents module. - * This function is being called by Asterisk when loading the module. - * Among other things it registers applications, cli commands and reads the cofiguration file. - * - * \returns int Always 0. - */ -static int load_module(void) -{ - /* Make sure we can register our agent channel type */ - if (ast_channel_register(&agent_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n"); - return -1; - } - /* Read in the config */ - if (!read_agent_config()) - return AST_MODULE_LOAD_DECLINE; - if (persistent_agents) - reload_agents(); - /* Dialplan applications */ - ast_register_application(app, login_exec, synopsis, descrip); - ast_register_application(app2, callback_exec, synopsis2, descrip2); - ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3); - - /* Manager commands */ - ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents); - ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff); - ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login); - - /* CLI Commands */ - ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry)); - - /* Dialplan Functions */ - ast_custom_function_register(&agent_function); - - ast_devstate_add(agent_devicestate_cb, NULL); - - return 0; -} - -static int reload(void) -{ - read_agent_config(); - if (persistent_agents) - reload_agents(); - return 0; -} - -static int unload_module(void) -{ - struct agent_pvt *p; - /* First, take us out of the channel loop */ - ast_channel_unregister(&agent_tech); - /* Delete devicestate subscription */ - ast_devstate_del(agent_devicestate_cb, NULL); - /* Unregister dialplan functions */ - ast_custom_function_unregister(&agent_function); - /* Unregister CLI commands */ - ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry)); - /* Unregister dialplan applications */ - ast_unregister_application(app); - ast_unregister_application(app2); - ast_unregister_application(app3); - /* Unregister manager command */ - ast_manager_unregister("Agents"); - ast_manager_unregister("AgentLogoff"); - ast_manager_unregister("AgentCallbackLogin"); - /* Unregister channel */ - AST_LIST_LOCK(&agents); - /* Hangup all interfaces if they have an owner */ - while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) { - if (p->owner) - ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD); - free(p); - } - AST_LIST_UNLOCK(&agents); - return 0; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel", - .load = load_module, - .unload = unload_module, - .reload = reload, - ); diff --git a/1.4.23-rc4/channels/chan_alsa.c b/1.4.23-rc4/channels/chan_alsa.c deleted file mode 100644 index 03db26b6a..000000000 --- a/1.4.23-rc4/channels/chan_alsa.c +++ /dev/null @@ -1,1391 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * By Matthew Fredrickson <creslin@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * \brief ALSA sound card channel driver - * - * \author Matthew Fredrickson <creslin@digium.com> - * - * \par See also - * \arg Config_alsa - * - * \ingroup channel_drivers - */ - -/*** MODULEINFO - <depend>asound</depend> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/ioctl.h> -#include <sys/time.h> -#include <string.h> -#include <stdlib.h> -#include <stdio.h> - -#define ALSA_PCM_NEW_HW_PARAMS_API -#define ALSA_PCM_NEW_SW_PARAMS_API -#include <alsa/asoundlib.h> - -#include "asterisk/frame.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/module.h" -#include "asterisk/options.h" -#include "asterisk/pbx.h" -#include "asterisk/config.h" -#include "asterisk/cli.h" -#include "asterisk/utils.h" -#include "asterisk/causes.h" -#include "asterisk/endian.h" -#include "asterisk/stringfields.h" -#include "asterisk/abstract_jb.h" -#include "asterisk/musiconhold.h" - -#include "busy_tone.h" -#include "ring_tone.h" -#include "ring10.h" -#include "answer.h" - -#ifdef ALSA_MONITOR -#include "alsa-monitor.h" -#endif - -/*! Global jitterbuffer configuration - by default, jb is disabled */ -static struct ast_jb_conf default_jbconf = { - .flags = 0, - .max_size = -1, - .resync_threshold = -1, - .impl = "" -}; -static struct ast_jb_conf global_jbconf; - -#define DEBUG 0 -/* Which device to use */ -#define ALSA_INDEV "default" -#define ALSA_OUTDEV "default" -#define DESIRED_RATE 8000 - -/* Lets use 160 sample frames, just like GSM. */ -#define FRAME_SIZE 160 -#define PERIOD_FRAMES 80 /* 80 Frames, at 2 bytes each */ - -/* When you set the frame size, you have to come up with - the right buffer format as well. */ -/* 5 64-byte frames = one frame */ -#define BUFFER_FMT ((buffersize * 10) << 16) | (0x0006); - -/* Don't switch between read/write modes faster than every 300 ms */ -#define MIN_SWITCH_TIME 600 - -#if __BYTE_ORDER == __LITTLE_ENDIAN -static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; -#else -static snd_pcm_format_t format = SND_PCM_FORMAT_S16_BE; -#endif - -static char indevname[50] = ALSA_INDEV; -static char outdevname[50] = ALSA_OUTDEV; - -#if 0 -static struct timeval lasttime; -#endif - -static int silencesuppression = 0; -static int silencethreshold = 1000; - -AST_MUTEX_DEFINE_STATIC(alsalock); - -static const char tdesc[] = "ALSA Console Channel Driver"; -static const char config[] = "alsa.conf"; - -static char context[AST_MAX_CONTEXT] = "default"; -static char language[MAX_LANGUAGE] = ""; -static char exten[AST_MAX_EXTENSION] = "s"; -static char mohinterpret[MAX_MUSICCLASS]; - -static int hookstate = 0; - -static short silence[FRAME_SIZE] = { 0, }; - -struct sound { - int ind; - short *data; - int datalen; - int samplen; - int silencelen; - int repeat; -}; - -static struct sound sounds[] = { - {AST_CONTROL_RINGING, ringtone, sizeof(ringtone) / 2, 16000, 32000, 1}, - {AST_CONTROL_BUSY, busy, sizeof(busy) / 2, 4000, 4000, 1}, - {AST_CONTROL_CONGESTION, busy, sizeof(busy) / 2, 2000, 2000, 1}, - {AST_CONTROL_RING, ring10, sizeof(ring10) / 2, 16000, 32000, 1}, - {AST_CONTROL_ANSWER, answer, sizeof(answer) / 2, 2200, 0, 0}, -}; - -/* Sound command pipe */ -static int sndcmd[2]; - -static struct chan_alsa_pvt { - /* We only have one ALSA structure -- near sighted perhaps, but it - keeps this driver as simple as possible -- as it should be. */ - struct ast_channel *owner; - char exten[AST_MAX_EXTENSION]; - char context[AST_MAX_CONTEXT]; -#if 0 - snd_pcm_t *card; -#endif - snd_pcm_t *icard, *ocard; - -} alsa; - -/* Number of buffers... Each is FRAMESIZE/8 ms long. For example - with 160 sample frames, and a buffer size of 3, we have a 60ms buffer, - usually plenty. */ - -pthread_t sthread; - -#define MAX_BUFFER_SIZE 100 - -/* File descriptors for sound device */ -static int readdev = -1; -static int writedev = -1; - -static int autoanswer = 1; - -static int cursound = -1; -static int sampsent = 0; -static int silencelen = 0; -static int offset = 0; -static int nosound = 0; - -/* ZZ */ -static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause); -static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration); -static int alsa_text(struct ast_channel *c, const char *text); -static int alsa_hangup(struct ast_channel *c); -static int alsa_answer(struct ast_channel *c); -static struct ast_frame *alsa_read(struct ast_channel *chan); -static int alsa_call(struct ast_channel *c, char *dest, int timeout); -static int alsa_write(struct ast_channel *chan, struct ast_frame *f); -static int alsa_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen); -static int alsa_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); - -static const struct ast_channel_tech alsa_tech = { - .type = "Console", - .description = tdesc, - .capabilities = AST_FORMAT_SLINEAR, - .requester = alsa_request, - .send_digit_end = alsa_digit, - .send_text = alsa_text, - .hangup = alsa_hangup, - .answer = alsa_answer, - .read = alsa_read, - .call = alsa_call, - .write = alsa_write, - .indicate = alsa_indicate, - .fixup = alsa_fixup, -}; - -static int send_sound(void) -{ - short myframe[FRAME_SIZE]; - int total = FRAME_SIZE; - short *frame = NULL; - int amt = 0, res, myoff; - snd_pcm_state_t state; - - if (cursound == -1) - return 0; - - res = total; - if (sampsent < sounds[cursound].samplen) { - myoff = 0; - while (total) { - amt = total; - if (amt > (sounds[cursound].datalen - offset)) - amt = sounds[cursound].datalen - offset; - memcpy(myframe + myoff, sounds[cursound].data + offset, amt * 2); - total -= amt; - offset += amt; - sampsent += amt; - myoff += amt; - if (offset >= sounds[cursound].datalen) - offset = 0; - } - /* Set it up for silence */ - if (sampsent >= sounds[cursound].samplen) - silencelen = sounds[cursound].silencelen; - frame = myframe; - } else { - if (silencelen > 0) { - frame = silence; - silencelen -= res; - } else { - if (sounds[cursound].repeat) { - /* Start over */ - sampsent = 0; - offset = 0; - } else { - cursound = -1; - nosound = 0; - } - return 0; - } - } - - if (res == 0 || !frame) - return 0; - -#ifdef ALSA_MONITOR - alsa_monitor_write((char *) frame, res * 2); -#endif - state = snd_pcm_state(alsa.ocard); - if (state == SND_PCM_STATE_XRUN) - snd_pcm_prepare(alsa.ocard); - while ((res = snd_pcm_writei(alsa.ocard, frame, res)) == -EAGAIN) { - usleep(1); - } - if (res > 0) - return 0; - return 0; -} - -static void *sound_thread(void *unused) -{ - fd_set rfds; - fd_set wfds; - int max, res; - - for (;;) { - FD_ZERO(&rfds); - FD_ZERO(&wfds); - max = sndcmd[0]; - FD_SET(sndcmd[0], &rfds); - if (cursound > -1) { - FD_SET(writedev, &wfds); - if (writedev > max) - max = writedev; - } -#ifdef ALSA_MONITOR - if (!alsa.owner) { - FD_SET(readdev, &rfds); - if (readdev > max) - max = readdev; - } -#endif - res = ast_select(max + 1, &rfds, &wfds, NULL, NULL); - if (res < 1) { - ast_log(LOG_WARNING, "select failed: %s\n", strerror(errno)); - continue; - } -#ifdef ALSA_MONITOR - if (FD_ISSET(readdev, &rfds)) { - /* Keep the pipe going with read audio */ - snd_pcm_state_t state; - short buf[FRAME_SIZE]; - int r; - - state = snd_pcm_state(alsa.ocard); - if (state == SND_PCM_STATE_XRUN) { - snd_pcm_prepare(alsa.ocard); - } - r = snd_pcm_readi(alsa.icard, buf, FRAME_SIZE); - if (r == -EPIPE) { -#if DEBUG - ast_log(LOG_ERROR, "XRUN read\n"); -#endif - snd_pcm_prepare(alsa.icard); - } else if (r == -ESTRPIPE) { - ast_log(LOG_ERROR, "-ESTRPIPE\n"); - snd_pcm_prepare(alsa.icard); - } else if (r < 0) { - ast_log(LOG_ERROR, "Read error: %s\n", snd_strerror(r)); - } else - alsa_monitor_read((char *) buf, r * 2); - } -#endif - if (FD_ISSET(sndcmd[0], &rfds)) { - if (read(sndcmd[0], &cursound, sizeof(cursound)) < 0) { - ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno)); - } - silencelen = 0; - offset = 0; - sampsent = 0; - } - if (FD_ISSET(writedev, &wfds)) - if (send_sound()) - ast_log(LOG_WARNING, "Failed to write sound\n"); - } - /* Never reached */ - return NULL; -} - -static snd_pcm_t *alsa_card_init(char *dev, snd_pcm_stream_t stream) -{ - int err; - int direction; - snd_pcm_t *handle = NULL; - snd_pcm_hw_params_t *hwparams = NULL; - snd_pcm_sw_params_t *swparams = NULL; - struct pollfd pfd; - snd_pcm_uframes_t period_size = PERIOD_FRAMES * 4; - /* int period_bytes = 0; */ - snd_pcm_uframes_t buffer_size = 0; - - unsigned int rate = DESIRED_RATE; -#if 0 - unsigned int per_min = 1; -#endif - /* unsigned int per_max = 8; */ - snd_pcm_uframes_t start_threshold, stop_threshold; - - err = snd_pcm_open(&handle, dev, stream, SND_PCM_NONBLOCK); - if (err < 0) { - ast_log(LOG_ERROR, "snd_pcm_open failed: %s\n", snd_strerror(err)); - return NULL; - } else - ast_log(LOG_DEBUG, "Opening device %s in %s mode\n", dev, (stream == SND_PCM_STREAM_CAPTURE) ? "read" : "write"); - - hwparams = alloca(snd_pcm_hw_params_sizeof()); - memset(hwparams, 0, snd_pcm_hw_params_sizeof()); - snd_pcm_hw_params_any(handle, hwparams); - - err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); - if (err < 0) - ast_log(LOG_ERROR, "set_access failed: %s\n", snd_strerror(err)); - - err = snd_pcm_hw_params_set_format(handle, hwparams, format); - if (err < 0) - ast_log(LOG_ERROR, "set_format failed: %s\n", snd_strerror(err)); - - err = snd_pcm_hw_params_set_channels(handle, hwparams, 1); - if (err < 0) - ast_log(LOG_ERROR, "set_channels failed: %s\n", snd_strerror(err)); - - direction = 0; - err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, &direction); - if (rate != DESIRED_RATE) - ast_log(LOG_WARNING, "Rate not correct, requested %d, got %d\n", DESIRED_RATE, rate); - - direction = 0; - err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_size, &direction); - if (err < 0) - ast_log(LOG_ERROR, "period_size(%ld frames) is bad: %s\n", period_size, snd_strerror(err)); - else - ast_log(LOG_DEBUG, "Period size is %d\n", err); - - buffer_size = 4096 * 2; /* period_size * 16; */ - err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_size); - if (err < 0) - ast_log(LOG_WARNING, "Problem setting buffer size of %ld: %s\n", buffer_size, snd_strerror(err)); - else - ast_log(LOG_DEBUG, "Buffer size is set to %d frames\n", err); - -#if 0 - direction = 0; - err = snd_pcm_hw_params_set_periods_min(handle, hwparams, &per_min, &direction); - if (err < 0) - ast_log(LOG_ERROR, "periods_min: %s\n", snd_strerror(err)); - - err = snd_pcm_hw_params_set_periods_max(handle, hwparams, &per_max, 0); - if (err < 0) - ast_log(LOG_ERROR, "periods_max: %s\n", snd_strerror(err)); -#endif - - err = snd_pcm_hw_params(handle, hwparams); - if (err < 0) - ast_log(LOG_ERROR, "Couldn't set the new hw params: %s\n", snd_strerror(err)); - - swparams = alloca(snd_pcm_sw_params_sizeof()); - memset(swparams, 0, snd_pcm_sw_params_sizeof()); - snd_pcm_sw_params_current(handle, swparams); - -#if 1 - if (stream == SND_PCM_STREAM_PLAYBACK) - start_threshold = period_size; - else - start_threshold = 1; - - err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold); - if (err < 0) - ast_log(LOG_ERROR, "start threshold: %s\n", snd_strerror(err)); -#endif - -#if 1 - if (stream == SND_PCM_STREAM_PLAYBACK) - stop_threshold = buffer_size; - else - stop_threshold = buffer_size; - - err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold); - if (err < 0) - ast_log(LOG_ERROR, "stop threshold: %s\n", snd_strerror(err)); -#endif -#if 0 - err = snd_pcm_sw_params_set_xfer_align(handle, swparams, PERIOD_FRAMES); - if (err < 0) - ast_log(LOG_ERROR, "Unable to set xfer alignment: %s\n", snd_strerror(err)); -#endif - -#if 0 - err = snd_pcm_sw_params_set_silence_threshold(handle, swparams, silencethreshold); - if (err < 0) - ast_log(LOG_ERROR, "Unable to set silence threshold: %s\n", snd_strerror(err)); -#endif - err = snd_pcm_sw_params(handle, swparams); - if (err < 0) - ast_log(LOG_ERROR, "sw_params: %s\n", snd_strerror(err)); - - err = snd_pcm_poll_descriptors_count(handle); - if (err <= 0) - ast_log(LOG_ERROR, "Unable to get a poll descriptors count, error is %s\n", snd_strerror(err)); - if (err != 1) - ast_log(LOG_DEBUG, "Can't handle more than one device\n"); - - snd_pcm_poll_descriptors(handle, &pfd, err); - ast_log(LOG_DEBUG, "Acquired fd %d from the poll descriptor\n", pfd.fd); - - if (stream == SND_PCM_STREAM_CAPTURE) - readdev = pfd.fd; - else - writedev = pfd.fd; - - return handle; -} - -static int soundcard_init(void) -{ - alsa.icard = alsa_card_init(indevname, SND_PCM_STREAM_CAPTURE); - alsa.ocard = alsa_card_init(outdevname, SND_PCM_STREAM_PLAYBACK); - - if (!alsa.icard || !alsa.ocard) { - ast_log(LOG_ERROR, "Problem opening alsa I/O devices\n"); - return -1; - } - - return readdev; -} - -static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration) -{ - ast_mutex_lock(&alsalock); - ast_verbose(" << Console Received digit %c of duration %u ms >> \n", - digit, duration); - ast_mutex_unlock(&alsalock); - return 0; -} - -static int alsa_text(struct ast_channel *c, const char *text) -{ - ast_mutex_lock(&alsalock); - ast_verbose(" << Console Received text %s >> \n", text); - ast_mutex_unlock(&alsalock); - return 0; -} - -static void grab_owner(void) -{ - while (alsa.owner && ast_mutex_trylock(&alsa.owner->lock)) { - DEADLOCK_AVOIDANCE(&alsalock); - } -} - -static int alsa_call(struct ast_channel *c, char *dest, int timeout) -{ - int res = 3; - struct ast_frame f = { AST_FRAME_CONTROL }; - ast_mutex_lock(&alsalock); - ast_verbose(" << Call placed to '%s' on console >> \n", dest); - if (autoanswer) { - ast_verbose(" << Auto-answered >> \n"); - grab_owner(); - if (alsa.owner) { - f.subclass = AST_CONTROL_ANSWER; - ast_queue_frame(alsa.owner, &f); - ast_mutex_unlock(&alsa.owner->lock); - } - } else { - ast_verbose(" << Type 'answer' to answer, or use 'autoanswer' for future calls >> \n"); - grab_owner(); - if (alsa.owner) { - f.subclass = AST_CONTROL_RINGING; - ast_queue_frame(alsa.owner, &f); - ast_mutex_unlock(&alsa.owner->lock); - } - if (write(sndcmd[1], &res, sizeof(res)) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } - } - snd_pcm_prepare(alsa.icard); - snd_pcm_start(alsa.icard); - ast_mutex_unlock(&alsalock); - return 0; -} - -static void answer_sound(void) -{ - int res; - - nosound = 1; - res = 4; - if (write(sndcmd[1], &res, sizeof(res)) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } -} - -static int alsa_answer(struct ast_channel *c) -{ - ast_mutex_lock(&alsalock); - ast_verbose(" << Console call has been answered >> \n"); - answer_sound(); - ast_setstate(c, AST_STATE_UP); - cursound = -1; - snd_pcm_prepare(alsa.icard); - snd_pcm_start(alsa.icard); - ast_mutex_unlock(&alsalock); - return 0; -} - -static int alsa_hangup(struct ast_channel *c) -{ - int res; - ast_mutex_lock(&alsalock); - cursound = -1; - c->tech_pvt = NULL; - alsa.owner = NULL; - ast_verbose(" << Hangup on console >> \n"); - ast_module_unref(ast_module_info->self); - if (hookstate) { - hookstate = 0; - if (!autoanswer) { - /* Congestion noise */ - res = 2; - if (write(sndcmd[1], &res, sizeof(res)) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } - } - } - snd_pcm_drop(alsa.icard); - ast_mutex_unlock(&alsalock); - return 0; -} - -static int alsa_write(struct ast_channel *chan, struct ast_frame *f) -{ - static char sizbuf[8000]; - static int sizpos = 0; - int len = sizpos; - int pos; - int res = 0; - /* size_t frames = 0; */ - snd_pcm_state_t state; - - /* Immediately return if no sound is enabled */ - if (nosound) - return 0; - - ast_mutex_lock(&alsalock); - /* Stop any currently playing sound */ - if (cursound != -1) { - snd_pcm_drop(alsa.ocard); - snd_pcm_prepare(alsa.ocard); - cursound = -1; - } - - - /* We have to digest the frame in 160-byte portions */ - if (f->datalen > sizeof(sizbuf) - sizpos) { - ast_log(LOG_WARNING, "Frame too large\n"); - res = -1; - } else { - memcpy(sizbuf + sizpos, f->data, f->datalen); - len += f->datalen; - pos = 0; -#ifdef ALSA_MONITOR - alsa_monitor_write(sizbuf, len); -#endif - state = snd_pcm_state(alsa.ocard); - if (state == SND_PCM_STATE_XRUN) - snd_pcm_prepare(alsa.ocard); - while ((res = snd_pcm_writei(alsa.ocard, sizbuf, len / 2)) == -EAGAIN) { - usleep(1); - } - if (res == -EPIPE) { -#if DEBUG - ast_log(LOG_DEBUG, "XRUN write\n"); -#endif - snd_pcm_prepare(alsa.ocard); - while ((res = snd_pcm_writei(alsa.ocard, sizbuf, len / 2)) == -EAGAIN) { - usleep(1); - } - if (res != len / 2) { - ast_log(LOG_ERROR, "Write error: %s\n", snd_strerror(res)); - res = -1; - } else if (res < 0) { - ast_log(LOG_ERROR, "Write error %s\n", snd_strerror(res)); - res = -1; - } - } else { - if (res == -ESTRPIPE) - ast_log(LOG_ERROR, "You've got some big problems\n"); - else if (res < 0) - ast_log(LOG_NOTICE, "Error %d on write\n", res); - } - } - ast_mutex_unlock(&alsalock); - if (res > 0) - res = 0; - return res; -} - - -static struct ast_frame *alsa_read(struct ast_channel *chan) -{ - static struct ast_frame f; - static short __buf[FRAME_SIZE + AST_FRIENDLY_OFFSET / 2]; - short *buf; - static int readpos = 0; - static int left = FRAME_SIZE; - snd_pcm_state_t state; - int r = 0; - int off = 0; - - ast_mutex_lock(&alsalock); - /* Acknowledge any pending cmd */ - f.frametype = AST_FRAME_NULL; - f.subclass = 0; - f.samples = 0; - f.datalen = 0; - f.data = NULL; - f.offset = 0; - f.src = "Console"; - f.mallocd = 0; - f.delivery.tv_sec = 0; - f.delivery.tv_usec = 0; - - state = snd_pcm_state(alsa.icard); - if ((state != SND_PCM_STATE_PREPARED) && (state != SND_PCM_STATE_RUNNING)) { - snd_pcm_prepare(alsa.icard); - } - - buf = __buf + AST_FRIENDLY_OFFSET / 2; - - r = snd_pcm_readi(alsa.icard, buf + readpos, left); - if (r == -EPIPE) { -#if DEBUG - ast_log(LOG_ERROR, "XRUN read\n"); -#endif - snd_pcm_prepare(alsa.icard); - } else if (r == -ESTRPIPE) { - ast_log(LOG_ERROR, "-ESTRPIPE\n"); - snd_pcm_prepare(alsa.icard); - } else if (r < 0) { - ast_log(LOG_ERROR, "Read error: %s\n", snd_strerror(r)); - } else if (r >= 0) { - off -= r; - } - /* Update positions */ - readpos += r; - left -= r; - - if (readpos >= FRAME_SIZE) { - /* A real frame */ - readpos = 0; - left = FRAME_SIZE; - if (chan->_state != AST_STATE_UP) { - /* Don't transmit unless it's up */ - ast_mutex_unlock(&alsalock); - return &f; - } - f.frametype = AST_FRAME_VOICE; - f.subclass = AST_FORMAT_SLINEAR; - f.samples = FRAME_SIZE; - f.datalen = FRAME_SIZE * 2; - f.data = buf; - f.offset = AST_FRIENDLY_OFFSET; - f.src = "Console"; - f.mallocd = 0; -#ifdef ALSA_MONITOR - alsa_monitor_read((char *) buf, FRAME_SIZE * 2); -#endif - - } - ast_mutex_unlock(&alsalock); - return &f; -} - -static int alsa_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct chan_alsa_pvt *p = newchan->tech_pvt; - ast_mutex_lock(&alsalock); - p->owner = newchan; - ast_mutex_unlock(&alsalock); - return 0; -} - -static int alsa_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen) -{ - int res = 0; - - ast_mutex_lock(&alsalock); - - switch (cond) { - case AST_CONTROL_BUSY: - res = 1; - break; - case AST_CONTROL_CONGESTION: - res = 2; - break; - case AST_CONTROL_RINGING: - case AST_CONTROL_PROGRESS: - break; - case -1: - res = -1; - break; - case AST_CONTROL_VIDUPDATE: - res = -1; - break; - case AST_CONTROL_HOLD: - ast_verbose(" << Console Has Been Placed on Hold >> \n"); - ast_moh_start(chan, data, mohinterpret); - break; - case AST_CONTROL_UNHOLD: - ast_verbose(" << Console Has Been Retrieved from Hold >> \n"); - ast_moh_stop(chan); - break; - case AST_CONTROL_SRCUPDATE: - break; - default: - ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, chan->name); - res = -1; - } - - if (res > -1) { - if (write(sndcmd[1], &res, sizeof(res)) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } - } - - ast_mutex_unlock(&alsalock); - - return res; -} - -static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state) -{ - struct ast_channel *tmp = NULL; - - if (!(tmp = ast_channel_alloc(1, state, 0, 0, "", p->exten, p->context, 0, "ALSA/%s", indevname))) - return NULL; - - tmp->tech = &alsa_tech; - tmp->fds[0] = readdev; - tmp->nativeformats = AST_FORMAT_SLINEAR; - tmp->readformat = AST_FORMAT_SLINEAR; - tmp->writeformat = AST_FORMAT_SLINEAR; - tmp->tech_pvt = p; - if (!ast_strlen_zero(p->context)) - ast_copy_string(tmp->context, p->context, sizeof(tmp->context)); - if (!ast_strlen_zero(p->exten)) - ast_copy_string(tmp->exten, p->exten, sizeof(tmp->exten)); - if (!ast_strlen_zero(language)) - ast_string_field_set(tmp, language, language); - p->owner = tmp; - ast_module_ref(ast_module_info->self); - ast_jb_configure(tmp, &global_jbconf); - if (state != AST_STATE_DOWN) { - if (ast_pbx_start(tmp)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); - ast_hangup(tmp); - tmp = NULL; - } - } - - return tmp; -} - -static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause) -{ - int oldformat = format; - struct ast_channel *tmp = NULL; - - format &= AST_FORMAT_SLINEAR; - if (!format) { - ast_log(LOG_NOTICE, "Asked to get a channel of format '%d'\n", oldformat); - return NULL; - } - - ast_mutex_lock(&alsalock); - - if (alsa.owner) { - ast_log(LOG_NOTICE, "Already have a call on the ALSA channel\n"); - *cause = AST_CAUSE_BUSY; - } else if (!(tmp = alsa_new(&alsa, AST_STATE_DOWN))) - ast_log(LOG_WARNING, "Unable to create new ALSA channel\n"); - - ast_mutex_unlock(&alsalock); - - return tmp; -} - -static int console_autoanswer_deprecated(int fd, int argc, char *argv[]) -{ - int res = RESULT_SUCCESS; - - if ((argc != 1) && (argc != 2)) - return RESULT_SHOWUSAGE; - - ast_mutex_lock(&alsalock); - - if (argc == 1) { - ast_cli(fd, "Auto answer is %s.\n", autoanswer ? "on" : "off"); - } else { - if (!strcasecmp(argv[1], "on")) - autoanswer = -1; - else if (!strcasecmp(argv[1], "off")) - autoanswer = 0; - else - res = RESULT_SHOWUSAGE; - } - - ast_mutex_unlock(&alsalock); - - return res; -} - -static int console_autoanswer(int fd, int argc, char *argv[]) -{ - int res = RESULT_SUCCESS;; - if ((argc != 2) && (argc != 3)) - return RESULT_SHOWUSAGE; - ast_mutex_lock(&alsalock); - if (argc == 2) { - ast_cli(fd, "Auto answer is %s.\n", autoanswer ? "on" : "off"); - } else { - if (!strcasecmp(argv[2], "on")) - autoanswer = -1; - else if (!strcasecmp(argv[2], "off")) - autoanswer = 0; - else - res = RESULT_SHOWUSAGE; - } - ast_mutex_unlock(&alsalock); - return res; -} - -static char *autoanswer_complete(const char *line, const char *word, int pos, int state) -{ -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - switch (state) { - case 0: - if (!ast_strlen_zero(word) && !strncasecmp(word, "on", MIN(strlen(word), 2))) - return ast_strdup("on"); - case 1: - if (!ast_strlen_zero(word) && !strncasecmp(word, "off", MIN(strlen(word), 3))) - return ast_strdup("off"); - default: - return NULL; - } - return NULL; -} - -static const char autoanswer_usage[] = - "Usage: console autoanswer [on|off]\n" - " Enables or disables autoanswer feature. If used without\n" - " argument, displays the current on/off status of autoanswer.\n" - " The default value of autoanswer is in 'alsa.conf'.\n"; - -static int console_answer_deprecated(int fd, int argc, char *argv[]) -{ - int res = RESULT_SUCCESS; - - if (argc != 1) - return RESULT_SHOWUSAGE; - - ast_mutex_lock(&alsalock); - - if (!alsa.owner) { - ast_cli(fd, "No one is calling us\n"); - res = RESULT_FAILURE; - } else { - hookstate = 1; - cursound = -1; - grab_owner(); - if (alsa.owner) { - struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; - ast_queue_frame(alsa.owner, &f); - ast_mutex_unlock(&alsa.owner->lock); - } - answer_sound(); - } - - snd_pcm_prepare(alsa.icard); - snd_pcm_start(alsa.icard); - - ast_mutex_unlock(&alsalock); - - return RESULT_SUCCESS; -} - -static int console_answer(int fd, int argc, char *argv[]) -{ - int res = RESULT_SUCCESS; - - if (argc != 2) - return RESULT_SHOWUSAGE; - - ast_mutex_lock(&alsalock); - - if (!alsa.owner) { - ast_cli(fd, "No one is calling us\n"); - res = RESULT_FAILURE; - } else { - hookstate = 1; - cursound = -1; - grab_owner(); - if (alsa.owner) { - struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; - ast_queue_frame(alsa.owner, &f); - ast_mutex_unlock(&alsa.owner->lock); - } - answer_sound(); - } - - snd_pcm_prepare(alsa.icard); - snd_pcm_start(alsa.icard); - - ast_mutex_unlock(&alsalock); - - return RESULT_SUCCESS; -} - -static char sendtext_usage[] = - "Usage: console send text <message>\n" - " Sends a text message for display on the remote terminal.\n"; - -static int console_sendtext_deprecated(int fd, int argc, char *argv[]) -{ - int tmparg = 2; - int res = RESULT_SUCCESS; - - if (argc < 2) - return RESULT_SHOWUSAGE; - - ast_mutex_lock(&alsalock); - - if (!alsa.owner) { - ast_cli(fd, "No one is calling us\n"); - res = RESULT_FAILURE; - } else { - struct ast_frame f = { AST_FRAME_TEXT, 0 }; - char text2send[256] = ""; - text2send[0] = '\0'; - while (tmparg < argc) { - strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send) - 1); - strncat(text2send, " ", sizeof(text2send) - strlen(text2send) - 1); - } - text2send[strlen(text2send) - 1] = '\n'; - f.data = text2send; - f.datalen = strlen(text2send) + 1; - grab_owner(); - if (alsa.owner) { - ast_queue_frame(alsa.owner, &f); - f.frametype = AST_FRAME_CONTROL; - f.subclass = AST_CONTROL_ANSWER; - f.data = NULL; - f.datalen = 0; - ast_queue_frame(alsa.owner, &f); - ast_mutex_unlock(&alsa.owner->lock); - } - } - - ast_mutex_unlock(&alsalock); - - return res; -} - -static int console_sendtext(int fd, int argc, char *argv[]) -{ - int tmparg = 3; - int res = RESULT_SUCCESS; - - if (argc < 3) - return RESULT_SHOWUSAGE; - - ast_mutex_lock(&alsalock); - - if (!alsa.owner) { - ast_cli(fd, "No one is calling us\n"); - res = RESULT_FAILURE; - } else { - struct ast_frame f = { AST_FRAME_TEXT, 0 }; - char text2send[256] = ""; - text2send[0] = '\0'; - while (tmparg < argc) { - strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send) - 1); - strncat(text2send, " ", sizeof(text2send) - strlen(text2send) - 1); - } - text2send[strlen(text2send) - 1] = '\n'; - f.data = text2send; - f.datalen = strlen(text2send) + 1; - grab_owner(); - if (alsa.owner) { - ast_queue_frame(alsa.owner, &f); - f.frametype = AST_FRAME_CONTROL; - f.subclass = AST_CONTROL_ANSWER; - f.data = NULL; - f.datalen = 0; - ast_queue_frame(alsa.owner, &f); - ast_mutex_unlock(&alsa.owner->lock); - } - } - - ast_mutex_unlock(&alsalock); - - return res; -} - -static char answer_usage[] = - "Usage: console answer\n" - " Answers an incoming call on the console (ALSA) channel.\n"; - -static int console_hangup_deprecated(int fd, int argc, char *argv[]) -{ - int res = RESULT_SUCCESS; - - if (argc != 1) - return RESULT_SHOWUSAGE; - - cursound = -1; - - ast_mutex_lock(&alsalock); - - if (!alsa.owner && !hookstate) { - ast_cli(fd, "No call to hangup up\n"); - res = RESULT_FAILURE; - } else { - hookstate = 0; - grab_owner(); - if (alsa.owner) { - ast_queue_hangup(alsa.owner); - ast_mutex_unlock(&alsa.owner->lock); - } - } - - ast_mutex_unlock(&alsalock); - - return res; -} - -static int console_hangup(int fd, int argc, char *argv[]) -{ - int res = RESULT_SUCCESS; - - if (argc != 2) - return RESULT_SHOWUSAGE; - - cursound = -1; - - ast_mutex_lock(&alsalock); - - if (!alsa.owner && !hookstate) { - ast_cli(fd, "No call to hangup up\n"); - res = RESULT_FAILURE; - } else { - hookstate = 0; - grab_owner(); - if (alsa.owner) { - ast_queue_hangup(alsa.owner); - ast_mutex_unlock(&alsa.owner->lock); - } - } - - ast_mutex_unlock(&alsalock); - - return res; -} - -static char hangup_usage[] = - "Usage: console hangup\n" - " Hangs up any call currently placed on the console.\n"; - -static int console_dial_deprecated(int fd, int argc, char *argv[]) -{ - char tmp[256], *tmp2; - char *mye, *myc; - char *d; - int res = RESULT_SUCCESS; - - if ((argc != 1) && (argc != 2)) - return RESULT_SHOWUSAGE; - - ast_mutex_lock(&alsalock); - - if (alsa.owner) { - if (argc == 2) { - d = argv[1]; - grab_owner(); - if (alsa.owner) { - struct ast_frame f = { AST_FRAME_DTMF }; - while (*d) { - f.subclass = *d; - ast_queue_frame(alsa.owner, &f); - d++; - } - ast_mutex_unlock(&alsa.owner->lock); - } - } else { - ast_cli(fd, "You're already in a call. You can use this only to dial digits until you hangup\n"); - res = RESULT_FAILURE; - } - } else { - mye = exten; - myc = context; - if (argc == 2) { - char *stringp = NULL; - ast_copy_string(tmp, argv[1], sizeof(tmp)); - stringp = tmp; - strsep(&stringp, "@"); - tmp2 = strsep(&stringp, "@"); - if (!ast_strlen_zero(tmp)) - mye = tmp; - if (!ast_strlen_zero(tmp2)) - myc = tmp2; - } - if (ast_exists_extension(NULL, myc, mye, 1, NULL)) { - ast_copy_string(alsa.exten, mye, sizeof(alsa.exten)); - ast_copy_string(alsa.context, myc, sizeof(alsa.context)); - hookstate = 1; - alsa_new(&alsa, AST_STATE_RINGING); - } else - ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc); - } - - ast_mutex_unlock(&alsalock); - - return res; -} - -static int console_dial(int fd, int argc, char *argv[]) -{ - char tmp[256], *tmp2; - char *mye, *myc; - char *d; - int res = RESULT_SUCCESS; - - if ((argc != 2) && (argc != 3)) - return RESULT_SHOWUSAGE; - - ast_mutex_lock(&alsalock); - - if (alsa.owner) { - if (argc == 3) { - d = argv[2]; - grab_owner(); - if (alsa.owner) { - struct ast_frame f = { AST_FRAME_DTMF }; - while (*d) { - f.subclass = *d; - ast_queue_frame(alsa.owner, &f); - d++; - } - ast_mutex_unlock(&alsa.owner->lock); - } - } else { - ast_cli(fd, "You're already in a call. You can use this only to dial digits until you hangup\n"); - res = RESULT_FAILURE; - } - } else { - mye = exten; - myc = context; - if (argc == 3) { - char *stringp = NULL; - ast_copy_string(tmp, argv[2], sizeof(tmp)); - stringp = tmp; - strsep(&stringp, "@"); - tmp2 = strsep(&stringp, "@"); - if (!ast_strlen_zero(tmp)) - mye = tmp; - if (!ast_strlen_zero(tmp2)) - myc = tmp2; - } - if (ast_exists_extension(NULL, myc, mye, 1, NULL)) { - ast_copy_string(alsa.exten, mye, sizeof(alsa.exten)); - ast_copy_string(alsa.context, myc, sizeof(alsa.context)); - hookstate = 1; - alsa_new(&alsa, AST_STATE_RINGING); - } else - ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc); - } - - ast_mutex_unlock(&alsalock); - - return res; -} - -static char dial_usage[] = - "Usage: console dial [extension[@context]]\n" - " Dials a given extension (and context if specified)\n"; - -static struct ast_cli_entry cli_alsa_answer_deprecated = { - { "answer", NULL }, - console_answer_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_alsa_hangup_deprecated = { - { "hangup", NULL }, - console_hangup_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_alsa_dial_deprecated = { - { "dial", NULL }, - console_dial_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_alsa_send_text_deprecated = { - { "send", "text", NULL }, - console_sendtext_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_alsa_autoanswer_deprecated = { - { "autoanswer", NULL }, - console_autoanswer_deprecated, NULL, - NULL, autoanswer_complete }; - -static struct ast_cli_entry cli_alsa[] = { - { { "console", "answer", NULL }, - console_answer, "Answer an incoming console call", - answer_usage, NULL, &cli_alsa_answer_deprecated }, - - { { "console", "hangup", NULL }, - console_hangup, "Hangup a call on the console", - hangup_usage, NULL, &cli_alsa_hangup_deprecated }, - - { { "console", "dial", NULL }, - console_dial, "Dial an extension on the console", - dial_usage, NULL, &cli_alsa_dial_deprecated }, - - { { "console", "send", "text", NULL }, - console_sendtext, "Send text to the remote device", - sendtext_usage, NULL, &cli_alsa_send_text_deprecated }, - - { { "console", "autoanswer", NULL }, - console_autoanswer, "Sets/displays autoanswer", - autoanswer_usage, autoanswer_complete, &cli_alsa_autoanswer_deprecated }, -}; - -static int load_module(void) -{ - int res; - struct ast_config *cfg; - struct ast_variable *v; - - /* Copy the default jb config over global_jbconf */ - memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); - - strcpy(mohinterpret, "default"); - - if ((cfg = ast_config_load(config))) { - v = ast_variable_browse(cfg, "general"); - for (; v; v = v->next) { - /* handle jb conf */ - if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) - continue; - - if (!strcasecmp(v->name, "autoanswer")) - autoanswer = ast_true(v->value); - else if (!strcasecmp(v->name, "silencesuppression")) - silencesuppression = ast_true(v->value); - else if (!strcasecmp(v->name, "silencethreshold")) - silencethreshold = atoi(v->value); - else if (!strcasecmp(v->name, "context")) - ast_copy_string(context, v->value, sizeof(context)); - else if (!strcasecmp(v->name, "language")) - ast_copy_string(language, v->value, sizeof(language)); - else if (!strcasecmp(v->name, "extension")) - ast_copy_string(exten, v->value, sizeof(exten)); - else if (!strcasecmp(v->name, "input_device")) - ast_copy_string(indevname, v->value, sizeof(indevname)); - else if (!strcasecmp(v->name, "output_device")) - ast_copy_string(outdevname, v->value, sizeof(outdevname)); - else if (!strcasecmp(v->name, "mohinterpret")) - ast_copy_string(mohinterpret, v->value, sizeof(mohinterpret)); - } - ast_config_destroy(cfg); - } - res = pipe(sndcmd); - if (res) { - ast_log(LOG_ERROR, "Unable to create pipe\n"); - return -1; - } - res = soundcard_init(); - if (res < 0) { - if (option_verbose > 1) { - ast_verbose(VERBOSE_PREFIX_2 "No sound card detected -- console channel will be unavailable\n"); - ast_verbose(VERBOSE_PREFIX_2 "Turn off ALSA support by adding 'noload=chan_alsa.so' in /etc/asterisk/modules.conf\n"); - } - return 0; - } - - res = ast_channel_register(&alsa_tech); - if (res < 0) { - ast_log(LOG_ERROR, "Unable to register channel class 'Console'\n"); - return -1; - } - ast_cli_register_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry)); - - ast_pthread_create_background(&sthread, NULL, sound_thread, NULL); -#ifdef ALSA_MONITOR - if (alsa_monitor_start()) - ast_log(LOG_ERROR, "Problem starting Monitoring\n"); -#endif - return 0; -} - -static int unload_module(void) -{ - ast_channel_unregister(&alsa_tech); - ast_cli_unregister_multiple(cli_alsa, sizeof(cli_alsa) / sizeof(struct ast_cli_entry)); - - if (alsa.icard) - snd_pcm_close(alsa.icard); - if (alsa.ocard) - snd_pcm_close(alsa.ocard); - if (sndcmd[0] > 0) { - close(sndcmd[0]); - close(sndcmd[1]); - } - if (alsa.owner) - ast_softhangup(alsa.owner, AST_SOFTHANGUP_APPUNLOAD); - if (alsa.owner) - return -1; - return 0; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ALSA Console Channel Driver"); diff --git a/1.4.23-rc4/channels/chan_dahdi.c b/1.4.23-rc4/channels/chan_dahdi.c deleted file mode 100644 index 81498b372..000000000 --- a/1.4.23-rc4/channels/chan_dahdi.c +++ /dev/null @@ -1,11992 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief DAHDI Pseudo TDM interface - * - * \author Mark Spencer <markster@digium.com> - * - * Connects to the DAHDI telephony library as well as - * libpri. Libpri is optional and needed only if you are - * going to use ISDN connections. - * - * You need to install libraries before you attempt to compile - * and install the DAHDI channel. - * - * \par See also - * \arg \ref Config_dahdi - * - * \ingroup channel_drivers - * - * \todo Deprecate the "musiconhold" configuration option post 1.4 - */ - -/*** MODULEINFO - <depend>res_smdi</depend> - <depend>dahdi</depend> - <depend>tonezone</depend> - <depend>res_features</depend> - <use>pri</use> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <string.h> -#ifdef __NetBSD__ -#include <pthread.h> -#include <signal.h> -#else -#include <sys/signal.h> -#endif -#include <errno.h> -#include <stdlib.h> -#if !defined(SOLARIS) && !defined(__FreeBSD__) -#include <stdint.h> -#endif -#include <unistd.h> -#include <sys/ioctl.h> -#include <math.h> -#include <ctype.h> - -#ifdef HAVE_PRI -#include <libpri.h> -#endif - -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/file.h" -#include "asterisk/ulaw.h" -#include "asterisk/alaw.h" -#include "asterisk/callerid.h" -#include "asterisk/adsi.h" -#include "asterisk/cli.h" -#include "asterisk/cdr.h" -#include "asterisk/features.h" -#include "asterisk/musiconhold.h" -#include "asterisk/say.h" -#include "asterisk/tdd.h" -#include "asterisk/app.h" -#include "asterisk/dsp.h" -#include "asterisk/astdb.h" -#include "asterisk/manager.h" -#include "asterisk/causes.h" -#include "asterisk/term.h" -#include "asterisk/utils.h" -#include "asterisk/transcap.h" -#include "asterisk/stringfields.h" -#include "asterisk/abstract_jb.h" -#include "asterisk/smdi.h" -#include "asterisk/astobj.h" -#define SMDI_MD_WAIT_TIMEOUT 1500 /* 1.5 seconds */ - -#include "asterisk/dahdi_compat.h" -#include "asterisk/tonezone_compat.h" - -/*! Global jitterbuffer configuration - by default, jb is disabled */ -static struct ast_jb_conf default_jbconf = -{ - .flags = 0, - .max_size = -1, - .resync_threshold = -1, - .impl = "" -}; -static struct ast_jb_conf global_jbconf; - -#ifndef DAHDI_TONEDETECT -/* Work around older code with no tone detect */ -#define DAHDI_EVENT_DTMFDOWN 0 -#define DAHDI_EVENT_DTMFUP 0 -#endif - -/* define this to send PRI user-user information elements */ -#undef SUPPORT_USERUSER - -/*! - * \note Define ZHONE_HACK to cause us to go off hook and then back on hook when - * the user hangs up to reset the state machine so ring works properly. - * This is used to be able to support kewlstart by putting the zhone in - * groundstart mode since their forward disconnect supervision is entirely - * broken even though their documentation says it isn't and their support - * is entirely unwilling to provide any assistance with their channel banks - * even though their web site says they support their products for life. - */ -/* #define ZHONE_HACK */ - -/*! \note - * Define if you want to check the hook state for an FXO (FXS signalled) interface - * before dialing on it. Certain FXO interfaces always think they're out of - * service with this method however. - */ -/* #define DAHDI_CHECK_HOOKSTATE */ - -/*! \brief Typically, how many rings before we should send Caller*ID */ -#define DEFAULT_CIDRINGS 1 - -#define CHANNEL_PSEUDO -12 - -#define AST_LAW(p) (((p)->law == DAHDI_LAW_ALAW) ? AST_FORMAT_ALAW : AST_FORMAT_ULAW) - -/*! \brief Signaling types that need to use MF detection should be placed in this macro */ -#define NEED_MFDETECT(p) (((p)->sig == SIG_FEATDMF) || ((p)->sig == SIG_FEATDMF_TA) || ((p)->sig == SIG_E911) || ((p)->sig == SIG_FGC_CAMA) || ((p)->sig == SIG_FGC_CAMAMF) || ((p)->sig == SIG_FEATB)) - -static const char tdesc[] = "DAHDI Telephony Driver" -#ifdef HAVE_PRI - " w/PRI" -#endif -; - -#define SIG_EM DAHDI_SIG_EM -#define SIG_EMWINK (0x0100000 | DAHDI_SIG_EM) -#define SIG_FEATD (0x0200000 | DAHDI_SIG_EM) -#define SIG_FEATDMF (0x0400000 | DAHDI_SIG_EM) -#define SIG_FEATB (0x0800000 | DAHDI_SIG_EM) -#define SIG_E911 (0x1000000 | DAHDI_SIG_EM) -#define SIG_FEATDMF_TA (0x2000000 | DAHDI_SIG_EM) -#define SIG_FGC_CAMA (0x4000000 | DAHDI_SIG_EM) -#define SIG_FGC_CAMAMF (0x8000000 | DAHDI_SIG_EM) -#define SIG_FXSLS DAHDI_SIG_FXSLS -#define SIG_FXSGS DAHDI_SIG_FXSGS -#define SIG_FXSKS DAHDI_SIG_FXSKS -#define SIG_FXOLS DAHDI_SIG_FXOLS -#define SIG_FXOGS DAHDI_SIG_FXOGS -#define SIG_FXOKS DAHDI_SIG_FXOKS -#define SIG_PRI DAHDI_SIG_CLEAR -#define SIG_SF DAHDI_SIG_SF -#define SIG_SFWINK (0x0100000 | DAHDI_SIG_SF) -#define SIG_SF_FEATD (0x0200000 | DAHDI_SIG_SF) -#define SIG_SF_FEATDMF (0x0400000 | DAHDI_SIG_SF) -#define SIG_SF_FEATB (0x0800000 | DAHDI_SIG_SF) -#define SIG_EM_E1 DAHDI_SIG_EM_E1 -#define SIG_GR303FXOKS (0x0100000 | DAHDI_SIG_FXOKS) -#define SIG_GR303FXSKS (0x0100000 | DAHDI_SIG_FXSKS) - -#define NUM_SPANS 32 -#define NUM_DCHANS 4 /*!< No more than 4 d-channels */ -#define MAX_CHANNELS 672 /*!< No more than a DS3 per trunk group */ - -#define CHAN_PSEUDO -2 - -#define DCHAN_PROVISIONED (1 << 0) -#define DCHAN_NOTINALARM (1 << 1) -#define DCHAN_UP (1 << 2) - -#define DCHAN_AVAILABLE (DCHAN_PROVISIONED | DCHAN_NOTINALARM | DCHAN_UP) - -static char defaultcic[64] = ""; -static char defaultozz[64] = ""; - -static char progzone[10] = ""; - -static int distinctiveringaftercid = 0; - -static int numbufs = 4; - -#ifdef HAVE_PRI -static struct ast_channel inuse; -#ifdef PRI_GETSET_TIMERS -static int pritimers[PRI_MAX_TIMERS]; -#endif -static int pridebugfd = -1; -static char pridebugfilename[1024] = ""; -#endif - -/*! \brief Wait up to 16 seconds for first digit (FXO logic) */ -static int firstdigittimeout = 16000; - -/*! \brief How long to wait for following digits (FXO logic) */ -static int gendigittimeout = 8000; - -/*! \brief How long to wait for an extra digit, if there is an ambiguous match */ -static int matchdigittimeout = 3000; - -/*! \brief Protect the interface list (of dahdi_pvt's) */ -AST_MUTEX_DEFINE_STATIC(iflock); - - -static int ifcount = 0; - -#ifdef HAVE_PRI -AST_MUTEX_DEFINE_STATIC(pridebugfdlock); -#endif - -/*! \brief Protect the monitoring thread, so only one process can kill or start it, and not - when it's doing something critical. */ -AST_MUTEX_DEFINE_STATIC(monlock); - -/*! \brief This is the thread for the monitor which checks for input on the channels - which are not currently in use. */ -static pthread_t monitor_thread = AST_PTHREADT_NULL; -static ast_cond_t ss_thread_complete; -AST_MUTEX_DEFINE_STATIC(ss_thread_lock); -AST_MUTEX_DEFINE_STATIC(restart_lock); -static int ss_thread_count = 0; -static int num_restart_pending = 0; - -static int restart_monitor(void); - -static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); - -static int dahdi_sendtext(struct ast_channel *c, const char *text); - -/*! \brief Avoid the silly dahdi_getevent which ignores a bunch of events */ -static inline int dahdi_get_event(int fd) -{ - int j; - if (ioctl(fd, DAHDI_GETEVENT, &j) == -1) - return -1; - return j; -} - -/*! \brief Avoid the silly dahdi_waitevent which ignores a bunch of events */ -static inline int dahdi_wait_event(int fd) -{ - int i, j = 0; - i = DAHDI_IOMUX_SIGEVENT; - if (ioctl(fd, DAHDI_IOMUX, &i) == -1) - return -1; - if (ioctl(fd, DAHDI_GETEVENT, &j) == -1) - return -1; - return j; -} - -/*! Chunk size to read -- we use 20ms chunks to make things happy. */ -#define READ_SIZE 160 - -#define MASK_AVAIL (1 << 0) /*!< Channel available for PRI use */ -#define MASK_INUSE (1 << 1) /*!< Channel currently in use */ - -#define CALLWAITING_SILENT_SAMPLES ( (300 * 8) / READ_SIZE) /*!< 300 ms */ -#define CALLWAITING_REPEAT_SAMPLES ( (10000 * 8) / READ_SIZE) /*!< 10,000 ms */ -#define CIDCW_EXPIRE_SAMPLES ( (500 * 8) / READ_SIZE) /*!< 500 ms */ -#define MIN_MS_SINCE_FLASH ( (2000) ) /*!< 2000 ms */ -#define DEFAULT_RINGT ( (8000 * 8) / READ_SIZE) /*!< 8,000 ms */ - -struct dahdi_pvt; - -static int ringt_base = DEFAULT_RINGT; - -#ifdef HAVE_PRI - -#define PVT_TO_CHANNEL(p) (((p)->prioffset) | ((p)->logicalspan << 8) | (p->pri->mastertrunkgroup ? 0x10000 : 0)) -#define PRI_CHANNEL(p) ((p) & 0xff) -#define PRI_SPAN(p) (((p) >> 8) & 0xff) -#define PRI_EXPLICIT(p) (((p) >> 16) & 0x01) - -struct dahdi_pri { - pthread_t master; /*!< Thread of master */ - ast_mutex_t lock; /*!< Mutex */ - char idleext[AST_MAX_EXTENSION]; /*!< Where to idle extra calls */ - char idlecontext[AST_MAX_CONTEXT]; /*!< What context to use for idle */ - char idledial[AST_MAX_EXTENSION]; /*!< What to dial before dumping */ - int minunused; /*!< Min # of channels to keep empty */ - int minidle; /*!< Min # of "idling" calls to keep active */ - int nodetype; /*!< Node type */ - int switchtype; /*!< Type of switch to emulate */ - int nsf; /*!< Network-Specific Facilities */ - int dialplan; /*!< Dialing plan */ - int localdialplan; /*!< Local dialing plan */ - char internationalprefix[10]; /*!< country access code ('00' for european dialplans) */ - char nationalprefix[10]; /*!< area access code ('0' for european dialplans) */ - char localprefix[20]; /*!< area access code + area code ('0'+area code for european dialplans) */ - char privateprefix[20]; /*!< for private dialplans */ - char unknownprefix[20]; /*!< for unknown dialplans */ - int dchannels[NUM_DCHANS]; /*!< What channel are the dchannels on */ - int trunkgroup; /*!< What our trunkgroup is */ - int mastertrunkgroup; /*!< What trunk group is our master */ - int prilogicalspan; /*!< Logical span number within trunk group */ - int numchans; /*!< Num of channels we represent */ - int overlapdial; /*!< In overlap dialing mode */ - int facilityenable; /*!< Enable facility IEs */ - struct pri *dchans[NUM_DCHANS]; /*!< Actual d-channels */ - int dchanavail[NUM_DCHANS]; /*!< Whether each channel is available */ - struct pri *pri; /*!< Currently active D-channel */ - int debug; - int fds[NUM_DCHANS]; /*!< FD's for d-channels */ - int offset; - int span; - int resetting; - int resetpos; -#ifdef HAVE_PRI_INBANDDISCONNECT - unsigned int inbanddisconnect:1; /*!< Should we support inband audio after receiving DISCONNECT? */ -#endif - time_t lastreset; /*!< time when unused channels were last reset */ - long resetinterval; /*!< Interval (in seconds) for resetting unused channels */ - struct dahdi_pvt *pvts[MAX_CHANNELS]; /*!< Member channel pvt structs */ - struct dahdi_pvt *crvs; /*!< Member CRV structs */ - struct dahdi_pvt *crvend; /*!< Pointer to end of CRV structs */ -}; - - -static struct dahdi_pri pris[NUM_SPANS]; - -#if 0 -#define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE) -#else -#define DEFAULT_PRI_DEBUG 0 -#endif - -static inline void pri_rel(struct dahdi_pri *pri) -{ - ast_mutex_unlock(&pri->lock); -} - -#else -/*! Shut up the compiler */ -struct dahdi_pri; -#endif - -#define SUB_REAL 0 /*!< Active call */ -#define SUB_CALLWAIT 1 /*!< Call-Waiting call on hold */ -#define SUB_THREEWAY 2 /*!< Three-way call */ - -/* Polarity states */ -#define POLARITY_IDLE 0 -#define POLARITY_REV 1 - - -static struct dahdi_distRings drings; - -struct distRingData { - int ring[3]; -}; -struct ringContextData { - char contextData[AST_MAX_CONTEXT]; -}; -struct dahdi_distRings { - struct distRingData ringnum[3]; - struct ringContextData ringContext[3]; -}; - -static char *subnames[] = { - "Real", - "Callwait", - "Threeway" -}; - -struct dahdi_subchannel { - int dfd; - struct ast_channel *owner; - int chan; - short buffer[AST_FRIENDLY_OFFSET/2 + READ_SIZE]; - struct ast_frame f; /*!< One frame for each channel. How did this ever work before? */ - unsigned int needringing:1; - unsigned int needbusy:1; - unsigned int needcongestion:1; - unsigned int needcallerid:1; - unsigned int needanswer:1; - unsigned int needflash:1; - unsigned int needhold:1; - unsigned int needunhold:1; - unsigned int linear:1; - unsigned int inthreeway:1; - struct dahdi_confinfo curconf; -}; - -#define CONF_USER_REAL (1 << 0) -#define CONF_USER_THIRDCALL (1 << 1) - -#define MAX_SLAVES 4 - -static struct dahdi_pvt { - ast_mutex_t lock; - struct ast_channel *owner; /*!< Our current active owner (if applicable) */ - /*!< Up to three channels can be associated with this call */ - - struct dahdi_subchannel sub_unused; /*!< Just a safety precaution */ - struct dahdi_subchannel subs[3]; /*!< Sub-channels */ - struct dahdi_confinfo saveconf; /*!< Saved conference info */ - - struct dahdi_pvt *slaves[MAX_SLAVES]; /*!< Slave to us (follows our conferencing) */ - struct dahdi_pvt *master; /*!< Master to us (we follow their conferencing) */ - int inconference; /*!< If our real should be in the conference */ - - int buf_no; /*!< Number of buffers */ - int buf_policy; /*!< Buffer policy */ - int sig; /*!< Signalling style */ - int radio; /*!< radio type */ - int outsigmod; /*!< Outbound Signalling style (modifier) */ - int oprmode; /*!< "Operator Services" mode */ - struct dahdi_pvt *oprpeer; /*!< "Operator Services" peer tech_pvt ptr */ - float rxgain; - float txgain; - int tonezone; /*!< tone zone for this chan, or -1 for default */ - struct dahdi_pvt *next; /*!< Next channel in list */ - struct dahdi_pvt *prev; /*!< Prev channel in list */ - - /* flags */ - unsigned int adsi:1; - unsigned int answeronpolarityswitch:1; - unsigned int busydetect:1; - unsigned int callreturn:1; - unsigned int callwaiting:1; - unsigned int callwaitingcallerid:1; - unsigned int cancallforward:1; - unsigned int canpark:1; - unsigned int confirmanswer:1; /*!< Wait for '#' to confirm answer */ - unsigned int destroy:1; - unsigned int didtdd:1; /*!< flag to say its done it once */ - unsigned int dialednone:1; - unsigned int dialing:1; - unsigned int digital:1; - unsigned int dnd:1; - unsigned int echobreak:1; - unsigned int echocanbridged:1; - unsigned int echocanon:1; - unsigned int faxhandled:1; /*!< Has a fax tone already been handled? */ - unsigned int firstradio:1; - unsigned int hanguponpolarityswitch:1; - unsigned int hardwaredtmf:1; - unsigned int hidecallerid:1; - unsigned int hidecalleridname:1; /*!< Hide just the name not the number for legacy PBX use */ - unsigned int ignoredtmf:1; - unsigned int immediate:1; /*!< Answer before getting digits? */ - unsigned int inalarm:1; - unsigned int unknown_alarm:1; - unsigned int mate:1; /*!< flag to say its in MATE mode */ - unsigned int outgoing:1; - unsigned int overlapdial:1; - unsigned int permcallwaiting:1; - unsigned int permhidecallerid:1; /*!< Whether to hide our outgoing caller ID or not */ - unsigned int priindication_oob:1; - unsigned int priexclusive:1; - unsigned int pulse:1; - unsigned int pulsedial:1; /*!< whether a pulse dial phone is detected */ - unsigned int restartpending:1; /*!< flag to ensure counted only once for restart */ - unsigned int restrictcid:1; /*!< Whether restrict the callerid -> only send ANI */ - unsigned int threewaycalling:1; - unsigned int transfer:1; - unsigned int use_callerid:1; /*!< Whether or not to use caller id on this channel */ - unsigned int use_callingpres:1; /*!< Whether to use the callingpres the calling switch sends */ - unsigned int usedistinctiveringdetection:1; - unsigned int dahditrcallerid:1; /*!< should we use the callerid from incoming call on dahdi transfer or not */ - unsigned int transfertobusy:1; /*!< allow flash-transfers to busy channels */ -#if defined(HAVE_PRI) - unsigned int alerting:1; - unsigned int alreadyhungup:1; - unsigned int isidlecall:1; - unsigned int proceeding:1; - unsigned int progress:1; - unsigned int resetting:1; - unsigned int setup_ack:1; -#endif - unsigned int use_smdi:1; /* Whether to use SMDI on this channel */ - struct ast_smdi_interface *smdi_iface; /* The serial port to listen for SMDI data on */ - - struct dahdi_distRings drings; - - char context[AST_MAX_CONTEXT]; - char defcontext[AST_MAX_CONTEXT]; - char exten[AST_MAX_EXTENSION]; - char language[MAX_LANGUAGE]; - char mohinterpret[MAX_MUSICCLASS]; - char mohsuggest[MAX_MUSICCLASS]; -#ifdef PRI_ANI - char cid_ani[AST_MAX_EXTENSION]; -#endif - char cid_num[AST_MAX_EXTENSION]; - int cid_ton; /*!< Type Of Number (TON) */ - char cid_name[AST_MAX_EXTENSION]; - char lastcid_num[AST_MAX_EXTENSION]; - char lastcid_name[AST_MAX_EXTENSION]; - char *origcid_num; /*!< malloced original callerid */ - char *origcid_name; /*!< malloced original callerid */ - char callwait_num[AST_MAX_EXTENSION]; - char callwait_name[AST_MAX_EXTENSION]; - char rdnis[AST_MAX_EXTENSION]; - char dnid[AST_MAX_EXTENSION]; - ast_group_t group; - int law; - int confno; /*!< Our conference */ - int confusers; /*!< Who is using our conference */ - int propconfno; /*!< Propagated conference number */ - ast_group_t callgroup; - ast_group_t pickupgroup; - int channel; /*!< Channel Number or CRV */ - int span; /*!< Span number */ - time_t guardtime; /*!< Must wait this much time before using for new call */ - int cid_signalling; /*!< CID signalling type bell202 or v23 */ - int cid_start; /*!< CID start indicator, polarity or ring */ - int callingpres; /*!< The value of callling presentation that we're going to use when placing a PRI call */ - int callwaitingrepeat; /*!< How many samples to wait before repeating call waiting */ - int cidcwexpire; /*!< When to expire our muting for CID/CW */ - unsigned char *cidspill; - int cidpos; - int cidlen; - int ringt; - int ringt_base; - int stripmsd; - int callwaitcas; - int callwaitrings; - int echocancel; - int echotraining; - char echorest[20]; - int busycount; - int busy_tonelength; - int busy_quietlength; - int callprogress; - struct timeval flashtime; /*!< Last flash-hook time */ - struct ast_dsp *dsp; - int cref; /*!< Call reference number */ - struct dahdi_dialoperation dop; - int whichwink; /*!< SIG_FEATDMF_TA Which wink are we on? */ - char finaldial[64]; - char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */ - int amaflags; /*!< AMA Flags */ - struct tdd_state *tdd; /*!< TDD flag */ - char call_forward[AST_MAX_EXTENSION]; - char mailbox[AST_MAX_EXTENSION]; - char dialdest[256]; - int onhooktime; - int msgstate; - int distinctivering; /*!< Which distinctivering to use */ - int cidrings; /*!< Which ring to deliver CID on */ - int dtmfrelax; /*!< whether to run in relaxed DTMF mode */ - int fake_event; - int polarityonanswerdelay; - struct timeval polaritydelaytv; - int sendcalleridafter; -#ifdef HAVE_PRI - struct dahdi_pri *pri; - struct dahdi_pvt *bearer; - struct dahdi_pvt *realcall; - q931_call *call; - int prioffset; - int logicalspan; -#endif - int polarity; - int dsp_features; - char begindigit; -} *iflist = NULL, *ifend = NULL; - -/*! \brief Channel configuration from chan_dahdi.conf . - * This struct is used for parsing the [channels] section of chan_dahdi.conf. - * Generally there is a field here for every possible configuration item. - * - * The state of fields is saved along the parsing and whenever a 'channel' - * statement is reached, the current dahdi_chan_conf is used to configure the - * channel (struct dahdi_pvt) - * - * @seealso dahdi_chan_init for the default values. - */ -struct dahdi_chan_conf { - struct dahdi_pvt chan; -#ifdef HAVE_PRI - struct dahdi_pri pri; -#endif - struct dahdi_params timing; - - char smdi_port[SMDI_MAX_FILENAME_LEN]; -}; - -/** returns a new dahdi_chan_conf with default values (by-value) */ -static struct dahdi_chan_conf dahdi_chan_conf_default(void) { - /* recall that if a field is not included here it is initialized - * to 0 or equivalent - */ - struct dahdi_chan_conf conf = { -#ifdef HAVE_PRI - .pri = { - .nsf = PRI_NSF_NONE, - .switchtype = PRI_SWITCH_NI2, - .dialplan = PRI_NATIONAL_ISDN + 1, - .localdialplan = PRI_NATIONAL_ISDN + 1, - .nodetype = PRI_CPE, - - .minunused = 2, - .idleext = "", - .idledial = "", - .internationalprefix = "", - .nationalprefix = "", - .localprefix = "", - .privateprefix = "", - .unknownprefix = "", - - .resetinterval = 3600 - }, -#endif - .chan = { - .context = "default", - .cid_num = "", - .cid_name = "", - .mohinterpret = "default", - .mohsuggest = "", - .transfertobusy = 1, - - .cid_signalling = CID_SIG_BELL, - .cid_start = CID_START_RING, - .dahditrcallerid = 0, - .use_callerid = 1, - .sig = -1, - .outsigmod = -1, - - .tonezone = -1, - - .echocancel = 1, - - .busycount = 3, - - .accountcode = "", - - .mailbox = "", - - - .polarityonanswerdelay = 600, - - .sendcalleridafter = DEFAULT_CIDRINGS, - - .buf_policy = DAHDI_POLICY_IMMEDIATE, - .buf_no = numbufs - }, - .timing = { - .prewinktime = -1, - .preflashtime = -1, - .winktime = -1, - .flashtime = -1, - .starttime = -1, - .rxwinktime = -1, - .rxflashtime = -1, - .debouncetime = -1 - }, - .smdi_port = "/dev/ttyS0", - }; - - return conf; -} - - -static struct ast_channel *dahdi_request(const char *type, int format, void *data, int *cause); -static int dahdi_digit_begin(struct ast_channel *ast, char digit); -static int dahdi_digit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int dahdi_sendtext(struct ast_channel *c, const char *text); -static int dahdi_call(struct ast_channel *ast, char *rdest, int timeout); -static int dahdi_hangup(struct ast_channel *ast); -static int dahdi_answer(struct ast_channel *ast); -static struct ast_frame *dahdi_read(struct ast_channel *ast); -static int dahdi_write(struct ast_channel *ast, struct ast_frame *frame); -static struct ast_frame *dahdi_exception(struct ast_channel *ast); -static int dahdi_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen); -static int dahdi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int datalen); -static int dahdi_func_read(struct ast_channel *chan, char *function, char *data, char *buf, size_t len); - -static const struct ast_channel_tech dahdi_tech = { - .type = "DAHDI", - .description = tdesc, - .capabilities = AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_ALAW, - .requester = dahdi_request, - .send_digit_begin = dahdi_digit_begin, - .send_digit_end = dahdi_digit_end, - .send_text = dahdi_sendtext, - .call = dahdi_call, - .hangup = dahdi_hangup, - .answer = dahdi_answer, - .read = dahdi_read, - .write = dahdi_write, - .bridge = dahdi_bridge, - .exception = dahdi_exception, - .indicate = dahdi_indicate, - .fixup = dahdi_fixup, - .setoption = dahdi_setoption, - .func_channel_read = dahdi_func_read, -}; - -static const struct ast_channel_tech zap_tech = { - .type = "Zap", - .description = tdesc, - .capabilities = AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_ALAW, - .requester = dahdi_request, - .send_digit_begin = dahdi_digit_begin, - .send_digit_end = dahdi_digit_end, - .send_text = dahdi_sendtext, - .call = dahdi_call, - .hangup = dahdi_hangup, - .answer = dahdi_answer, - .read = dahdi_read, - .write = dahdi_write, - .bridge = dahdi_bridge, - .exception = dahdi_exception, - .indicate = dahdi_indicate, - .fixup = dahdi_fixup, - .setoption = dahdi_setoption, - .func_channel_read = dahdi_func_read, -}; - -static const struct ast_channel_tech *chan_tech; - -#ifdef HAVE_PRI -#define GET_CHANNEL(p) ((p)->bearer ? (p)->bearer->channel : p->channel) -#else -#define GET_CHANNEL(p) ((p)->channel) -#endif - -struct dahdi_pvt *round_robin[32]; - -#ifdef HAVE_PRI -static inline int pri_grab(struct dahdi_pvt *pvt, struct dahdi_pri *pri) -{ - int res; - /* Grab the lock first */ - do { - res = ast_mutex_trylock(&pri->lock); - if (res) { - DEADLOCK_AVOIDANCE(&pvt->lock); - } - } while (res); - /* Then break the poll */ - if (pri->master != AST_PTHREADT_NULL) - pthread_kill(pri->master, SIGURG); - return 0; -} -#endif - -#define NUM_CADENCE_MAX 25 -static int num_cadence = 4; -static int user_has_defined_cadences = 0; - -static struct dahdi_ring_cadence cadences[NUM_CADENCE_MAX] = { - { { 125, 125, 2000, 4000 } }, /*!< Quick chirp followed by normal ring */ - { { 250, 250, 500, 1000, 250, 250, 500, 4000 } }, /*!< British style ring */ - { { 125, 125, 125, 125, 125, 4000 } }, /*!< Three short bursts */ - { { 1000, 500, 2500, 5000 } }, /*!< Long ring */ -}; - -/*! \brief cidrings says in which pause to transmit the cid information, where the first pause - * is 1, the second pause is 2 and so on. - */ - -static int cidrings[NUM_CADENCE_MAX] = { - 2, /*!< Right after first long ring */ - 4, /*!< Right after long part */ - 3, /*!< After third chirp */ - 2, /*!< Second spell */ -}; - -#define ISTRUNK(p) ((p->sig == SIG_FXSLS) || (p->sig == SIG_FXSKS) || \ - (p->sig == SIG_FXSGS) || (p->sig == SIG_PRI)) - -#define CANBUSYDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __DAHDI_SIG_FXO) */) -#define CANPROGRESSDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __DAHDI_SIG_FXO) */) - -static int dahdi_get_index(struct ast_channel *ast, struct dahdi_pvt *p, int nullok) -{ - int res; - if (p->subs[SUB_REAL].owner == ast) - res = 0; - else if (p->subs[SUB_CALLWAIT].owner == ast) - res = 1; - else if (p->subs[SUB_THREEWAY].owner == ast) - res = 2; - else { - res = -1; - if (!nullok) - ast_log(LOG_WARNING, "Unable to get index, and nullok is not asserted\n"); - } - return res; -} - -#ifdef HAVE_PRI -static void wakeup_sub(struct dahdi_pvt *p, int a, struct dahdi_pri *pri) -#else -static void wakeup_sub(struct dahdi_pvt *p, int a, void *pri) -#endif -{ -#ifdef HAVE_PRI - if (pri) - ast_mutex_unlock(&pri->lock); -#endif - for (;;) { - if (p->subs[a].owner) { - if (ast_mutex_trylock(&p->subs[a].owner->lock)) { - DEADLOCK_AVOIDANCE(&p->lock); - } else { - ast_queue_frame(p->subs[a].owner, &ast_null_frame); - ast_mutex_unlock(&p->subs[a].owner->lock); - break; - } - } else - break; - } -#ifdef HAVE_PRI - if (pri) - ast_mutex_lock(&pri->lock); -#endif -} - -#ifdef HAVE_PRI -static void dahdi_queue_frame(struct dahdi_pvt *p, struct ast_frame *f, struct dahdi_pri *pri) -#else -static void dahdi_queue_frame(struct dahdi_pvt *p, struct ast_frame *f, void *pri) -#endif -{ - /* We must unlock the PRI to avoid the possibility of a deadlock */ -#ifdef HAVE_PRI - if (pri) - ast_mutex_unlock(&pri->lock); -#endif - for (;;) { - if (p->owner) { - if (ast_mutex_trylock(&p->owner->lock)) { - DEADLOCK_AVOIDANCE(&p->lock); - } else { - ast_queue_frame(p->owner, f); - ast_mutex_unlock(&p->owner->lock); - break; - } - } else - break; - } -#ifdef HAVE_PRI - if (pri) - ast_mutex_lock(&pri->lock); -#endif -} - -static int restore_gains(struct dahdi_pvt *p); - -static void swap_subs(struct dahdi_pvt *p, int a, int b) -{ - int tchan; - int tinthreeway; - struct ast_channel *towner; - - ast_log(LOG_DEBUG, "Swapping %d and %d\n", a, b); - - tchan = p->subs[a].chan; - towner = p->subs[a].owner; - tinthreeway = p->subs[a].inthreeway; - - p->subs[a].chan = p->subs[b].chan; - p->subs[a].owner = p->subs[b].owner; - p->subs[a].inthreeway = p->subs[b].inthreeway; - - p->subs[b].chan = tchan; - p->subs[b].owner = towner; - p->subs[b].inthreeway = tinthreeway; - - if (p->subs[a].owner) - p->subs[a].owner->fds[0] = p->subs[a].dfd; - if (p->subs[b].owner) - p->subs[b].owner->fds[0] = p->subs[b].dfd; - wakeup_sub(p, a, NULL); - wakeup_sub(p, b, NULL); -} - -static int dahdi_open(char *fn) -{ - int fd; - int isnum; - int chan = 0; - int bs; - int x; - isnum = 1; - for (x = 0; x < strlen(fn); x++) { - if (!isdigit(fn[x])) { - isnum = 0; - break; - } - } - if (isnum) { - chan = atoi(fn); - if (chan < 1) { - ast_log(LOG_WARNING, "Invalid channel number '%s'\n", fn); - return -1; - } - fn = DAHDI_FILE_CHANNEL; - } - fd = open(fn, O_RDWR | O_NONBLOCK); - if (fd < 0) { - ast_log(LOG_WARNING, "Unable to open '%s': %s\n", fn, strerror(errno)); - return -1; - } - if (chan) { - if (ioctl(fd, DAHDI_SPECIFY, &chan)) { - x = errno; - close(fd); - errno = x; - ast_log(LOG_WARNING, "Unable to specify channel %d: %s\n", chan, strerror(errno)); - return -1; - } - } - bs = READ_SIZE; - if (ioctl(fd, DAHDI_SET_BLOCKSIZE, &bs) == -1) { - ast_log(LOG_WARNING, "Unable to set blocksize '%d': %s\n", bs, strerror(errno)); - x = errno; - close(fd); - errno = x; - return -1; - } - return fd; -} - -static void dahdi_close(int fd) -{ - if (fd > 0) - close(fd); -} - -static void dahdi_close_sub(struct dahdi_pvt *chan_pvt, int sub_num) -{ - dahdi_close(chan_pvt->subs[sub_num].dfd); - chan_pvt->subs[sub_num].dfd = -1; -} - -#ifdef HAVE_PRI -static void dahdi_close_pri_fd(struct dahdi_pri *pri, int fd_num) -{ - dahdi_close(pri->fds[fd_num]); - pri->fds[fd_num] = -1; -} -#endif - -static int dahdi_setlinear(int dfd, int linear) -{ - int res; - res = ioctl(dfd, DAHDI_SETLINEAR, &linear); - if (res) - return res; - return 0; -} - - -static int alloc_sub(struct dahdi_pvt *p, int x) -{ - struct dahdi_bufferinfo bi; - int res; - if (p->subs[x].dfd < 0) { - p->subs[x].dfd = dahdi_open(DAHDI_FILE_PSEUDO); - if (p->subs[x].dfd > -1) { - res = ioctl(p->subs[x].dfd, DAHDI_GET_BUFINFO, &bi); - if (!res) { - bi.txbufpolicy = p->buf_policy; - bi.rxbufpolicy = p->buf_policy; - bi.numbufs = p->buf_no; - res = ioctl(p->subs[x].dfd, DAHDI_SET_BUFINFO, &bi); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to set buffer policy on channel %d: %s\n", x, strerror(errno)); - } - } else - ast_log(LOG_WARNING, "Unable to check buffer policy on channel %d: %s\n", x, strerror(errno)); - if (ioctl(p->subs[x].dfd, DAHDI_CHANNO, &p->subs[x].chan) == 1) { - ast_log(LOG_WARNING, "Unable to get channel number for pseudo channel on FD %d: %s\n", p->subs[x].dfd, strerror(errno)); - dahdi_close_sub(p, x); - return -1; - } - if (option_debug) - ast_log(LOG_DEBUG, "Allocated %s subchannel on FD %d channel %d\n", subnames[x], p->subs[x].dfd, p->subs[x].chan); - return 0; - } else - ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno)); - return -1; - } - ast_log(LOG_WARNING, "%s subchannel of %d already in use\n", subnames[x], p->channel); - return -1; -} - -static int unalloc_sub(struct dahdi_pvt *p, int x) -{ - if (!x) { - ast_log(LOG_WARNING, "Trying to unalloc the real channel %d?!?\n", p->channel); - return -1; - } - ast_log(LOG_DEBUG, "Released sub %d of channel %d\n", x, p->channel); - dahdi_close_sub(p, x); - p->subs[x].linear = 0; - p->subs[x].chan = 0; - p->subs[x].owner = NULL; - p->subs[x].inthreeway = 0; - p->polarity = POLARITY_IDLE; - memset(&p->subs[x].curconf, 0, sizeof(p->subs[x].curconf)); - return 0; -} - -static int digit_to_dtmfindex(char digit) -{ - if (isdigit(digit)) - return DAHDI_TONE_DTMF_BASE + (digit - '0'); - else if (digit >= 'A' && digit <= 'D') - return DAHDI_TONE_DTMF_A + (digit - 'A'); - else if (digit >= 'a' && digit <= 'd') - return DAHDI_TONE_DTMF_A + (digit - 'a'); - else if (digit == '*') - return DAHDI_TONE_DTMF_s; - else if (digit == '#') - return DAHDI_TONE_DTMF_p; - else - return -1; -} - -static int dahdi_digit_begin(struct ast_channel *chan, char digit) -{ - struct dahdi_pvt *pvt; - int index; - int dtmf = -1; - - pvt = chan->tech_pvt; - - ast_mutex_lock(&pvt->lock); - - index = dahdi_get_index(chan, pvt, 0); - - if ((index != SUB_REAL) || !pvt->owner) - goto out; - -#ifdef HAVE_PRI - if ((pvt->sig == SIG_PRI) && (chan->_state == AST_STATE_DIALING) && !pvt->proceeding) { - if (pvt->setup_ack) { - if (!pri_grab(pvt, pvt->pri)) { - pri_information(pvt->pri->pri, pvt->call, digit); - pri_rel(pvt->pri); - } else - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", pvt->span); - } else if (strlen(pvt->dialdest) < sizeof(pvt->dialdest) - 1) { - int res; - ast_log(LOG_DEBUG, "Queueing digit '%c' since setup_ack not yet received\n", digit); - res = strlen(pvt->dialdest); - pvt->dialdest[res++] = digit; - pvt->dialdest[res] = '\0'; - } - goto out; - } -#endif - if ((dtmf = digit_to_dtmfindex(digit)) == -1) - goto out; - - if (pvt->pulse || ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_SENDTONE, &dtmf)) { - int res; - struct dahdi_dialoperation zo = { - .op = DAHDI_DIAL_OP_APPEND, - .dialstr[0] = 'T', - .dialstr[1] = digit, - .dialstr[2] = 0, - }; - if ((res = ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_DIAL, &zo))) - ast_log(LOG_WARNING, "Couldn't dial digit %c: %s\n", digit, strerror(errno)); - else - pvt->dialing = 1; - } else { - ast_log(LOG_DEBUG, "Started VLDTMF digit '%c'\n", digit); - pvt->dialing = 1; - pvt->begindigit = digit; - } - -out: - ast_mutex_unlock(&pvt->lock); - - return 0; -} - -static int dahdi_digit_end(struct ast_channel *chan, char digit, unsigned int duration) -{ - struct dahdi_pvt *pvt; - int res = 0; - int index; - int x; - - pvt = chan->tech_pvt; - - ast_mutex_lock(&pvt->lock); - - index = dahdi_get_index(chan, pvt, 0); - - if ((index != SUB_REAL) || !pvt->owner || pvt->pulse) - goto out; - -#ifdef HAVE_PRI - /* This means that the digit was already sent via PRI signalling */ - if (pvt->sig == SIG_PRI && !pvt->begindigit) - goto out; -#endif - - if (pvt->begindigit) { - x = -1; - ast_log(LOG_DEBUG, "Ending VLDTMF digit '%c'\n", digit); - res = ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_SENDTONE, &x); - pvt->dialing = 0; - pvt->begindigit = 0; - } - -out: - ast_mutex_unlock(&pvt->lock); - - return res; -} - -static char *events[] = { - "No event", - "On hook", - "Ring/Answered", - "Wink/Flash", - "Alarm", - "No more alarm", - "HDLC Abort", - "HDLC Overrun", - "HDLC Bad FCS", - "Dial Complete", - "Ringer On", - "Ringer Off", - "Hook Transition Complete", - "Bits Changed", - "Pulse Start", - "Timer Expired", - "Timer Ping", - "Polarity Reversal", - "Ring Begin", -}; - -static struct { - int alarm; - char *name; -} alarms[] = { - { DAHDI_ALARM_RED, "Red Alarm" }, - { DAHDI_ALARM_YELLOW, "Yellow Alarm" }, - { DAHDI_ALARM_BLUE, "Blue Alarm" }, - { DAHDI_ALARM_RECOVER, "Recovering" }, - { DAHDI_ALARM_LOOPBACK, "Loopback" }, - { DAHDI_ALARM_NOTOPEN, "Not Open" }, - { DAHDI_ALARM_NONE, "None" }, -}; - -static char *alarm2str(int alarm) -{ - int x; - for (x = 0; x < sizeof(alarms) / sizeof(alarms[0]); x++) { - if (alarms[x].alarm & alarm) - return alarms[x].name; - } - return alarm ? "Unknown Alarm" : "No Alarm"; -} - -static char *event2str(int event) -{ - static char buf[256]; - if ((event < (sizeof(events) / sizeof(events[0]))) && (event > -1)) - return events[event]; - sprintf(buf, "Event %d", event); /* safe */ - return buf; -} - -#ifdef HAVE_PRI -static char *dialplan2str(int dialplan) -{ - if (dialplan == -1) { - return("Dynamically set dialplan in ISDN"); - } - return (pri_plan2str(dialplan)); -} -#endif - -static char *dahdi_sig2str(int sig) -{ - static char buf[256]; - switch (sig) { - case SIG_EM: - return "E & M Immediate"; - case SIG_EMWINK: - return "E & M Wink"; - case SIG_EM_E1: - return "E & M E1"; - case SIG_FEATD: - return "Feature Group D (DTMF)"; - case SIG_FEATDMF: - return "Feature Group D (MF)"; - case SIG_FEATDMF_TA: - return "Feature Groud D (MF) Tandem Access"; - case SIG_FEATB: - return "Feature Group B (MF)"; - case SIG_E911: - return "E911 (MF)"; - case SIG_FGC_CAMA: - return "FGC/CAMA (Dialpulse)"; - case SIG_FGC_CAMAMF: - return "FGC/CAMA (MF)"; - case SIG_FXSLS: - return "FXS Loopstart"; - case SIG_FXSGS: - return "FXS Groundstart"; - case SIG_FXSKS: - return "FXS Kewlstart"; - case SIG_FXOLS: - return "FXO Loopstart"; - case SIG_FXOGS: - return "FXO Groundstart"; - case SIG_FXOKS: - return "FXO Kewlstart"; - case SIG_PRI: - return "ISDN PRI"; - case SIG_SF: - return "SF (Tone) Immediate"; - case SIG_SFWINK: - return "SF (Tone) Wink"; - case SIG_SF_FEATD: - return "SF (Tone) with Feature Group D (DTMF)"; - case SIG_SF_FEATDMF: - return "SF (Tone) with Feature Group D (MF)"; - case SIG_SF_FEATB: - return "SF (Tone) with Feature Group B (MF)"; - case SIG_GR303FXOKS: - return "GR-303 with FXOKS"; - case SIG_GR303FXSKS: - return "GR-303 with FXSKS"; - case 0: - return "Pseudo"; - default: - snprintf(buf, sizeof(buf), "Unknown signalling %d", sig); - return buf; - } -} - -#define sig2str dahdi_sig2str - -static int conf_add(struct dahdi_pvt *p, struct dahdi_subchannel *c, int index, int slavechannel) -{ - /* If the conference already exists, and we're already in it - don't bother doing anything */ - struct dahdi_confinfo zi; - - memset(&zi, 0, sizeof(zi)); - zi.chan = 0; - - if (slavechannel > 0) { - /* If we have only one slave, do a digital mon */ - zi.confmode = DAHDI_CONF_DIGITALMON; - zi.confno = slavechannel; - } else { - if (!index) { - /* Real-side and pseudo-side both participate in conference */ - zi.confmode = DAHDI_CONF_REALANDPSEUDO | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER | - DAHDI_CONF_PSEUDO_TALKER | DAHDI_CONF_PSEUDO_LISTENER; - } else - zi.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER; - zi.confno = p->confno; - } - if ((zi.confno == c->curconf.confno) && (zi.confmode == c->curconf.confmode)) - return 0; - if (c->dfd < 0) - return 0; - if (ioctl(c->dfd, DAHDI_SETCONF, &zi)) { - ast_log(LOG_WARNING, "Failed to add %d to conference %d/%d: %s\n", c->dfd, zi.confmode, zi.confno, strerror(errno)); - return -1; - } - if (slavechannel < 1) { - p->confno = zi.confno; - } - memcpy(&c->curconf, &zi, sizeof(c->curconf)); - ast_log(LOG_DEBUG, "Added %d to conference %d/%d\n", c->dfd, c->curconf.confmode, c->curconf.confno); - return 0; -} - -static int isourconf(struct dahdi_pvt *p, struct dahdi_subchannel *c) -{ - /* If they're listening to our channel, they're ours */ - if ((p->channel == c->curconf.confno) && (c->curconf.confmode == DAHDI_CONF_DIGITALMON)) - return 1; - /* If they're a talker on our (allocated) conference, they're ours */ - if ((p->confno > 0) && (p->confno == c->curconf.confno) && (c->curconf.confmode & DAHDI_CONF_TALKER)) - return 1; - return 0; -} - -static int conf_del(struct dahdi_pvt *p, struct dahdi_subchannel *c, int index) -{ - struct dahdi_confinfo zi; - if (/* Can't delete if there's no dfd */ - (c->dfd < 0) || - /* Don't delete from the conference if it's not our conference */ - !isourconf(p, c) - /* Don't delete if we don't think it's conferenced at all (implied) */ - ) return 0; - memset(&zi, 0, sizeof(zi)); - zi.chan = 0; - zi.confno = 0; - zi.confmode = 0; - if (ioctl(c->dfd, DAHDI_SETCONF, &zi)) { - ast_log(LOG_WARNING, "Failed to drop %d from conference %d/%d: %s\n", c->dfd, c->curconf.confmode, c->curconf.confno, strerror(errno)); - return -1; - } - ast_log(LOG_DEBUG, "Removed %d from conference %d/%d\n", c->dfd, c->curconf.confmode, c->curconf.confno); - memcpy(&c->curconf, &zi, sizeof(c->curconf)); - return 0; -} - -static int isslavenative(struct dahdi_pvt *p, struct dahdi_pvt **out) -{ - int x; - int useslavenative; - struct dahdi_pvt *slave = NULL; - /* Start out optimistic */ - useslavenative = 1; - /* Update conference state in a stateless fashion */ - for (x = 0; x < 3; x++) { - /* Any three-way calling makes slave native mode *definitely* out - of the question */ - if ((p->subs[x].dfd > -1) && p->subs[x].inthreeway) - useslavenative = 0; - } - /* If we don't have any 3-way calls, check to see if we have - precisely one slave */ - if (useslavenative) { - for (x = 0; x < MAX_SLAVES; x++) { - if (p->slaves[x]) { - if (slave) { - /* Whoops already have a slave! No - slave native and stop right away */ - slave = NULL; - useslavenative = 0; - break; - } else { - /* We have one slave so far */ - slave = p->slaves[x]; - } - } - } - } - /* If no slave, slave native definitely out */ - if (!slave) - useslavenative = 0; - else if (slave->law != p->law) { - useslavenative = 0; - slave = NULL; - } - if (out) - *out = slave; - return useslavenative; -} - -static int reset_conf(struct dahdi_pvt *p) -{ - struct dahdi_confinfo zi; - memset(&zi, 0, sizeof(zi)); - p->confno = -1; - memset(&p->subs[SUB_REAL].curconf, 0, sizeof(p->subs[SUB_REAL].curconf)); - if (p->subs[SUB_REAL].dfd > -1) { - if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETCONF, &zi)) - ast_log(LOG_WARNING, "Failed to reset conferencing on channel %d: %s\n", p->channel, strerror(errno)); - } - return 0; -} - -static int update_conf(struct dahdi_pvt *p) -{ - int needconf = 0; - int x; - int useslavenative; - struct dahdi_pvt *slave = NULL; - - useslavenative = isslavenative(p, &slave); - /* Start with the obvious, general stuff */ - for (x = 0; x < 3; x++) { - /* Look for three way calls */ - if ((p->subs[x].dfd > -1) && p->subs[x].inthreeway) { - conf_add(p, &p->subs[x], x, 0); - needconf++; - } else { - conf_del(p, &p->subs[x], x); - } - } - /* If we have a slave, add him to our conference now. or DAX - if this is slave native */ - for (x = 0; x < MAX_SLAVES; x++) { - if (p->slaves[x]) { - if (useslavenative) - conf_add(p, &p->slaves[x]->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(p)); - else { - conf_add(p, &p->slaves[x]->subs[SUB_REAL], SUB_REAL, 0); - needconf++; - } - } - } - /* If we're supposed to be in there, do so now */ - if (p->inconference && !p->subs[SUB_REAL].inthreeway) { - if (useslavenative) - conf_add(p, &p->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(slave)); - else { - conf_add(p, &p->subs[SUB_REAL], SUB_REAL, 0); - needconf++; - } - } - /* If we have a master, add ourselves to his conference */ - if (p->master) { - if (isslavenative(p->master, NULL)) { - conf_add(p->master, &p->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(p->master)); - } else { - conf_add(p->master, &p->subs[SUB_REAL], SUB_REAL, 0); - } - } - if (!needconf) { - /* Nobody is left (or should be left) in our conference. - Kill it. */ - p->confno = -1; - } - if (option_debug) - ast_log(LOG_DEBUG, "Updated conferencing on %d, with %d conference users\n", p->channel, needconf); - return 0; -} - -static void dahdi_enable_ec(struct dahdi_pvt *p) -{ - int x; - int res; - if (!p) - return; - if (p->echocanon) { - ast_log(LOG_DEBUG, "Echo cancellation already on\n"); - return; - } - if (p->digital) { - ast_log(LOG_DEBUG, "Echo cancellation isn't required on digital connection\n"); - return; - } - if (p->echocancel) { - if (p->sig == SIG_PRI) { - x = 1; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &x); - if (res) - ast_log(LOG_WARNING, "Unable to enable audio mode on channel %d (%s)\n", p->channel, strerror(errno)); - } - x = p->echocancel; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_ECHOCANCEL, &x); - if (res) - ast_log(LOG_WARNING, "Unable to enable echo cancellation on channel %d (%s)\n", p->channel, strerror(errno)); - else { - p->echocanon = 1; - if (option_debug) - ast_log(LOG_DEBUG, "Enabled echo cancellation on channel %d\n", p->channel); - } - } else if (option_debug) - ast_log(LOG_DEBUG, "No echo cancellation requested\n"); -} - -static void dahdi_train_ec(struct dahdi_pvt *p) -{ - int x; - int res; - if (p && p->echocancel && p->echotraining) { - x = p->echotraining; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_ECHOTRAIN, &x); - if (res) - ast_log(LOG_WARNING, "Unable to request echo training on channel %d: %s\n", p->channel, strerror(errno)); - else { - ast_log(LOG_DEBUG, "Engaged echo training on channel %d\n", p->channel); - } - } else - ast_log(LOG_DEBUG, "No echo training requested\n"); -} - -static void dahdi_disable_ec(struct dahdi_pvt *p) -{ - int x; - int res; - if (p->echocancel) { - x = 0; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_ECHOCANCEL, &x); - if (res) - ast_log(LOG_WARNING, "Unable to disable echo cancellation on channel %d: %s\n", p->channel, strerror(errno)); - else if (option_debug) - ast_log(LOG_DEBUG, "disabled echo cancellation on channel %d\n", p->channel); - } - p->echocanon = 0; -} - -static void fill_txgain(struct dahdi_gains *g, float gain, int law) -{ - int j; - int k; - float linear_gain = pow(10.0, gain / 20.0); - - switch (law) { - case DAHDI_LAW_ALAW: - for (j = 0; j < (sizeof(g->txgain) / sizeof(g->txgain[0])); j++) { - if (gain) { - k = (int) (((float) AST_ALAW(j)) * linear_gain); - if (k > 32767) k = 32767; - if (k < -32767) k = -32767; - g->txgain[j] = AST_LIN2A(k); - } else { - g->txgain[j] = j; - } - } - break; - case DAHDI_LAW_MULAW: - for (j = 0; j < (sizeof(g->txgain) / sizeof(g->txgain[0])); j++) { - if (gain) { - k = (int) (((float) AST_MULAW(j)) * linear_gain); - if (k > 32767) k = 32767; - if (k < -32767) k = -32767; - g->txgain[j] = AST_LIN2MU(k); - } else { - g->txgain[j] = j; - } - } - break; - } -} - -static void fill_rxgain(struct dahdi_gains *g, float gain, int law) -{ - int j; - int k; - float linear_gain = pow(10.0, gain / 20.0); - - switch (law) { - case DAHDI_LAW_ALAW: - for (j = 0; j < (sizeof(g->rxgain) / sizeof(g->rxgain[0])); j++) { - if (gain) { - k = (int) (((float) AST_ALAW(j)) * linear_gain); - if (k > 32767) k = 32767; - if (k < -32767) k = -32767; - g->rxgain[j] = AST_LIN2A(k); - } else { - g->rxgain[j] = j; - } - } - break; - case DAHDI_LAW_MULAW: - for (j = 0; j < (sizeof(g->rxgain) / sizeof(g->rxgain[0])); j++) { - if (gain) { - k = (int) (((float) AST_MULAW(j)) * linear_gain); - if (k > 32767) k = 32767; - if (k < -32767) k = -32767; - g->rxgain[j] = AST_LIN2MU(k); - } else { - g->rxgain[j] = j; - } - } - break; - } -} - -static int set_actual_txgain(int fd, int chan, float gain, int law) -{ - struct dahdi_gains g; - int res; - - memset(&g, 0, sizeof(g)); - g.chan = chan; - res = ioctl(fd, DAHDI_GETGAINS, &g); - if (res) { - if (option_debug) - ast_log(LOG_DEBUG, "Failed to read gains: %s\n", strerror(errno)); - return res; - } - - fill_txgain(&g, gain, law); - - return ioctl(fd, DAHDI_SETGAINS, &g); -} - -static int set_actual_rxgain(int fd, int chan, float gain, int law) -{ - struct dahdi_gains g; - int res; - - memset(&g, 0, sizeof(g)); - g.chan = chan; - res = ioctl(fd, DAHDI_GETGAINS, &g); - if (res) { - ast_log(LOG_DEBUG, "Failed to read gains: %s\n", strerror(errno)); - return res; - } - - fill_rxgain(&g, gain, law); - - return ioctl(fd, DAHDI_SETGAINS, &g); -} - -static int set_actual_gain(int fd, int chan, float rxgain, float txgain, int law) -{ - return set_actual_txgain(fd, chan, txgain, law) | set_actual_rxgain(fd, chan, rxgain, law); -} - -static int bump_gains(struct dahdi_pvt *p) -{ - int res; - - /* Bump receive gain by 5.0db */ - res = set_actual_gain(p->subs[SUB_REAL].dfd, 0, p->rxgain + 5.0, p->txgain, p->law); - if (res) { - ast_log(LOG_WARNING, "Unable to bump gain: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -static int restore_gains(struct dahdi_pvt *p) -{ - int res; - - res = set_actual_gain(p->subs[SUB_REAL].dfd, 0, p->rxgain, p->txgain, p->law); - if (res) { - ast_log(LOG_WARNING, "Unable to restore gains: %s\n", strerror(errno)); - return -1; - } - - return 0; -} - -static inline int dahdi_set_hook(int fd, int hs) -{ - int x, res; - - x = hs; - res = ioctl(fd, DAHDI_HOOK, &x); - - if (res < 0) { - if (errno == EINPROGRESS) - return 0; - ast_log(LOG_WARNING, "DAHDI hook failed returned %d (trying %d): %s\n", res, hs, strerror(errno)); - /* will expectedly fail if phone is off hook during operation, such as during a restart */ - } - - return res; -} - -static inline int dahdi_confmute(struct dahdi_pvt *p, int muted) -{ - int x, y, res; - x = muted; - if (p->sig == SIG_PRI) { - y = 1; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &y); - if (res) - ast_log(LOG_WARNING, "Unable to set audio mode on %d: %s\n", p->channel, strerror(errno)); - } - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_CONFMUTE, &x); - if (res < 0) - ast_log(LOG_WARNING, "dahdi confmute(%d) failed on channel %d: %s\n", muted, p->channel, strerror(errno)); - return res; -} - -static int save_conference(struct dahdi_pvt *p) -{ - struct dahdi_confinfo c; - int res; - if (p->saveconf.confmode) { - ast_log(LOG_WARNING, "Can't save conference -- already in use\n"); - return -1; - } - p->saveconf.chan = 0; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GETCONF, &p->saveconf); - if (res) { - ast_log(LOG_WARNING, "Unable to get conference info: %s\n", strerror(errno)); - p->saveconf.confmode = 0; - return -1; - } - c.chan = 0; - c.confno = 0; - c.confmode = DAHDI_CONF_NORMAL; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETCONF, &c); - if (res) { - ast_log(LOG_WARNING, "Unable to set conference info: %s\n", strerror(errno)); - return -1; - } - if (option_debug) - ast_log(LOG_DEBUG, "Disabled conferencing\n"); - return 0; -} - -static int restore_conference(struct dahdi_pvt *p) -{ - int res; - if (p->saveconf.confmode) { - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETCONF, &p->saveconf); - p->saveconf.confmode = 0; - if (res) { - ast_log(LOG_WARNING, "Unable to restore conference info: %s\n", strerror(errno)); - return -1; - } - } - if (option_debug) - ast_log(LOG_DEBUG, "Restored conferencing\n"); - return 0; -} - -static int send_callerid(struct dahdi_pvt *p); - -static int send_cwcidspill(struct dahdi_pvt *p) -{ - p->callwaitcas = 0; - p->cidcwexpire = 0; - if (!(p->cidspill = ast_malloc(MAX_CALLERID_SIZE))) - return -1; - p->cidlen = ast_callerid_callwaiting_generate(p->cidspill, p->callwait_name, p->callwait_num, AST_LAW(p)); - /* Make sure we account for the end */ - p->cidlen += READ_SIZE * 4; - p->cidpos = 0; - send_callerid(p); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "CPE supports Call Waiting Caller*ID. Sending '%s/%s'\n", p->callwait_name, p->callwait_num); - return 0; -} - -static int has_voicemail(struct dahdi_pvt *p) -{ - - return ast_app_has_voicemail(p->mailbox, NULL); -} - -static int send_callerid(struct dahdi_pvt *p) -{ - /* Assumes spill in p->cidspill, p->cidlen in length and we're p->cidpos into it */ - int res; - /* Take out of linear mode if necessary */ - if (p->subs[SUB_REAL].linear) { - p->subs[SUB_REAL].linear = 0; - dahdi_setlinear(p->subs[SUB_REAL].dfd, 0); - } - while (p->cidpos < p->cidlen) { - res = write(p->subs[SUB_REAL].dfd, p->cidspill + p->cidpos, p->cidlen - p->cidpos); - if (res < 0) { - if (errno == EAGAIN) - return 0; - else { - ast_log(LOG_WARNING, "write failed: %s\n", strerror(errno)); - return -1; - } - } - if (!res) - return 0; - p->cidpos += res; - } - free(p->cidspill); - p->cidspill = NULL; - if (p->callwaitcas) { - /* Wait for CID/CW to expire */ - p->cidcwexpire = CIDCW_EXPIRE_SAMPLES; - } else - restore_conference(p); - return 0; -} - -static int dahdi_callwait(struct ast_channel *ast) -{ - struct dahdi_pvt *p = ast->tech_pvt; - p->callwaitingrepeat = CALLWAITING_REPEAT_SAMPLES; - if (p->cidspill) { - ast_log(LOG_WARNING, "Spill already exists?!?\n"); - free(p->cidspill); - } - if (!(p->cidspill = ast_malloc(2400 /* SAS */ + 680 /* CAS */ + READ_SIZE * 4))) - return -1; - save_conference(p); - /* Silence */ - memset(p->cidspill, 0x7f, 2400 + 600 + READ_SIZE * 4); - if (!p->callwaitrings && p->callwaitingcallerid) { - ast_gen_cas(p->cidspill, 1, 2400 + 680, AST_LAW(p)); - p->callwaitcas = 1; - p->cidlen = 2400 + 680 + READ_SIZE * 4; - } else { - ast_gen_cas(p->cidspill, 1, 2400, AST_LAW(p)); - p->callwaitcas = 0; - p->cidlen = 2400 + READ_SIZE * 4; - } - p->cidpos = 0; - send_callerid(p); - - return 0; -} - -static int dahdi_call(struct ast_channel *ast, char *rdest, int timeout) -{ - struct dahdi_pvt *p = ast->tech_pvt; - int x, res, index,mysig; - char *c, *n, *l; -#ifdef HAVE_PRI - char *s = NULL; -#endif - char dest[256]; /* must be same length as p->dialdest */ - ast_mutex_lock(&p->lock); - ast_copy_string(dest, rdest, sizeof(dest)); - ast_copy_string(p->dialdest, rdest, sizeof(p->dialdest)); - if ((ast->_state == AST_STATE_BUSY)) { - p->subs[SUB_REAL].needbusy = 1; - ast_mutex_unlock(&p->lock); - return 0; - } - if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { - ast_log(LOG_WARNING, "dahdi_call called on %s, neither down nor reserved\n", ast->name); - ast_mutex_unlock(&p->lock); - return -1; - } - p->dialednone = 0; - if ((p->radio || (p->oprmode < 0))) /* if a radio channel, up immediately */ - { - /* Special pseudo -- automatically up */ - ast_setstate(ast, AST_STATE_UP); - ast_mutex_unlock(&p->lock); - return 0; - } - x = DAHDI_FLUSH_READ | DAHDI_FLUSH_WRITE; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_FLUSH, &x); - if (res) - ast_log(LOG_WARNING, "Unable to flush input on channel %d: %s\n", p->channel, strerror(errno)); - p->outgoing = 1; - - set_actual_gain(p->subs[SUB_REAL].dfd, 0, p->rxgain, p->txgain, p->law); - - mysig = p->sig; - if (p->outsigmod > -1) - mysig = p->outsigmod; - - switch (mysig) { - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FXOKS: - if (p->owner == ast) { - /* Normal ring, on hook */ - - /* Don't send audio while on hook, until the call is answered */ - p->dialing = 1; - if (p->use_callerid) { - /* Generate the Caller-ID spill if desired */ - if (p->cidspill) { - ast_log(LOG_WARNING, "cidspill already exists??\n"); - free(p->cidspill); - } - p->callwaitcas = 0; - if ((p->cidspill = ast_malloc(MAX_CALLERID_SIZE))) { - p->cidlen = ast_callerid_generate(p->cidspill, ast->cid.cid_name, ast->cid.cid_num, AST_LAW(p)); - p->cidpos = 0; - send_callerid(p); - } - } - /* Choose proper cadence */ - if ((p->distinctivering > 0) && (p->distinctivering <= num_cadence)) { - if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETCADENCE, &cadences[p->distinctivering - 1])) - ast_log(LOG_WARNING, "Unable to set distinctive ring cadence %d on '%s': %s\n", p->distinctivering, ast->name, strerror(errno)); - p->cidrings = cidrings[p->distinctivering - 1]; - } else { - if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETCADENCE, NULL)) - ast_log(LOG_WARNING, "Unable to reset default ring on '%s': %s\n", ast->name, strerror(errno)); - p->cidrings = p->sendcalleridafter; - } - - /* nick@dccinc.com 4/3/03 mods to allow for deferred dialing */ - c = strchr(dest, '/'); - if (c) - c++; - if (c && (strlen(c) < p->stripmsd)) { - ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd); - c = NULL; - } - if (c) { - p->dop.op = DAHDI_DIAL_OP_REPLACE; - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "Tw%s", c); - ast_log(LOG_DEBUG, "FXO: setup deferred dialstring: %s\n", c); - } else { - p->dop.dialstr[0] = '\0'; - } - x = DAHDI_RING; - if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x) && (errno != EINPROGRESS)) { - ast_log(LOG_WARNING, "Unable to ring phone: %s\n", strerror(errno)); - ast_mutex_unlock(&p->lock); - return -1; - } - p->dialing = 1; - } else { - /* Call waiting call */ - p->callwaitrings = 0; - if (ast->cid.cid_num) - ast_copy_string(p->callwait_num, ast->cid.cid_num, sizeof(p->callwait_num)); - else - p->callwait_num[0] = '\0'; - if (ast->cid.cid_name) - ast_copy_string(p->callwait_name, ast->cid.cid_name, sizeof(p->callwait_name)); - else - p->callwait_name[0] = '\0'; - /* Call waiting tone instead */ - if (dahdi_callwait(ast)) { - ast_mutex_unlock(&p->lock); - return -1; - } - /* Make ring-back */ - if (tone_zone_play_tone(p->subs[SUB_CALLWAIT].dfd, DAHDI_TONE_RINGTONE)) - ast_log(LOG_WARNING, "Unable to generate call-wait ring-back on channel %s\n", ast->name); - - } - n = ast->cid.cid_name; - l = ast->cid.cid_num; - if (l) - ast_copy_string(p->lastcid_num, l, sizeof(p->lastcid_num)); - else - p->lastcid_num[0] = '\0'; - if (n) - ast_copy_string(p->lastcid_name, n, sizeof(p->lastcid_name)); - else - p->lastcid_name[0] = '\0'; - ast_setstate(ast, AST_STATE_RINGING); - index = dahdi_get_index(ast, p, 0); - if (index > -1) { - p->subs[index].needringing = 1; - } - break; - case SIG_FXSLS: - case SIG_FXSGS: - case SIG_FXSKS: - case SIG_EMWINK: - case SIG_EM: - case SIG_EM_E1: - case SIG_FEATD: - case SIG_FEATDMF: - case SIG_E911: - case SIG_FGC_CAMA: - case SIG_FGC_CAMAMF: - case SIG_FEATB: - case SIG_SFWINK: - case SIG_SF: - case SIG_SF_FEATD: - case SIG_SF_FEATDMF: - case SIG_FEATDMF_TA: - case SIG_SF_FEATB: - c = strchr(dest, '/'); - if (c) - c++; - else - c = ""; - if (strlen(c) < p->stripmsd) { - ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd); - ast_mutex_unlock(&p->lock); - return -1; - } -#ifdef HAVE_PRI - /* Start the trunk, if not GR-303 */ - if (!p->pri) { -#endif - x = DAHDI_START; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); - if (res < 0) { - if (errno != EINPROGRESS) { - ast_log(LOG_WARNING, "Unable to start channel: %s\n", strerror(errno)); - ast_mutex_unlock(&p->lock); - return -1; - } - } -#ifdef HAVE_PRI - } -#endif - ast_log(LOG_DEBUG, "Dialing '%s'\n", c); - p->dop.op = DAHDI_DIAL_OP_REPLACE; - - c += p->stripmsd; - - switch (mysig) { - case SIG_FEATD: - l = ast->cid.cid_num; - if (l) - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T*%s*%s*", l, c); - else - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T**%s*", c); - break; - case SIG_FEATDMF: - l = ast->cid.cid_num; - if (l) - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*00%s#*%s#", l, c); - else - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*02#*%s#", c); - break; - case SIG_FEATDMF_TA: - { - const char *cic, *ozz; - - /* If you have to go through a Tandem Access point you need to use this */ - ozz = pbx_builtin_getvar_helper(p->owner, "FEATDMF_OZZ"); - if (!ozz) - ozz = defaultozz; - cic = pbx_builtin_getvar_helper(p->owner, "FEATDMF_CIC"); - if (!cic) - cic = defaultcic; - if (!ozz || !cic) { - ast_log(LOG_WARNING, "Unable to dial channel of type feature group D MF tandem access without CIC or OZZ set\n"); - ast_mutex_unlock(&p->lock); - return -1; - } - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s%s#", ozz, cic); - snprintf(p->finaldial, sizeof(p->finaldial), "M*%s#", c); - p->whichwink = 0; - } - break; - case SIG_E911: - ast_copy_string(p->dop.dialstr, "M*911#", sizeof(p->dop.dialstr)); - break; - case SIG_FGC_CAMA: - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%s", c); - break; - case SIG_FGC_CAMAMF: - case SIG_FEATB: - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s#", c); - break; - default: - if (p->pulse) - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%sw", c); - else - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T%sw", c); - break; - } - - if (p->echotraining && (strlen(p->dop.dialstr) > 4)) { - memset(p->echorest, 'w', sizeof(p->echorest) - 1); - strcpy(p->echorest + (p->echotraining / 400) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2); - p->echorest[sizeof(p->echorest) - 1] = '\0'; - p->echobreak = 1; - p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0'; - } else - p->echobreak = 0; - if (!res) { - if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop)) { - int saveerr = errno; - - x = DAHDI_ONHOOK; - ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); - ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(saveerr)); - ast_mutex_unlock(&p->lock); - return -1; - } - } else - ast_log(LOG_DEBUG, "Deferring dialing...\n"); - p->dialing = 1; - if (ast_strlen_zero(c)) - p->dialednone = 1; - ast_setstate(ast, AST_STATE_DIALING); - break; - case 0: - /* Special pseudo -- automatically up*/ - ast_setstate(ast, AST_STATE_UP); - break; - case SIG_PRI: - /* We'll get it in a moment -- but use dialdest to store pre-setup_ack digits */ - p->dialdest[0] = '\0'; - break; - default: - ast_log(LOG_DEBUG, "not yet implemented\n"); - ast_mutex_unlock(&p->lock); - return -1; - } -#ifdef HAVE_PRI - if (p->pri) { - struct pri_sr *sr; -#ifdef SUPPORT_USERUSER - const char *useruser; -#endif - int pridialplan; - int dp_strip; - int prilocaldialplan; - int ldp_strip; - int exclusive; - const char *rr_str; - int redirect_reason; - - c = strchr(dest, '/'); - if (c) - c++; - else - c = dest; - - l = NULL; - n = NULL; - - if (!p->hidecallerid) { - l = ast->cid.cid_num; - if (!p->hidecalleridname) { - n = ast->cid.cid_name; - } - } - - - if (strlen(c) < p->stripmsd) { - ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd); - ast_mutex_unlock(&p->lock); - return -1; - } - if (mysig != SIG_FXSKS) { - p->dop.op = DAHDI_DIAL_OP_REPLACE; - s = strchr(c + p->stripmsd, 'w'); - if (s) { - if (strlen(s) > 1) - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T%s", s); - else - p->dop.dialstr[0] = '\0'; - *s = '\0'; - } else { - p->dop.dialstr[0] = '\0'; - } - } - if (pri_grab(p, p->pri)) { - ast_log(LOG_WARNING, "Failed to grab PRI!\n"); - ast_mutex_unlock(&p->lock); - return -1; - } - if (!(p->call = pri_new_call(p->pri->pri))) { - ast_log(LOG_WARNING, "Unable to create call on channel %d\n", p->channel); - pri_rel(p->pri); - ast_mutex_unlock(&p->lock); - return -1; - } - if (!(sr = pri_sr_new())) { - ast_log(LOG_WARNING, "Failed to allocate setup request channel %d\n", p->channel); - pri_destroycall(p->pri->pri, p->call); - p->call = NULL; - pri_rel(p->pri); - ast_mutex_unlock(&p->lock); - return -1; - } - if (p->bearer || (mysig == SIG_FXSKS)) { - if (p->bearer) { - ast_log(LOG_DEBUG, "Oooh, I have a bearer on %d (%d:%d)\n", PVT_TO_CHANNEL(p->bearer), p->bearer->logicalspan, p->bearer->channel); - p->bearer->call = p->call; - } else - ast_log(LOG_DEBUG, "I'm being setup with no bearer right now...\n"); - pri_set_crv(p->pri->pri, p->call, p->channel, 0); - } - p->digital = IS_DIGITAL(ast->transfercapability); - /* Add support for exclusive override */ - if (p->priexclusive) - exclusive = 1; - else { - /* otherwise, traditional behavior */ - if (p->pri->nodetype == PRI_NETWORK) - exclusive = 0; - else - exclusive = 1; - } - - pri_sr_set_channel(sr, p->bearer ? PVT_TO_CHANNEL(p->bearer) : PVT_TO_CHANNEL(p), exclusive, 1); - pri_sr_set_bearer(sr, p->digital ? PRI_TRANS_CAP_DIGITAL : ast->transfercapability, - (p->digital ? -1 : - ((p->law == DAHDI_LAW_ALAW) ? PRI_LAYER_1_ALAW : PRI_LAYER_1_ULAW))); - if (p->pri->facilityenable) - pri_facility_enable(p->pri->pri); - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Requested transfer capability: 0x%.2x - %s\n", ast->transfercapability, ast_transfercapability2str(ast->transfercapability)); - dp_strip = 0; - pridialplan = p->pri->dialplan - 1; - if (pridialplan == -2) { /* compute dynamically */ - if (strncmp(c + p->stripmsd, p->pri->internationalprefix, strlen(p->pri->internationalprefix)) == 0) { - dp_strip = strlen(p->pri->internationalprefix); - pridialplan = PRI_INTERNATIONAL_ISDN; - } else if (strncmp(c + p->stripmsd, p->pri->nationalprefix, strlen(p->pri->nationalprefix)) == 0) { - dp_strip = strlen(p->pri->nationalprefix); - pridialplan = PRI_NATIONAL_ISDN; - } else { - pridialplan = PRI_LOCAL_ISDN; - } - } - pri_sr_set_called(sr, c + p->stripmsd + dp_strip, pridialplan, s ? 1 : 0); - - ldp_strip = 0; - prilocaldialplan = p->pri->localdialplan - 1; - if ((l != NULL) && (prilocaldialplan == -2)) { /* compute dynamically */ - if (strncmp(l, p->pri->internationalprefix, strlen(p->pri->internationalprefix)) == 0) { - ldp_strip = strlen(p->pri->internationalprefix); - prilocaldialplan = PRI_INTERNATIONAL_ISDN; - } else if (strncmp(l, p->pri->nationalprefix, strlen(p->pri->nationalprefix)) == 0) { - ldp_strip = strlen(p->pri->nationalprefix); - prilocaldialplan = PRI_NATIONAL_ISDN; - } else { - prilocaldialplan = PRI_LOCAL_ISDN; - } - } - pri_sr_set_caller(sr, l ? (l + ldp_strip) : NULL, n, prilocaldialplan, - p->use_callingpres ? ast->cid.cid_pres : (l ? PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN : PRES_NUMBER_NOT_AVAILABLE)); - if ((rr_str = pbx_builtin_getvar_helper(ast, "PRIREDIRECTREASON"))) { - if (!strcasecmp(rr_str, "UNKNOWN")) - redirect_reason = 0; - else if (!strcasecmp(rr_str, "BUSY")) - redirect_reason = 1; - else if (!strcasecmp(rr_str, "NO_REPLY")) - redirect_reason = 2; - else if (!strcasecmp(rr_str, "UNCONDITIONAL")) - redirect_reason = 15; - else - redirect_reason = PRI_REDIR_UNCONDITIONAL; - } else - redirect_reason = PRI_REDIR_UNCONDITIONAL; - pri_sr_set_redirecting(sr, ast->cid.cid_rdnis, p->pri->localdialplan - 1, PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, redirect_reason); - -#ifdef SUPPORT_USERUSER - /* User-user info */ - useruser = pbx_builtin_getvar_helper(p->owner, "USERUSERINFO"); - - if (useruser) - pri_sr_set_useruser(sr, useruser); -#endif - - if (pri_setup(p->pri->pri, p->call, sr)) { - ast_log(LOG_WARNING, "Unable to setup call to %s (using %s)\n", - c + p->stripmsd + dp_strip, dialplan2str(p->pri->dialplan)); - pri_rel(p->pri); - ast_mutex_unlock(&p->lock); - pri_sr_free(sr); - return -1; - } - pri_sr_free(sr); - ast_setstate(ast, AST_STATE_DIALING); - pri_rel(p->pri); - } -#endif - ast_mutex_unlock(&p->lock); - return 0; -} - -static void destroy_dahdi_pvt(struct dahdi_pvt **pvt) -{ - struct dahdi_pvt *p = *pvt; - /* Remove channel from the list */ - if (p->prev) - p->prev->next = p->next; - if (p->next) - p->next->prev = p->prev; - if (p->use_smdi) - ast_smdi_interface_unref(p->smdi_iface); - ast_mutex_destroy(&p->lock); - dahdi_close_sub(p, SUB_REAL); - if (p->owner) - p->owner->tech_pvt = NULL; - free(p); - *pvt = NULL; -} - -static int destroy_channel(struct dahdi_pvt *prev, struct dahdi_pvt *cur, int now) -{ - int owned = 0; - int i = 0; - - if (!now) { - if (cur->owner) { - owned = 1; - } - - for (i = 0; i < 3; i++) { - if (cur->subs[i].owner) { - owned = 1; - } - } - if (!owned) { - if (prev) { - prev->next = cur->next; - if (prev->next) - prev->next->prev = prev; - else - ifend = prev; - } else { - iflist = cur->next; - if (iflist) - iflist->prev = NULL; - else - ifend = NULL; - } - destroy_dahdi_pvt(&cur); - } - } else { - if (prev) { - prev->next = cur->next; - if (prev->next) - prev->next->prev = prev; - else - ifend = prev; - } else { - iflist = cur->next; - if (iflist) - iflist->prev = NULL; - else - ifend = NULL; - } - destroy_dahdi_pvt(&cur); - } - return 0; -} - -static void destroy_all_channels(void) -{ - int x; - struct dahdi_pvt *p, *pl; - - while (num_restart_pending) { - usleep(1); - } - - ast_mutex_lock(&iflock); - /* Destroy all the interfaces and free their memory */ - p = iflist; - while (p) { - /* Free any callerid */ - if (p->cidspill) - ast_free(p->cidspill); - pl = p; - p = p->next; - x = pl->channel; - /* Free associated memory */ - if (pl) - destroy_dahdi_pvt(&pl); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_2 "Unregistered channel %d\n", x); - } - iflist = NULL; - ifcount = 0; - ast_mutex_unlock(&iflock); -} - -#ifdef HAVE_PRI -static char *dahdi_send_keypad_facility_app = "DAHDISendKeypadFacility"; -static char *zap_send_keypad_facility_app = "ZapSendKeypadFacility"; - -static char *dahdi_send_keypad_facility_synopsis = "Send digits out of band over a PRI"; -static char *zap_send_keypad_facility_synopsis = "Send digits out of band over a PRI"; - -static char *dahdi_send_keypad_facility_descrip = -" DAHDISendKeypadFacility(): This application will send the given string of digits in a Keypad Facility\n" -" IE over the current channel.\n"; -static char *zap_send_keypad_facility_descrip = -" ZapSendKeypadFacility(): This application will send the given string of digits in a Keypad Facility\n" -" IE over the current channel.\n"; - -static int send_keypad_facility_exec(struct ast_channel *chan, void *data) -{ - /* Data will be our digit string */ - struct dahdi_pvt *p; - char *digits = (char *) data; - - if (ast_strlen_zero(digits)) { - ast_log(LOG_DEBUG, "No digit string sent to application!\n"); - return -1; - } - - p = (struct dahdi_pvt *)chan->tech_pvt; - - if (!p) { - ast_log(LOG_DEBUG, "Unable to find technology private\n"); - return -1; - } - - ast_mutex_lock(&p->lock); - - if (!p->pri || !p->call) { - ast_log(LOG_DEBUG, "Unable to find pri or call on channel!\n"); - ast_mutex_unlock(&p->lock); - return -1; - } - - if (!pri_grab(p, p->pri)) { - pri_keypad_facility(p->pri->pri, p->call, digits); - pri_rel(p->pri); - } else { - ast_log(LOG_DEBUG, "Unable to grab pri to send keypad facility!\n"); - ast_mutex_unlock(&p->lock); - return -1; - } - - ast_mutex_unlock(&p->lock); - - return 0; -} - -static int dahdi_send_keypad_facility_exec(struct ast_channel *chan, void *data) -{ - return send_keypad_facility_exec(chan, data); -} - -static int zap_send_keypad_facility_exec(struct ast_channel *chan, void *data) -{ - ast_log(LOG_WARNING, "Use of the command %s is deprecated, please use %s instead.\n", zap_send_keypad_facility_app, dahdi_send_keypad_facility_app); - return send_keypad_facility_exec(chan, data); -} - -static int pri_is_up(struct dahdi_pri *pri) -{ - int x; - for (x = 0; x < NUM_DCHANS; x++) { - if (pri->dchanavail[x] == DCHAN_AVAILABLE) - return 1; - } - return 0; -} - -static int pri_assign_bearer(struct dahdi_pvt *crv, struct dahdi_pri *pri, struct dahdi_pvt *bearer) -{ - bearer->owner = &inuse; - bearer->realcall = crv; - crv->subs[SUB_REAL].dfd = bearer->subs[SUB_REAL].dfd; - if (crv->subs[SUB_REAL].owner) - crv->subs[SUB_REAL].owner->fds[0] = crv->subs[SUB_REAL].dfd; - crv->bearer = bearer; - crv->call = bearer->call; - crv->pri = pri; - return 0; -} - -static char *pri_order(int level) -{ - switch (level) { - case 0: - return "Primary"; - case 1: - return "Secondary"; - case 2: - return "Tertiary"; - case 3: - return "Quaternary"; - default: - return "<Unknown>"; - } -} - -/* Returns fd of the active dchan */ -static int pri_active_dchan_fd(struct dahdi_pri *pri) -{ - int x = -1; - - for (x = 0; x < NUM_DCHANS; x++) { - if ((pri->dchans[x] == pri->pri)) - break; - } - - return pri->fds[x]; -} - -static int pri_find_dchan(struct dahdi_pri *pri) -{ - int oldslot = -1; - struct pri *old; - int newslot = -1; - int x; - old = pri->pri; - for (x = 0; x < NUM_DCHANS; x++) { - if ((pri->dchanavail[x] == DCHAN_AVAILABLE) && (newslot < 0)) - newslot = x; - if (pri->dchans[x] == old) { - oldslot = x; - } - } - if (newslot < 0) { - newslot = 0; - ast_log(LOG_WARNING, "No D-channels available! Using Primary channel %d as D-channel anyway!\n", - pri->dchannels[newslot]); - } - if (old && (oldslot != newslot)) - ast_log(LOG_NOTICE, "Switching from from d-channel %d to channel %d!\n", - pri->dchannels[oldslot], pri->dchannels[newslot]); - pri->pri = pri->dchans[newslot]; - return 0; -} -#endif - -static int dahdi_hangup(struct ast_channel *ast) -{ - int res; - int index,x, law; - /*static int restore_gains(struct dahdi_pvt *p);*/ - struct dahdi_pvt *p = ast->tech_pvt; - struct dahdi_pvt *tmp = NULL; - struct dahdi_pvt *prev = NULL; - struct dahdi_params par; - - if (option_debug) - ast_log(LOG_DEBUG, "dahdi_hangup(%s)\n", ast->name); - if (!ast->tech_pvt) { - ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); - return 0; - } - - ast_mutex_lock(&p->lock); - - index = dahdi_get_index(ast, p, 1); - - if (p->sig == SIG_PRI) { - x = 1; - ast_channel_setoption(ast,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0); - } - - x = 0; - dahdi_confmute(p, 0); - restore_gains(p); - if (p->origcid_num) { - ast_copy_string(p->cid_num, p->origcid_num, sizeof(p->cid_num)); - free(p->origcid_num); - p->origcid_num = NULL; - } - if (p->origcid_name) { - ast_copy_string(p->cid_name, p->origcid_name, sizeof(p->cid_name)); - free(p->origcid_name); - p->origcid_name = NULL; - } - if (p->dsp) - ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax); - if (p->exten) - p->exten[0] = '\0'; - - if (option_debug) - ast_log(LOG_DEBUG, "Hangup: channel: %d index = %d, normal = %d, callwait = %d, thirdcall = %d\n", - p->channel, index, p->subs[SUB_REAL].dfd, p->subs[SUB_CALLWAIT].dfd, p->subs[SUB_THREEWAY].dfd); - p->ignoredtmf = 0; - - if (index > -1) { - /* Real channel, do some fixup */ - p->subs[index].owner = NULL; - p->subs[index].needanswer = 0; - p->subs[index].needflash = 0; - p->subs[index].needringing = 0; - p->subs[index].needbusy = 0; - p->subs[index].needcongestion = 0; - p->subs[index].linear = 0; - p->subs[index].needcallerid = 0; - p->polarity = POLARITY_IDLE; - dahdi_setlinear(p->subs[index].dfd, 0); - if (index == SUB_REAL) { - if ((p->subs[SUB_CALLWAIT].dfd > -1) && (p->subs[SUB_THREEWAY].dfd > -1)) { - ast_log(LOG_DEBUG, "Normal call hung up with both three way call and a call waiting call in place?\n"); - if (p->subs[SUB_CALLWAIT].inthreeway) { - /* We had flipped over to answer a callwait and now it's gone */ - ast_log(LOG_DEBUG, "We were flipped over to the callwait, moving back and unowning.\n"); - /* Move to the call-wait, but un-own us until they flip back. */ - swap_subs(p, SUB_CALLWAIT, SUB_REAL); - unalloc_sub(p, SUB_CALLWAIT); - p->owner = NULL; - } else { - /* The three way hung up, but we still have a call wait */ - ast_log(LOG_DEBUG, "We were in the threeway and have a callwait still. Ditching the threeway.\n"); - swap_subs(p, SUB_THREEWAY, SUB_REAL); - unalloc_sub(p, SUB_THREEWAY); - if (p->subs[SUB_REAL].inthreeway) { - /* This was part of a three way call. Immediately make way for - another call */ - ast_log(LOG_DEBUG, "Call was complete, setting owner to former third call\n"); - p->owner = p->subs[SUB_REAL].owner; - } else { - /* This call hasn't been completed yet... Set owner to NULL */ - ast_log(LOG_DEBUG, "Call was incomplete, setting owner to NULL\n"); - p->owner = NULL; - } - p->subs[SUB_REAL].inthreeway = 0; - } - } else if (p->subs[SUB_CALLWAIT].dfd > -1) { - /* Move to the call-wait and switch back to them. */ - swap_subs(p, SUB_CALLWAIT, SUB_REAL); - unalloc_sub(p, SUB_CALLWAIT); - p->owner = p->subs[SUB_REAL].owner; - if (p->owner->_state != AST_STATE_UP) - p->subs[SUB_REAL].needanswer = 1; - if (ast_bridged_channel(p->subs[SUB_REAL].owner)) - ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD); - } else if (p->subs[SUB_THREEWAY].dfd > -1) { - swap_subs(p, SUB_THREEWAY, SUB_REAL); - unalloc_sub(p, SUB_THREEWAY); - if (p->subs[SUB_REAL].inthreeway) { - /* This was part of a three way call. Immediately make way for - another call */ - ast_log(LOG_DEBUG, "Call was complete, setting owner to former third call\n"); - p->owner = p->subs[SUB_REAL].owner; - } else { - /* This call hasn't been completed yet... Set owner to NULL */ - ast_log(LOG_DEBUG, "Call was incomplete, setting owner to NULL\n"); - p->owner = NULL; - } - p->subs[SUB_REAL].inthreeway = 0; - } - } else if (index == SUB_CALLWAIT) { - /* Ditch the holding callwait call, and immediately make it availabe */ - if (p->subs[SUB_CALLWAIT].inthreeway) { - /* This is actually part of a three way, placed on hold. Place the third part - on music on hold now */ - if (p->subs[SUB_THREEWAY].owner && ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) { - ast_queue_control_data(p->subs[SUB_THREEWAY].owner, AST_CONTROL_HOLD, - S_OR(p->mohsuggest, NULL), - !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); - } - p->subs[SUB_THREEWAY].inthreeway = 0; - /* Make it the call wait now */ - swap_subs(p, SUB_CALLWAIT, SUB_THREEWAY); - unalloc_sub(p, SUB_THREEWAY); - } else - unalloc_sub(p, SUB_CALLWAIT); - } else if (index == SUB_THREEWAY) { - if (p->subs[SUB_CALLWAIT].inthreeway) { - /* The other party of the three way call is currently in a call-wait state. - Start music on hold for them, and take the main guy out of the third call */ - if (p->subs[SUB_CALLWAIT].owner && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) { - ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD, - S_OR(p->mohsuggest, NULL), - !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); - } - p->subs[SUB_CALLWAIT].inthreeway = 0; - } - p->subs[SUB_REAL].inthreeway = 0; - /* If this was part of a three way call index, let us make - another three way call */ - unalloc_sub(p, SUB_THREEWAY); - } else { - /* This wasn't any sort of call, but how are we an index? */ - ast_log(LOG_WARNING, "Index found but not any type of call?\n"); - } - } - - if (!p->subs[SUB_REAL].owner && !p->subs[SUB_CALLWAIT].owner && !p->subs[SUB_THREEWAY].owner) { - p->owner = NULL; - p->ringt = 0; - p->distinctivering = 0; - p->confirmanswer = 0; - p->cidrings = 1; - p->outgoing = 0; - p->digital = 0; - p->faxhandled = 0; - p->pulsedial = 0; - p->onhooktime = time(NULL); -#ifdef HAVE_PRI - p->proceeding = 0; - p->progress = 0; - p->alerting = 0; - p->setup_ack = 0; -#endif - if (p->dsp) { - ast_dsp_free(p->dsp); - p->dsp = NULL; - } - - law = DAHDI_LAW_DEFAULT; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETLAW, &law); - if (res < 0) - ast_log(LOG_WARNING, "Unable to set law on channel %d to default: %s\n", p->channel, strerror(errno)); - /* Perform low level hangup if no owner left */ -#ifdef HAVE_PRI - if (p->pri) { -#ifdef SUPPORT_USERUSER - const char *useruser = pbx_builtin_getvar_helper(ast,"USERUSERINFO"); -#endif - - /* Make sure we have a call (or REALLY have a call in the case of a PRI) */ - if (p->call && (!p->bearer || (p->bearer->call == p->call))) { - if (!pri_grab(p, p->pri)) { - if (p->alreadyhungup) { - ast_log(LOG_DEBUG, "Already hungup... Calling hangup once, and clearing call\n"); - -#ifdef SUPPORT_USERUSER - pri_call_set_useruser(p->call, useruser); -#endif - - pri_hangup(p->pri->pri, p->call, -1); - p->call = NULL; - if (p->bearer) - p->bearer->call = NULL; - } else { - const char *cause = pbx_builtin_getvar_helper(ast,"PRI_CAUSE"); - int icause = ast->hangupcause ? ast->hangupcause : -1; - ast_log(LOG_DEBUG, "Not yet hungup... Calling hangup once with icause, and clearing call\n"); - -#ifdef SUPPORT_USERUSER - pri_call_set_useruser(p->call, useruser); -#endif - - p->alreadyhungup = 1; - if (p->bearer) - p->bearer->alreadyhungup = 1; - if (cause) { - if (atoi(cause)) - icause = atoi(cause); - } - pri_hangup(p->pri->pri, p->call, icause); - } - if (res < 0) - ast_log(LOG_WARNING, "pri_disconnect failed\n"); - pri_rel(p->pri); - } else { - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); - res = -1; - } - } else { - if (p->bearer) - ast_log(LOG_DEBUG, "Bearer call is %p, while ours is still %p\n", p->bearer->call, p->call); - p->call = NULL; - res = 0; - } - } -#endif - if (p->sig && (p->sig != SIG_PRI)) - res = dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_ONHOOK); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name); - } - switch (p->sig) { - case SIG_FXOGS: - case SIG_FXOLS: - case SIG_FXOKS: - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par); - if (!res) { -#if 0 - ast_log(LOG_DEBUG, "Hanging up channel %d, offhook = %d\n", p->channel, par.rxisoffhook); -#endif - /* If they're off hook, try playing congestion */ - if ((par.rxisoffhook) && (!(p->radio || (p->oprmode < 0)))) - tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); - else - tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1); - } - break; - case SIG_FXSGS: - case SIG_FXSLS: - case SIG_FXSKS: - /* Make sure we're not made available for at least two seconds assuming - we were actually used for an inbound or outbound call. */ - if (ast->_state != AST_STATE_RESERVED) { - time(&p->guardtime); - p->guardtime += 2; - } - break; - default: - tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1); - } - if (p->cidspill) - free(p->cidspill); - if (p->sig) - dahdi_disable_ec(p); - x = 0; - ast_channel_setoption(ast,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0); - ast_channel_setoption(ast,AST_OPTION_TDD,&x,sizeof(char),0); - p->didtdd = 0; - p->cidspill = NULL; - p->callwaitcas = 0; - p->callwaiting = p->permcallwaiting; - p->hidecallerid = p->permhidecallerid; - p->dialing = 0; - p->rdnis[0] = '\0'; - update_conf(p); - reset_conf(p); - /* Restore data mode */ - if (p->sig == SIG_PRI) { - x = 0; - ast_channel_setoption(ast,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0); - } -#ifdef HAVE_PRI - if (p->bearer) { - ast_log(LOG_DEBUG, "Freeing up bearer channel %d\n", p->bearer->channel); - /* Free up the bearer channel as well, and - don't use its file descriptor anymore */ - update_conf(p->bearer); - reset_conf(p->bearer); - p->bearer->owner = NULL; - p->bearer->realcall = NULL; - p->bearer = NULL; - p->subs[SUB_REAL].dfd = -1; - p->pri = NULL; - } -#endif - if (num_restart_pending == 0) - restart_monitor(); - } - - p->callwaitingrepeat = 0; - p->cidcwexpire = 0; - p->oprmode = 0; - ast->tech_pvt = NULL; - ast_mutex_unlock(&p->lock); - ast_module_unref(ast_module_info->self); - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Hungup '%s'\n", ast->name); - - ast_mutex_lock(&iflock); - - if (p->restartpending) { - num_restart_pending--; - } - - tmp = iflist; - prev = NULL; - if (p->destroy) { - while (tmp) { - if (tmp == p) { - destroy_channel(prev, tmp, 0); - break; - } else { - prev = tmp; - tmp = tmp->next; - } - } - } - ast_mutex_unlock(&iflock); - return 0; -} - -static int dahdi_answer(struct ast_channel *ast) -{ - struct dahdi_pvt *p = ast->tech_pvt; - int res = 0; - int index; - int oldstate = ast->_state; - ast_setstate(ast, AST_STATE_UP); - ast_mutex_lock(&p->lock); - index = dahdi_get_index(ast, p, 0); - if (index < 0) - index = SUB_REAL; - /* nothing to do if a radio channel */ - if ((p->radio || (p->oprmode < 0))) { - ast_mutex_unlock(&p->lock); - return 0; - } - switch (p->sig) { - case SIG_FXSLS: - case SIG_FXSGS: - case SIG_FXSKS: - p->ringt = 0; - /* Fall through */ - case SIG_EM: - case SIG_EM_E1: - case SIG_EMWINK: - case SIG_FEATD: - case SIG_FEATDMF: - case SIG_FEATDMF_TA: - case SIG_E911: - case SIG_FGC_CAMA: - case SIG_FGC_CAMAMF: - case SIG_FEATB: - case SIG_SF: - case SIG_SFWINK: - case SIG_SF_FEATD: - case SIG_SF_FEATDMF: - case SIG_SF_FEATB: - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FXOKS: - /* Pick up the line */ - ast_log(LOG_DEBUG, "Took %s off hook\n", ast->name); - if (p->hanguponpolarityswitch) { - gettimeofday(&p->polaritydelaytv, NULL); - } - res = dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_OFFHOOK); - tone_zone_play_tone(p->subs[index].dfd, -1); - p->dialing = 0; - if ((index == SUB_REAL) && p->subs[SUB_THREEWAY].inthreeway) { - if (oldstate == AST_STATE_RINGING) { - ast_log(LOG_DEBUG, "Finally swapping real and threeway\n"); - tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, -1); - swap_subs(p, SUB_THREEWAY, SUB_REAL); - p->owner = p->subs[SUB_REAL].owner; - } - } - if (p->sig & __DAHDI_SIG_FXS) { - dahdi_enable_ec(p); - dahdi_train_ec(p); - } - break; -#ifdef HAVE_PRI - case SIG_PRI: - /* Send a pri acknowledge */ - if (!pri_grab(p, p->pri)) { - p->proceeding = 1; - res = pri_answer(p->pri->pri, p->call, 0, !p->digital); - pri_rel(p->pri); - } else { - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); - res = -1; - } - break; -#endif - case 0: - ast_mutex_unlock(&p->lock); - return 0; - default: - ast_log(LOG_WARNING, "Don't know how to answer signalling %d (channel %d)\n", p->sig, p->channel); - res = -1; - } - ast_mutex_unlock(&p->lock); - return res; -} - -static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int datalen) -{ - char *cp; - signed char *scp; - int x; - int index; - struct dahdi_pvt *p = chan->tech_pvt, *pp; - struct oprmode *oprmode; - - - /* all supported options require data */ - if (!data || (datalen < 1)) { - errno = EINVAL; - return -1; - } - - switch (option) { - case AST_OPTION_TXGAIN: - scp = (signed char *) data; - index = dahdi_get_index(chan, p, 0); - if (index < 0) { - ast_log(LOG_WARNING, "No index in TXGAIN?\n"); - return -1; - } - if (option_debug) - ast_log(LOG_DEBUG, "Setting actual tx gain on %s to %f\n", chan->name, p->txgain + (float) *scp); - return set_actual_txgain(p->subs[index].dfd, 0, p->txgain + (float) *scp, p->law); - case AST_OPTION_RXGAIN: - scp = (signed char *) data; - index = dahdi_get_index(chan, p, 0); - if (index < 0) { - ast_log(LOG_WARNING, "No index in RXGAIN?\n"); - return -1; - } - if (option_debug) - ast_log(LOG_DEBUG, "Setting actual rx gain on %s to %f\n", chan->name, p->rxgain + (float) *scp); - return set_actual_rxgain(p->subs[index].dfd, 0, p->rxgain + (float) *scp, p->law); - case AST_OPTION_TONE_VERIFY: - if (!p->dsp) - break; - cp = (char *) data; - switch (*cp) { - case 1: - ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: MUTECONF(1) on %s\n",chan->name); - ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MUTECONF | p->dtmfrelax); /* set mute mode if desired */ - break; - case 2: - ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: MUTECONF/MAX(2) on %s\n",chan->name); - ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX | p->dtmfrelax); /* set mute mode if desired */ - break; - default: - ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: OFF(0) on %s\n",chan->name); - ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax); /* set mute mode if desired */ - break; - } - break; - case AST_OPTION_TDD: - /* turn on or off TDD */ - cp = (char *) data; - p->mate = 0; - if (!*cp) { /* turn it off */ - if (option_debug) - ast_log(LOG_DEBUG, "Set option TDD MODE, value: OFF(0) on %s\n",chan->name); - if (p->tdd) - tdd_free(p->tdd); - p->tdd = 0; - break; - } - ast_log(LOG_DEBUG, "Set option TDD MODE, value: %s(%d) on %s\n", - (*cp == 2) ? "MATE" : "ON", (int) *cp, chan->name); - dahdi_disable_ec(p); - /* otherwise, turn it on */ - if (!p->didtdd) { /* if havent done it yet */ - unsigned char mybuf[41000], *buf; - int size, res, fd, len; - struct pollfd fds[1]; - - buf = mybuf; - memset(buf, 0x7f, sizeof(mybuf)); /* set to silence */ - ast_tdd_gen_ecdisa(buf + 16000, 16000); /* put in tone */ - len = 40000; - index = dahdi_get_index(chan, p, 0); - if (index < 0) { - ast_log(LOG_WARNING, "No index in TDD?\n"); - return -1; - } - fd = p->subs[index].dfd; - while (len) { - if (ast_check_hangup(chan)) - return -1; - size = len; - if (size > READ_SIZE) - size = READ_SIZE; - fds[0].fd = fd; - fds[0].events = POLLPRI | POLLOUT; - fds[0].revents = 0; - res = poll(fds, 1, -1); - if (!res) { - ast_log(LOG_DEBUG, "poll (for write) ret. 0 on channel %d\n", p->channel); - continue; - } - /* if got exception */ - if (fds[0].revents & POLLPRI) - return -1; - if (!(fds[0].revents & POLLOUT)) { - ast_log(LOG_DEBUG, "write fd not ready on channel %d\n", p->channel); - continue; - } - res = write(fd, buf, size); - if (res != size) { - if (res == -1) return -1; - ast_log(LOG_DEBUG, "Write returned %d (%s) on channel %d\n", res, strerror(errno), p->channel); - break; - } - len -= size; - buf += size; - } - p->didtdd = 1; /* set to have done it now */ - } - if (*cp == 2) { /* Mate mode */ - if (p->tdd) - tdd_free(p->tdd); - p->tdd = 0; - p->mate = 1; - break; - } - if (!p->tdd) { /* if we dont have one yet */ - p->tdd = tdd_new(); /* allocate one */ - } - break; - case AST_OPTION_RELAXDTMF: /* Relax DTMF decoding (or not) */ - if (!p->dsp) - break; - cp = (char *) data; - ast_log(LOG_DEBUG, "Set option RELAX DTMF, value: %s(%d) on %s\n", - *cp ? "ON" : "OFF", (int) *cp, chan->name); - p->dtmfrelax = 0; - if (*cp) p->dtmfrelax = DSP_DIGITMODE_RELAXDTMF; - ast_dsp_digitmode(p->dsp, DSP_DIGITMODE_DTMF | p->dtmfrelax); - break; - case AST_OPTION_AUDIO_MODE: /* Set AUDIO mode (or not) */ - cp = (char *) data; - if (!*cp) { - ast_log(LOG_DEBUG, "Set option AUDIO MODE, value: OFF(0) on %s\n", chan->name); - x = 0; - dahdi_disable_ec(p); - } else { - ast_log(LOG_DEBUG, "Set option AUDIO MODE, value: ON(1) on %s\n", chan->name); - x = 1; - } - if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &x) == -1) - ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d: %s\n", p->channel, x, strerror(errno)); - break; - case AST_OPTION_OPRMODE: /* Operator services mode */ - oprmode = (struct oprmode *) data; - pp = oprmode->peer->tech_pvt; - p->oprmode = pp->oprmode = 0; - /* setup peers */ - p->oprpeer = pp; - pp->oprpeer = p; - /* setup modes, if any */ - if (oprmode->mode) - { - pp->oprmode = oprmode->mode; - p->oprmode = -oprmode->mode; - } - ast_log(LOG_DEBUG, "Set Operator Services mode, value: %d on %s/%s\n", - oprmode->mode, chan->name,oprmode->peer->name);; - break; - case AST_OPTION_ECHOCAN: - cp = (char *) data; - if (*cp) { - ast_log(LOG_DEBUG, "Enabling echo cancelation on %s\n", chan->name); - dahdi_enable_ec(p); - } else { - ast_log(LOG_DEBUG, "Disabling echo cancelation on %s\n", chan->name); - dahdi_disable_ec(p); - } - break; - } - errno = 0; - - return 0; -} - -static int dahdi_func_read(struct ast_channel *chan, char *function, char *data, char *buf, size_t len) -{ - struct dahdi_pvt *p = chan->tech_pvt; - - if (!strcasecmp(data, "rxgain")) { - ast_mutex_lock(&p->lock); - snprintf(buf, len, "%f", p->rxgain); - ast_mutex_unlock(&p->lock); - } else if (!strcasecmp(data, "txgain")) { - ast_mutex_lock(&p->lock); - snprintf(buf, len, "%f", p->txgain); - ast_mutex_unlock(&p->lock); - } else { - ast_copy_string(buf, "", len); - } - return 0; -} - - -static void dahdi_unlink(struct dahdi_pvt *slave, struct dahdi_pvt *master, int needlock) -{ - /* Unlink a specific slave or all slaves/masters from a given master */ - int x; - int hasslaves; - if (!master) - return; - if (needlock) { - ast_mutex_lock(&master->lock); - if (slave) { - while (ast_mutex_trylock(&slave->lock)) { - DEADLOCK_AVOIDANCE(&master->lock); - } - } - } - hasslaves = 0; - for (x = 0; x < MAX_SLAVES; x++) { - if (master->slaves[x]) { - if (!slave || (master->slaves[x] == slave)) { - /* Take slave out of the conference */ - ast_log(LOG_DEBUG, "Unlinking slave %d from %d\n", master->slaves[x]->channel, master->channel); - conf_del(master, &master->slaves[x]->subs[SUB_REAL], SUB_REAL); - conf_del(master->slaves[x], &master->subs[SUB_REAL], SUB_REAL); - master->slaves[x]->master = NULL; - master->slaves[x] = NULL; - } else - hasslaves = 1; - } - if (!hasslaves) - master->inconference = 0; - } - if (!slave) { - if (master->master) { - /* Take master out of the conference */ - conf_del(master->master, &master->subs[SUB_REAL], SUB_REAL); - conf_del(master, &master->master->subs[SUB_REAL], SUB_REAL); - hasslaves = 0; - for (x = 0; x < MAX_SLAVES; x++) { - if (master->master->slaves[x] == master) - master->master->slaves[x] = NULL; - else if (master->master->slaves[x]) - hasslaves = 1; - } - if (!hasslaves) - master->master->inconference = 0; - } - master->master = NULL; - } - update_conf(master); - if (needlock) { - if (slave) - ast_mutex_unlock(&slave->lock); - ast_mutex_unlock(&master->lock); - } -} - -static void dahdi_link(struct dahdi_pvt *slave, struct dahdi_pvt *master) { - int x; - if (!slave || !master) { - ast_log(LOG_WARNING, "Tried to link to/from NULL??\n"); - return; - } - for (x = 0; x < MAX_SLAVES; x++) { - if (!master->slaves[x]) { - master->slaves[x] = slave; - break; - } - } - if (x >= MAX_SLAVES) { - ast_log(LOG_WARNING, "Replacing slave %d with new slave, %d\n", master->slaves[MAX_SLAVES - 1]->channel, slave->channel); - master->slaves[MAX_SLAVES - 1] = slave; - } - if (slave->master) - ast_log(LOG_WARNING, "Replacing master %d with new master, %d\n", slave->master->channel, master->channel); - slave->master = master; - - ast_log(LOG_DEBUG, "Making %d slave to master %d at %d\n", slave->channel, master->channel, x); -} - -static void disable_dtmf_detect(struct dahdi_pvt *p) -{ -#ifdef DAHDI_TONEDETECT - int val; -#endif - - p->ignoredtmf = 1; - -#ifdef DAHDI_TONEDETECT - val = 0; - ioctl(p->subs[SUB_REAL].dfd, DAHDI_TONEDETECT, &val); -#endif - if (!p->hardwaredtmf && p->dsp) { - p->dsp_features &= ~DSP_FEATURE_DTMF_DETECT; - ast_dsp_set_features(p->dsp, p->dsp_features); - } -} - -static void enable_dtmf_detect(struct dahdi_pvt *p) -{ -#ifdef DAHDI_TONEDETECT - int val; -#endif - - if (p->channel == CHAN_PSEUDO) - return; - - p->ignoredtmf = 0; - -#ifdef DAHDI_TONEDETECT - val = DAHDI_TONEDETECT_ON | DAHDI_TONEDETECT_MUTE; - ioctl(p->subs[SUB_REAL].dfd, DAHDI_TONEDETECT, &val); -#endif - if (!p->hardwaredtmf && p->dsp) { - p->dsp_features |= DSP_FEATURE_DTMF_DETECT; - ast_dsp_set_features(p->dsp, p->dsp_features); - } -} - -static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms) -{ - struct ast_channel *who; - struct dahdi_pvt *p0, *p1, *op0, *op1; - struct dahdi_pvt *master = NULL, *slave = NULL; - struct ast_frame *f; - int inconf = 0; - int nothingok = 1; - int ofd0, ofd1; - int oi0, oi1, i0 = -1, i1 = -1, t0, t1; - int os0 = -1, os1 = -1; - int priority = 0; - struct ast_channel *oc0, *oc1; - enum ast_bridge_result res; - -#ifdef PRI_2BCT - int triedtopribridge = 0; - q931_call *q931c0 = NULL, *q931c1 = NULL; -#endif - - /* For now, don't attempt to native bridge if either channel needs DTMF detection. - There is code below to handle it properly until DTMF is actually seen, - but due to currently unresolved issues it's ignored... - */ - - if (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1)) - return AST_BRIDGE_FAILED_NOWARN; - - ast_mutex_lock(&c0->lock); - while (ast_mutex_trylock(&c1->lock)) { - DEADLOCK_AVOIDANCE(&c0->lock); - } - - p0 = c0->tech_pvt; - p1 = c1->tech_pvt; - /* cant do pseudo-channels here */ - if (!p0 || (!p0->sig) || !p1 || (!p1->sig)) { - ast_mutex_unlock(&c0->lock); - ast_mutex_unlock(&c1->lock); - return AST_BRIDGE_FAILED_NOWARN; - } - - oi0 = dahdi_get_index(c0, p0, 0); - oi1 = dahdi_get_index(c1, p1, 0); - if ((oi0 < 0) || (oi1 < 0)) { - ast_mutex_unlock(&c0->lock); - ast_mutex_unlock(&c1->lock); - return AST_BRIDGE_FAILED; - } - - op0 = p0 = c0->tech_pvt; - op1 = p1 = c1->tech_pvt; - ofd0 = c0->fds[0]; - ofd1 = c1->fds[0]; - oc0 = p0->owner; - oc1 = p1->owner; - - if (ast_mutex_trylock(&p0->lock)) { - /* Don't block, due to potential for deadlock */ - ast_mutex_unlock(&c0->lock); - ast_mutex_unlock(&c1->lock); - ast_log(LOG_NOTICE, "Avoiding deadlock...\n"); - return AST_BRIDGE_RETRY; - } - if (ast_mutex_trylock(&p1->lock)) { - /* Don't block, due to potential for deadlock */ - ast_mutex_unlock(&p0->lock); - ast_mutex_unlock(&c0->lock); - ast_mutex_unlock(&c1->lock); - ast_log(LOG_NOTICE, "Avoiding deadlock...\n"); - return AST_BRIDGE_RETRY; - } - - if ((oi0 == SUB_REAL) && (oi1 == SUB_REAL)) { - if (p0->owner && p1->owner) { - /* If we don't have a call-wait in a 3-way, and we aren't in a 3-way, we can be master */ - if (!p0->subs[SUB_CALLWAIT].inthreeway && !p1->subs[SUB_REAL].inthreeway) { - master = p0; - slave = p1; - inconf = 1; - } else if (!p1->subs[SUB_CALLWAIT].inthreeway && !p0->subs[SUB_REAL].inthreeway) { - master = p1; - slave = p0; - inconf = 1; - } else { - ast_log(LOG_WARNING, "Huh? Both calls are callwaits or 3-ways? That's clever...?\n"); - ast_log(LOG_WARNING, "p0: chan %d/%d/CW%d/3W%d, p1: chan %d/%d/CW%d/3W%d\n", - p0->channel, - oi0, (p0->subs[SUB_CALLWAIT].dfd > -1) ? 1 : 0, - p0->subs[SUB_REAL].inthreeway, p0->channel, - oi0, (p1->subs[SUB_CALLWAIT].dfd > -1) ? 1 : 0, - p1->subs[SUB_REAL].inthreeway); - } - nothingok = 0; - } - } else if ((oi0 == SUB_REAL) && (oi1 == SUB_THREEWAY)) { - if (p1->subs[SUB_THREEWAY].inthreeway) { - master = p1; - slave = p0; - nothingok = 0; - } - } else if ((oi0 == SUB_THREEWAY) && (oi1 == SUB_REAL)) { - if (p0->subs[SUB_THREEWAY].inthreeway) { - master = p0; - slave = p1; - nothingok = 0; - } - } else if ((oi0 == SUB_REAL) && (oi1 == SUB_CALLWAIT)) { - /* We have a real and a call wait. If we're in a three way call, put us in it, otherwise, - don't put us in anything */ - if (p1->subs[SUB_CALLWAIT].inthreeway) { - master = p1; - slave = p0; - nothingok = 0; - } - } else if ((oi0 == SUB_CALLWAIT) && (oi1 == SUB_REAL)) { - /* Same as previous */ - if (p0->subs[SUB_CALLWAIT].inthreeway) { - master = p0; - slave = p1; - nothingok = 0; - } - } - ast_log(LOG_DEBUG, "master: %d, slave: %d, nothingok: %d\n", - master ? master->channel : 0, slave ? slave->channel : 0, nothingok); - if (master && slave) { - /* Stop any tones, or play ringtone as appropriate. If they're bridged - in an active threeway call with a channel that is ringing, we should - indicate ringing. */ - if ((oi1 == SUB_THREEWAY) && - p1->subs[SUB_THREEWAY].inthreeway && - p1->subs[SUB_REAL].owner && - p1->subs[SUB_REAL].inthreeway && - (p1->subs[SUB_REAL].owner->_state == AST_STATE_RINGING)) { - ast_log(LOG_DEBUG, "Playing ringback on %s since %s is in a ringing three-way\n", c0->name, c1->name); - tone_zone_play_tone(p0->subs[oi0].dfd, DAHDI_TONE_RINGTONE); - os1 = p1->subs[SUB_REAL].owner->_state; - } else { - ast_log(LOG_DEBUG, "Stopping tones on %d/%d talking to %d/%d\n", p0->channel, oi0, p1->channel, oi1); - tone_zone_play_tone(p0->subs[oi0].dfd, -1); - } - if ((oi0 == SUB_THREEWAY) && - p0->subs[SUB_THREEWAY].inthreeway && - p0->subs[SUB_REAL].owner && - p0->subs[SUB_REAL].inthreeway && - (p0->subs[SUB_REAL].owner->_state == AST_STATE_RINGING)) { - ast_log(LOG_DEBUG, "Playing ringback on %s since %s is in a ringing three-way\n", c1->name, c0->name); - tone_zone_play_tone(p1->subs[oi1].dfd, DAHDI_TONE_RINGTONE); - os0 = p0->subs[SUB_REAL].owner->_state; - } else { - ast_log(LOG_DEBUG, "Stopping tones on %d/%d talking to %d/%d\n", p1->channel, oi1, p0->channel, oi0); - tone_zone_play_tone(p1->subs[oi0].dfd, -1); - } - if ((oi0 == SUB_REAL) && (oi1 == SUB_REAL)) { - if (!p0->echocanbridged || !p1->echocanbridged) { - /* Disable echo cancellation if appropriate */ - dahdi_disable_ec(p0); - dahdi_disable_ec(p1); - } - } - dahdi_link(slave, master); - master->inconference = inconf; - } else if (!nothingok) - ast_log(LOG_WARNING, "Can't link %d/%s with %d/%s\n", p0->channel, subnames[oi0], p1->channel, subnames[oi1]); - - update_conf(p0); - update_conf(p1); - t0 = p0->subs[SUB_REAL].inthreeway; - t1 = p1->subs[SUB_REAL].inthreeway; - - ast_mutex_unlock(&p0->lock); - ast_mutex_unlock(&p1->lock); - - ast_mutex_unlock(&c0->lock); - ast_mutex_unlock(&c1->lock); - - /* Native bridge failed */ - if ((!master || !slave) && !nothingok) { - dahdi_enable_ec(p0); - dahdi_enable_ec(p1); - return AST_BRIDGE_FAILED; - } - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name); - - if (!(flags & AST_BRIDGE_DTMF_CHANNEL_0) && (oi0 == SUB_REAL)) - disable_dtmf_detect(op0); - - if (!(flags & AST_BRIDGE_DTMF_CHANNEL_1) && (oi1 == SUB_REAL)) - disable_dtmf_detect(op1); - - for (;;) { - struct ast_channel *c0_priority[2] = {c0, c1}; - struct ast_channel *c1_priority[2] = {c1, c0}; - - /* Here's our main loop... Start by locking things, looking for private parts, - and then balking if anything is wrong */ - ast_mutex_lock(&c0->lock); - while (ast_mutex_trylock(&c1->lock)) { - DEADLOCK_AVOIDANCE(&c0->lock); - } - - p0 = c0->tech_pvt; - p1 = c1->tech_pvt; - - if (op0 == p0) - i0 = dahdi_get_index(c0, p0, 1); - if (op1 == p1) - i1 = dahdi_get_index(c1, p1, 1); - ast_mutex_unlock(&c0->lock); - ast_mutex_unlock(&c1->lock); - - if (!timeoutms || - (op0 != p0) || - (op1 != p1) || - (ofd0 != c0->fds[0]) || - (ofd1 != c1->fds[0]) || - (p0->subs[SUB_REAL].owner && (os0 > -1) && (os0 != p0->subs[SUB_REAL].owner->_state)) || - (p1->subs[SUB_REAL].owner && (os1 > -1) && (os1 != p1->subs[SUB_REAL].owner->_state)) || - (oc0 != p0->owner) || - (oc1 != p1->owner) || - (t0 != p0->subs[SUB_REAL].inthreeway) || - (t1 != p1->subs[SUB_REAL].inthreeway) || - (oi0 != i0) || - (oi1 != i1)) { - ast_log(LOG_DEBUG, "Something changed out on %d/%d to %d/%d, returning -3 to restart\n", - op0->channel, oi0, op1->channel, oi1); - res = AST_BRIDGE_RETRY; - goto return_from_bridge; - } - -#ifdef PRI_2BCT - q931c0 = p0->call; - q931c1 = p1->call; - if (p0->transfer && p1->transfer - && q931c0 && q931c1 - && !triedtopribridge) { - pri_channel_bridge(q931c0, q931c1); - triedtopribridge = 1; - } -#endif - - who = ast_waitfor_n(priority ? c0_priority : c1_priority, 2, &timeoutms); - if (!who) { - ast_log(LOG_DEBUG, "Ooh, empty read...\n"); - continue; - } - f = ast_read(who); - if (!f || (f->frametype == AST_FRAME_CONTROL)) { - *fo = f; - *rc = who; - res = AST_BRIDGE_COMPLETE; - goto return_from_bridge; - } - if (f->frametype == AST_FRAME_DTMF) { - if ((who == c0) && p0->pulsedial) { - ast_write(c1, f); - } else if ((who == c1) && p1->pulsedial) { - ast_write(c0, f); - } else { - *fo = f; - *rc = who; - res = AST_BRIDGE_COMPLETE; - goto return_from_bridge; - } - } - ast_frfree(f); - - /* Swap who gets priority */ - priority = !priority; - } - -return_from_bridge: - if (op0 == p0) - dahdi_enable_ec(p0); - - if (op1 == p1) - dahdi_enable_ec(p1); - - if (!(flags & AST_BRIDGE_DTMF_CHANNEL_0) && (oi0 == SUB_REAL)) - enable_dtmf_detect(op0); - - if (!(flags & AST_BRIDGE_DTMF_CHANNEL_1) && (oi1 == SUB_REAL)) - enable_dtmf_detect(op1); - - dahdi_unlink(slave, master, 1); - - return res; -} - -static int dahdi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct dahdi_pvt *p = newchan->tech_pvt; - int x; - ast_mutex_lock(&p->lock); - ast_log(LOG_DEBUG, "New owner for channel %d is %s\n", p->channel, newchan->name); - if (p->owner == oldchan) { - p->owner = newchan; - } - for (x = 0; x < 3; x++) - if (p->subs[x].owner == oldchan) { - if (!x) - dahdi_unlink(NULL, p, 0); - p->subs[x].owner = newchan; - } - if (newchan->_state == AST_STATE_RINGING) - dahdi_indicate(newchan, AST_CONTROL_RINGING, NULL, 0); - update_conf(p); - ast_mutex_unlock(&p->lock); - return 0; -} - -static int dahdi_ring_phone(struct dahdi_pvt *p) -{ - int x; - int res; - /* Make sure our transmit state is on hook */ - x = 0; - x = DAHDI_ONHOOK; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); - do { - x = DAHDI_RING; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); - if (res) { - switch (errno) { - case EBUSY: - case EINTR: - /* Wait just in case */ - usleep(10000); - continue; - case EINPROGRESS: - res = 0; - break; - default: - ast_log(LOG_WARNING, "Couldn't ring the phone: %s\n", strerror(errno)); - res = 0; - } - } - } while (res); - return res; -} - -static void *ss_thread(void *data); - -static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int); - -static int attempt_transfer(struct dahdi_pvt *p) -{ - /* In order to transfer, we need at least one of the channels to - actually be in a call bridge. We can't conference two applications - together (but then, why would we want to?) */ - if (ast_bridged_channel(p->subs[SUB_REAL].owner)) { - /* The three-way person we're about to transfer to could still be in MOH, so - stop if now if appropriate */ - if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) - ast_queue_control(p->subs[SUB_THREEWAY].owner, AST_CONTROL_UNHOLD); - if (p->subs[SUB_REAL].owner->_state == AST_STATE_RINGING) { - ast_indicate(ast_bridged_channel(p->subs[SUB_REAL].owner), AST_CONTROL_RINGING); - } - if (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_RING) { - tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE); - } - if (ast_channel_masquerade(p->subs[SUB_THREEWAY].owner, ast_bridged_channel(p->subs[SUB_REAL].owner))) { - ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", - ast_bridged_channel(p->subs[SUB_REAL].owner)->name, p->subs[SUB_THREEWAY].owner->name); - return -1; - } - /* Orphan the channel after releasing the lock */ - ast_mutex_unlock(&p->subs[SUB_THREEWAY].owner->lock); - unalloc_sub(p, SUB_THREEWAY); - } else if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) { - ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD); - if (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_RINGING) { - ast_indicate(ast_bridged_channel(p->subs[SUB_THREEWAY].owner), AST_CONTROL_RINGING); - } - if (p->subs[SUB_REAL].owner->_state == AST_STATE_RING) { - tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); - } - if (ast_channel_masquerade(p->subs[SUB_REAL].owner, ast_bridged_channel(p->subs[SUB_THREEWAY].owner))) { - ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", - ast_bridged_channel(p->subs[SUB_THREEWAY].owner)->name, p->subs[SUB_REAL].owner->name); - return -1; - } - /* Three-way is now the REAL */ - swap_subs(p, SUB_THREEWAY, SUB_REAL); - ast_mutex_unlock(&p->subs[SUB_REAL].owner->lock); - unalloc_sub(p, SUB_THREEWAY); - /* Tell the caller not to hangup */ - return 1; - } else { - ast_log(LOG_DEBUG, "Neither %s nor %s are in a bridge, nothing to transfer\n", - p->subs[SUB_REAL].owner->name, p->subs[SUB_THREEWAY].owner->name); - p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; - return -1; - } - return 0; -} - -static int check_for_conference(struct dahdi_pvt *p) -{ - struct dahdi_confinfo ci; - /* Fine if we already have a master, etc */ - if (p->master || (p->confno > -1)) - return 0; - memset(&ci, 0, sizeof(ci)); - if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_GETCONF, &ci)) { - ast_log(LOG_WARNING, "Failed to get conference info on channel %d: %s\n", p->channel, strerror(errno)); - return 0; - } - /* If we have no master and don't have a confno, then - if we're in a conference, it's probably a MeetMe room or - some such, so don't let us 3-way out! */ - if ((p->subs[SUB_REAL].curconf.confno != ci.confno) || (p->subs[SUB_REAL].curconf.confmode != ci.confmode)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Avoiding 3-way call when in an external conference\n"); - return 1; - } - return 0; -} - -static int get_alarms(struct dahdi_pvt *p) -{ - int res; - struct dahdi_spaninfo zi; -#if !defined(HAVE_ZAPTEL) || defined(HAVE_ZAPTEL_CHANALARMS) - /* - * The conditional compilation is needed only in asterisk-1.4 for - * backward compatibility with old zaptel drivers that don't have - * a DAHDI_PARAMS.chan_alarms field. - */ - struct dahdi_params params; -#endif - - memset(&zi, 0, sizeof(zi)); - zi.spanno = p->span; - - /* First check for span alarms */ - if((res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SPANSTAT, &zi)) < 0) { - ast_log(LOG_WARNING, "Unable to determine alarm on channel %d: %s\n", p->channel, strerror(errno)); - return 0; - } - if (zi.alarms != DAHDI_ALARM_NONE) - return zi.alarms; -#if !defined(HAVE_ZAPTEL) || defined(HAVE_ZAPTEL_CHANALARMS) - /* No alarms on the span. Check for channel alarms. */ - if ((res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, ¶ms)) >= 0) - return params.chan_alarms; - /* ioctl failed */ - ast_log(LOG_WARNING, "Unable to determine alarm on channel %d\n", p->channel); -#endif - return DAHDI_ALARM_NONE; -} - -static void dahdi_handle_dtmfup(struct ast_channel *ast, int index, struct ast_frame **dest) -{ - struct dahdi_pvt *p = ast->tech_pvt; - struct ast_frame *f = *dest; - - if (option_debug) - ast_log(LOG_DEBUG, "DTMF digit: %c on %s\n", f->subclass, ast->name); - - if (p->confirmanswer) { - if (option_debug) - ast_log(LOG_DEBUG, "Confirm answer on %s!\n", ast->name); - /* Upon receiving a DTMF digit, consider this an answer confirmation instead - of a DTMF digit */ - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_ANSWER; - *dest = &p->subs[index].f; - /* Reset confirmanswer so DTMF's will behave properly for the duration of the call */ - p->confirmanswer = 0; - } else if (p->callwaitcas) { - if ((f->subclass == 'A') || (f->subclass == 'D')) { - if (option_debug) - ast_log(LOG_DEBUG, "Got some DTMF, but it's for the CAS\n"); - if (p->cidspill) - free(p->cidspill); - send_cwcidspill(p); - } - if ((f->subclass != 'm') && (f->subclass != 'u')) - p->callwaitcas = 0; - p->subs[index].f.frametype = AST_FRAME_NULL; - p->subs[index].f.subclass = 0; - *dest = &p->subs[index].f; - } else if (f->subclass == 'f') { - /* Fax tone -- Handle and return NULL */ - if ((p->callprogress & 0x6) && !p->faxhandled) { - p->faxhandled++; - if (strcmp(ast->exten, "fax")) { - const char *target_context = S_OR(ast->macrocontext, ast->context); - - /* We need to unlock 'ast' here because ast_exists_extension has the - * potential to start autoservice on the channel. Such action is prone - * to deadlock. - */ - ast_mutex_unlock(&p->lock); - ast_channel_unlock(ast); - if (ast_exists_extension(ast, target_context, "fax", 1, ast->cid.cid_num)) { - ast_channel_lock(ast); - ast_mutex_lock(&p->lock); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Redirecting %s to fax extension\n", ast->name); - /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ - pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten); - if (ast_async_goto(ast, target_context, "fax", 1)) - ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context); - } else { - ast_channel_lock(ast); - ast_mutex_lock(&p->lock); - ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n"); - } - } else if (option_debug) - ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n"); - } else if (option_debug) - ast_log(LOG_DEBUG, "Fax already handled\n"); - dahdi_confmute(p, 0); - p->subs[index].f.frametype = AST_FRAME_NULL; - p->subs[index].f.subclass = 0; - *dest = &p->subs[index].f; - } else if (f->subclass == 'm') { - /* Confmute request */ - dahdi_confmute(p, 1); - p->subs[index].f.frametype = AST_FRAME_NULL; - p->subs[index].f.subclass = 0; - *dest = &p->subs[index].f; - } else if (f->subclass == 'u') { - /* Unmute */ - dahdi_confmute(p, 0); - p->subs[index].f.frametype = AST_FRAME_NULL; - p->subs[index].f.subclass = 0; - *dest = &p->subs[index].f; - } else - dahdi_confmute(p, 0); -} - -static void handle_alarms(struct dahdi_pvt *p, int alarms) -{ - const char *alarm_str = alarm2str(alarms); - - /* hack alert! Zaptel 1.4 and DAHDI expose FXO battery as an alarm, but this code - * doesn't know what to do with it. Don't confuse users with log messages. */ - if (!strcasecmp(alarm_str, "No Alarm") || !strcasecmp(alarm_str, "Unknown Alarm")) { - p->unknown_alarm = 1; - return; - } else { - p->unknown_alarm = 0; - } - - ast_log(LOG_WARNING, "Detected alarm on channel %d: %s\n", p->channel, alarm_str); - manager_event(EVENT_FLAG_SYSTEM, "Alarm", - "Alarm: %s\r\n" - "Channel: %d\r\n", - alarm_str, p->channel); -} - -static struct ast_frame *dahdi_handle_event(struct ast_channel *ast) -{ - int res, x; - int index, mysig; - char *c; - struct dahdi_pvt *p = ast->tech_pvt; - pthread_t threadid; - pthread_attr_t attr; - struct ast_channel *chan; - struct ast_frame *f; - - index = dahdi_get_index(ast, p, 0); - mysig = p->sig; - if (p->outsigmod > -1) - mysig = p->outsigmod; - p->subs[index].f.frametype = AST_FRAME_NULL; - p->subs[index].f.subclass = 0; - p->subs[index].f.datalen = 0; - p->subs[index].f.samples = 0; - p->subs[index].f.mallocd = 0; - p->subs[index].f.offset = 0; - p->subs[index].f.src = "dahdi_handle_event"; - p->subs[index].f.data = NULL; - f = &p->subs[index].f; - - if (index < 0) - return &p->subs[index].f; - if (p->fake_event) { - res = p->fake_event; - p->fake_event = 0; - } else - res = dahdi_get_event(p->subs[index].dfd); - - if (option_debug) - ast_log(LOG_DEBUG, "Got event %s(%d) on channel %d (index %d)\n", event2str(res), res, p->channel, index); - - if (res & (DAHDI_EVENT_PULSEDIGIT | DAHDI_EVENT_DTMFUP)) { - p->pulsedial = (res & DAHDI_EVENT_PULSEDIGIT) ? 1 : 0; - - ast_log(LOG_DEBUG, "Detected %sdigit '%c'\n", p->pulsedial ? "pulse ": "", res & 0xff); -#ifdef HAVE_PRI - if (!p->proceeding && p->sig == SIG_PRI && p->pri && p->pri->overlapdial) { - /* absorb event */ - } else { -#endif - p->subs[index].f.frametype = AST_FRAME_DTMF_END; - p->subs[index].f.subclass = res & 0xff; -#ifdef HAVE_PRI - } -#endif - dahdi_handle_dtmfup(ast, index, &f); - return f; - } - - if (res & DAHDI_EVENT_DTMFDOWN) { - if (option_debug) - ast_log(LOG_DEBUG, "DTMF Down '%c'\n", res & 0xff); - /* Mute conference */ - dahdi_confmute(p, 1); - p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN; - p->subs[index].f.subclass = res & 0xff; - return &p->subs[index].f; - } - - switch (res) { -#ifdef DAHDI_EVENT_EC_DISABLED - case DAHDI_EVENT_EC_DISABLED: - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d echo canceler disabled due to CED detection\n", p->channel); - p->echocanon = 0; - break; -#endif - case DAHDI_EVENT_BITSCHANGED: - ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig)); - case DAHDI_EVENT_PULSE_START: - /* Stop tone if there's a pulse start and the PBX isn't started */ - if (!ast->pbx) - tone_zone_play_tone(p->subs[index].dfd, -1); - break; - case DAHDI_EVENT_DIALCOMPLETE: - if (p->inalarm) break; - if ((p->radio || (p->oprmode < 0))) break; - if (ioctl(p->subs[index].dfd,DAHDI_DIALING,&x) == -1) { - ast_log(LOG_DEBUG, "DAHDI_DIALING ioctl failed on %s: %s\n",ast->name, strerror(errno)); - return NULL; - } - if (!x) { /* if not still dialing in driver */ - dahdi_enable_ec(p); - if (p->echobreak) { - dahdi_train_ec(p); - ast_copy_string(p->dop.dialstr, p->echorest, sizeof(p->dop.dialstr)); - p->dop.op = DAHDI_DIAL_OP_REPLACE; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); - p->echobreak = 0; - } else { - p->dialing = 0; - if ((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) { - /* if thru with dialing after offhook */ - if (ast->_state == AST_STATE_DIALING_OFFHOOK) { - ast_setstate(ast, AST_STATE_UP); - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_ANSWER; - break; - } else { /* if to state wait for offhook to dial rest */ - /* we now wait for off hook */ - ast_setstate(ast,AST_STATE_DIALING_OFFHOOK); - } - } - if (ast->_state == AST_STATE_DIALING) { - if ((p->callprogress & 1) && CANPROGRESSDETECT(p) && p->dsp && p->outgoing) { - ast_log(LOG_DEBUG, "Done dialing, but waiting for progress detection before doing more...\n"); - } else if (p->confirmanswer || (!p->dialednone && ((mysig == SIG_EM) || (mysig == SIG_EM_E1) || (mysig == SIG_EMWINK) || (mysig == SIG_FEATD) || (mysig == SIG_FEATDMF_TA) || (mysig == SIG_FEATDMF) || (mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF) || (mysig == SIG_FEATB) || (mysig == SIG_SF) || (mysig == SIG_SFWINK) || (mysig == SIG_SF_FEATD) || (mysig == SIG_SF_FEATDMF) || (mysig == SIG_SF_FEATB)))) { - ast_setstate(ast, AST_STATE_RINGING); - } else if (!p->answeronpolarityswitch) { - ast_setstate(ast, AST_STATE_UP); - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_ANSWER; - /* If aops=0 and hops=1, this is necessary */ - p->polarity = POLARITY_REV; - } else { - /* Start clean, so we can catch the change to REV polarity when party answers */ - p->polarity = POLARITY_IDLE; - } - } - } - } - break; - case DAHDI_EVENT_ALARM: -#ifdef HAVE_PRI - if (!p->pri || !p->pri->pri || (pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0)) { - /* T309 is not enabled : hangup calls when alarm occurs */ - if (p->call) { - if (p->pri && p->pri->pri) { - if (!pri_grab(p, p->pri)) { - pri_hangup(p->pri->pri, p->call, -1); - pri_destroycall(p->pri->pri, p->call); - p->call = NULL; - pri_rel(p->pri); - } else - ast_log(LOG_WARNING, "Failed to grab PRI!\n"); - } else - ast_log(LOG_WARNING, "The PRI Call has not been destroyed\n"); - } - if (p->owner) - p->owner->_softhangup |= AST_SOFTHANGUP_DEV; - } - if (p->bearer) - p->bearer->inalarm = 1; - else -#endif - p->inalarm = 1; - res = get_alarms(p); - handle_alarms(p, res); -#ifdef HAVE_LIBPRI - if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) { - /* fall through intentionally */ - } else { - break; - } -#endif - case DAHDI_EVENT_ONHOOK: - if (p->radio) { - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_RADIO_UNKEY; - break; - } - if (p->oprmode < 0) - { - if (p->oprmode != -1) break; - if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS)) - { - /* Make sure it starts ringing */ - dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RINGOFF); - dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RING); - save_conference(p->oprpeer); - tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); - } - break; - } - switch (p->sig) { - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FXOKS: - p->onhooktime = time(NULL); - p->msgstate = -1; - /* Check for some special conditions regarding call waiting */ - if (index == SUB_REAL) { - /* The normal line was hung up */ - if (p->subs[SUB_CALLWAIT].owner) { - /* There's a call waiting call, so ring the phone, but make it unowned in the mean time */ - swap_subs(p, SUB_CALLWAIT, SUB_REAL); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d still has (callwait) call, ringing phone\n", p->channel); - unalloc_sub(p, SUB_CALLWAIT); -#if 0 - p->subs[index].needanswer = 0; - p->subs[index].needringing = 0; -#endif - p->callwaitingrepeat = 0; - p->cidcwexpire = 0; - p->owner = NULL; - /* Don't start streaming audio yet if the incoming call isn't up yet */ - if (p->subs[SUB_REAL].owner->_state != AST_STATE_UP) - p->dialing = 1; - dahdi_ring_phone(p); - } else if (p->subs[SUB_THREEWAY].owner) { - unsigned int mssinceflash; - /* Here we have to retain the lock on both the main channel, the 3-way channel, and - the private structure -- not especially easy or clean */ - while (p->subs[SUB_THREEWAY].owner && ast_mutex_trylock(&p->subs[SUB_THREEWAY].owner->lock)) { - /* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */ - ast_mutex_unlock(&p->lock); - DEADLOCK_AVOIDANCE(&ast->lock); - /* We can grab ast and p in that order, without worry. We should make sure - nothing seriously bad has happened though like some sort of bizarre double - masquerade! */ - ast_mutex_lock(&p->lock); - if (p->owner != ast) { - ast_log(LOG_WARNING, "This isn't good...\n"); - return NULL; - } - } - if (!p->subs[SUB_THREEWAY].owner) { - ast_log(LOG_NOTICE, "Whoa, threeway disappeared kinda randomly.\n"); - return NULL; - } - mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime); - ast_log(LOG_DEBUG, "Last flash was %d ms ago\n", mssinceflash); - if (mssinceflash < MIN_MS_SINCE_FLASH) { - /* It hasn't been long enough since the last flashook. This is probably a bounce on - hanging up. Hangup both channels now */ - if (p->subs[SUB_THREEWAY].owner) - ast_queue_hangup(p->subs[SUB_THREEWAY].owner); - p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; - ast_log(LOG_DEBUG, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel); - ast_mutex_unlock(&p->subs[SUB_THREEWAY].owner->lock); - } else if ((ast->pbx) || (ast->_state == AST_STATE_UP)) { - if (p->transfer) { - /* In any case this isn't a threeway call anymore */ - p->subs[SUB_REAL].inthreeway = 0; - p->subs[SUB_THREEWAY].inthreeway = 0; - /* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */ - if (!p->transfertobusy && ast->_state == AST_STATE_BUSY) { - ast_mutex_unlock(&p->subs[SUB_THREEWAY].owner->lock); - /* Swap subs and dis-own channel */ - swap_subs(p, SUB_THREEWAY, SUB_REAL); - p->owner = NULL; - /* Ring the phone */ - dahdi_ring_phone(p); - } else { - if ((res = attempt_transfer(p)) < 0) { - p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; - if (p->subs[SUB_THREEWAY].owner) - ast_mutex_unlock(&p->subs[SUB_THREEWAY].owner->lock); - } else if (res) { - /* Don't actually hang up at this point */ - if (p->subs[SUB_THREEWAY].owner) - ast_mutex_unlock(&p->subs[SUB_THREEWAY].owner->lock); - break; - } - } - } else { - p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; - if (p->subs[SUB_THREEWAY].owner) - ast_mutex_unlock(&p->subs[SUB_THREEWAY].owner->lock); - } - } else { - ast_mutex_unlock(&p->subs[SUB_THREEWAY].owner->lock); - /* Swap subs and dis-own channel */ - swap_subs(p, SUB_THREEWAY, SUB_REAL); - p->owner = NULL; - /* Ring the phone */ - dahdi_ring_phone(p); - } - } - } else { - ast_log(LOG_WARNING, "Got a hangup and my index is %d?\n", index); - } - /* Fall through */ - default: - dahdi_disable_ec(p); - return NULL; - } - break; - case DAHDI_EVENT_RINGOFFHOOK: - if (p->inalarm) break; - if (p->oprmode < 0) - { - if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS)) - { - /* Make sure it stops ringing */ - dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RINGOFF); - tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].dfd, -1); - restore_conference(p->oprpeer); - } - break; - } - if (p->radio) - { - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_RADIO_KEY; - break; - } - /* for E911, its supposed to wait for offhook then dial - the second half of the dial string */ - if (((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) && (ast->_state == AST_STATE_DIALING_OFFHOOK)) { - c = strchr(p->dialdest, '/'); - if (c) - c++; - else - c = p->dialdest; - if (*c) snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*0%s#", c); - else ast_copy_string(p->dop.dialstr,"M*2#", sizeof(p->dop.dialstr)); - if (strlen(p->dop.dialstr) > 4) { - memset(p->echorest, 'w', sizeof(p->echorest) - 1); - strcpy(p->echorest + (p->echotraining / 401) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2); - p->echorest[sizeof(p->echorest) - 1] = '\0'; - p->echobreak = 1; - p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0'; - } else - p->echobreak = 0; - if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop)) { - int saveerr = errno; - - x = DAHDI_ONHOOK; - ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); - ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(saveerr)); - return NULL; - } - p->dialing = 1; - return &p->subs[index].f; - } - switch (p->sig) { - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FXOKS: - switch (ast->_state) { - case AST_STATE_RINGING: - dahdi_enable_ec(p); - dahdi_train_ec(p); - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_ANSWER; - /* Make sure it stops ringing */ - dahdi_set_hook(p->subs[index].dfd, DAHDI_OFFHOOK); - ast_log(LOG_DEBUG, "channel %d answered\n", p->channel); - if (p->cidspill) { - /* Cancel any running CallerID spill */ - free(p->cidspill); - p->cidspill = NULL; - } - p->dialing = 0; - p->callwaitcas = 0; - if (p->confirmanswer) { - /* Ignore answer if "confirm answer" is enabled */ - p->subs[index].f.frametype = AST_FRAME_NULL; - p->subs[index].f.subclass = 0; - } else if (!ast_strlen_zero(p->dop.dialstr)) { - /* nick@dccinc.com 4/3/03 - fxo should be able to do deferred dialing */ - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); - p->dop.dialstr[0] = '\0'; - return NULL; - } else { - ast_log(LOG_DEBUG, "Sent FXO deferred digit string: %s\n", p->dop.dialstr); - p->subs[index].f.frametype = AST_FRAME_NULL; - p->subs[index].f.subclass = 0; - p->dialing = 1; - } - p->dop.dialstr[0] = '\0'; - ast_setstate(ast, AST_STATE_DIALING); - } else - ast_setstate(ast, AST_STATE_UP); - return &p->subs[index].f; - case AST_STATE_DOWN: - ast_setstate(ast, AST_STATE_RING); - ast->rings = 1; - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_OFFHOOK; - ast_log(LOG_DEBUG, "channel %d picked up\n", p->channel); - return &p->subs[index].f; - case AST_STATE_UP: - /* Make sure it stops ringing */ - dahdi_set_hook(p->subs[index].dfd, DAHDI_OFFHOOK); - /* Okay -- probably call waiting*/ - if (ast_bridged_channel(p->owner)) - ast_queue_control(p->owner, AST_CONTROL_UNHOLD); - p->subs[index].needunhold = 1; - break; - case AST_STATE_RESERVED: - /* Start up dialtone */ - if (has_voicemail(p)) - res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_STUTTER); - else - res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_DIALTONE); - break; - default: - ast_log(LOG_WARNING, "FXO phone off hook in weird state %d??\n", ast->_state); - } - break; - case SIG_FXSLS: - case SIG_FXSGS: - case SIG_FXSKS: - if (ast->_state == AST_STATE_RING) { - p->ringt = p->ringt_base; - } - - /* Fall through */ - case SIG_EM: - case SIG_EM_E1: - case SIG_EMWINK: - case SIG_FEATD: - case SIG_FEATDMF: - case SIG_FEATDMF_TA: - case SIG_E911: - case SIG_FGC_CAMA: - case SIG_FGC_CAMAMF: - case SIG_FEATB: - case SIG_SF: - case SIG_SFWINK: - case SIG_SF_FEATD: - case SIG_SF_FEATDMF: - case SIG_SF_FEATB: - if (ast->_state == AST_STATE_PRERING) - ast_setstate(ast, AST_STATE_RING); - if ((ast->_state == AST_STATE_DOWN) || (ast->_state == AST_STATE_RING)) { - if (option_debug) - ast_log(LOG_DEBUG, "Ring detected\n"); - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_RING; - } else if (p->outgoing && ((ast->_state == AST_STATE_RINGING) || (ast->_state == AST_STATE_DIALING))) { - if (option_debug) - ast_log(LOG_DEBUG, "Line answered\n"); - if (p->confirmanswer) { - p->subs[index].f.frametype = AST_FRAME_NULL; - p->subs[index].f.subclass = 0; - } else { - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_ANSWER; - ast_setstate(ast, AST_STATE_UP); - } - } else if (ast->_state != AST_STATE_RING) - ast_log(LOG_WARNING, "Ring/Off-hook in strange state %d on channel %d\n", ast->_state, p->channel); - break; - default: - ast_log(LOG_WARNING, "Don't know how to handle ring/off hook for signalling %d\n", p->sig); - } - break; -#ifdef DAHDI_EVENT_RINGBEGIN - case DAHDI_EVENT_RINGBEGIN: - switch (p->sig) { - case SIG_FXSLS: - case SIG_FXSGS: - case SIG_FXSKS: - if (ast->_state == AST_STATE_RING) { - p->ringt = p->ringt_base; - } - break; - } - break; -#endif - case DAHDI_EVENT_RINGEROFF: - if (p->inalarm) break; - if ((p->radio || (p->oprmode < 0))) break; - ast->rings++; - if ((ast->rings > p->cidrings) && (p->cidspill)) { - ast_log(LOG_WARNING, "Didn't finish Caller-ID spill. Cancelling.\n"); - free(p->cidspill); - p->cidspill = NULL; - p->callwaitcas = 0; - } - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_RINGING; - break; - case DAHDI_EVENT_RINGERON: - break; - case DAHDI_EVENT_NOALARM: - p->inalarm = 0; -#ifdef HAVE_PRI - /* Extremely unlikely but just in case */ - if (p->bearer) - p->bearer->inalarm = 0; -#endif - if (!p->unknown_alarm) { - ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", p->channel); - manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", - "Channel: %d\r\n", p->channel); - } else { - p->unknown_alarm = 0; - } - break; - case DAHDI_EVENT_WINKFLASH: - if (p->inalarm) break; - if (p->radio) break; - if (p->oprmode < 0) break; - if (p->oprmode > 1) - { - struct dahdi_params par; - - if (ioctl(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par) != -1) - { - if (!par.rxisoffhook) - { - /* Make sure it stops ringing */ - dahdi_set_hook(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_RINGOFF); - dahdi_set_hook(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_RING); - save_conference(p); - tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); - } - } - break; - } - /* Remember last time we got a flash-hook */ - gettimeofday(&p->flashtime, NULL); - switch (mysig) { - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FXOKS: - ast_log(LOG_DEBUG, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n", - index, p->subs[SUB_REAL].dfd, p->subs[SUB_CALLWAIT].dfd, p->subs[SUB_THREEWAY].dfd); - p->callwaitcas = 0; - - if (index != SUB_REAL) { - ast_log(LOG_WARNING, "Got flash hook with index %d on channel %d?!?\n", index, p->channel); - goto winkflashdone; - } - - if (p->subs[SUB_CALLWAIT].owner) { - /* Swap to call-wait */ - swap_subs(p, SUB_REAL, SUB_CALLWAIT); - tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1); - p->owner = p->subs[SUB_REAL].owner; - ast_log(LOG_DEBUG, "Making %s the new owner\n", p->owner->name); - if (p->owner->_state == AST_STATE_RINGING) { - ast_setstate(p->owner, AST_STATE_UP); - p->subs[SUB_REAL].needanswer = 1; - } - p->callwaitingrepeat = 0; - p->cidcwexpire = 0; - /* Start music on hold if appropriate */ - if (!p->subs[SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) { - ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD, - S_OR(p->mohsuggest, NULL), - !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); - } - p->subs[SUB_CALLWAIT].needhold = 1; - if (ast_bridged_channel(p->subs[SUB_REAL].owner)) { - ast_queue_control_data(p->subs[SUB_REAL].owner, AST_CONTROL_HOLD, - S_OR(p->mohsuggest, NULL), - !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); - } - p->subs[SUB_REAL].needunhold = 1; - } else if (!p->subs[SUB_THREEWAY].owner) { - char cid_num[256]; - char cid_name[256]; - - if (!p->threewaycalling) { - /* Just send a flash if no 3-way calling */ - p->subs[SUB_REAL].needflash = 1; - goto winkflashdone; - } else if (!check_for_conference(p)) { - if (p->dahditrcallerid && p->owner) { - if (p->owner->cid.cid_num) - ast_copy_string(cid_num, p->owner->cid.cid_num, sizeof(cid_num)); - if (p->owner->cid.cid_name) - ast_copy_string(cid_name, p->owner->cid.cid_name, sizeof(cid_name)); - } - /* XXX This section needs much more error checking!!! XXX */ - /* Start a 3-way call if feasible */ - if (!((ast->pbx) || - (ast->_state == AST_STATE_UP) || - (ast->_state == AST_STATE_RING))) { - ast_log(LOG_DEBUG, "Flash when call not up or ringing\n"); - goto winkflashdone; - } - if (alloc_sub(p, SUB_THREEWAY)) { - ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\n"); - goto winkflashdone; - } - /* Make new channel */ - chan = dahdi_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0); - if (p->dahditrcallerid) { - if (!p->origcid_num) - p->origcid_num = ast_strdup(p->cid_num); - if (!p->origcid_name) - p->origcid_name = ast_strdup(p->cid_name); - ast_copy_string(p->cid_num, cid_num, sizeof(p->cid_num)); - ast_copy_string(p->cid_name, cid_name, sizeof(p->cid_name)); - } - /* Swap things around between the three-way and real call */ - swap_subs(p, SUB_THREEWAY, SUB_REAL); - /* Disable echo canceller for better dialing */ - dahdi_disable_ec(p); - res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_DIALRECALL); - if (res) - ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel); - p->owner = chan; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (!chan) { - ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", p->channel); - } else if (ast_pthread_create(&threadid, &attr, ss_thread, chan)) { - ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel); - res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); - dahdi_enable_ec(p); - ast_hangup(chan); - } else { - struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner); - int way3bridge = 0, cdr3way = 0; - - if (!other) { - other = ast_bridged_channel(p->subs[SUB_REAL].owner); - } else - way3bridge = 1; - - if (p->subs[SUB_THREEWAY].owner->cdr) - cdr3way = 1; - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Started three way call on channel %d\n", p->channel); - /* Start music on hold if appropriate */ - if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) { - ast_queue_control_data(p->subs[SUB_THREEWAY].owner, AST_CONTROL_HOLD, - S_OR(p->mohsuggest, NULL), - !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); - } - p->subs[SUB_THREEWAY].needhold = 1; - } - pthread_attr_destroy(&attr); - } - } else { - /* Already have a 3 way call */ - if (p->subs[SUB_THREEWAY].inthreeway) { - /* Call is already up, drop the last person */ - if (option_debug) - ast_log(LOG_DEBUG, "Got flash with three way call up, dropping last call on %d\n", p->channel); - /* If the primary call isn't answered yet, use it */ - if ((p->subs[SUB_REAL].owner->_state != AST_STATE_UP) && (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_UP)) { - /* Swap back -- we're dropping the real 3-way that isn't finished yet*/ - swap_subs(p, SUB_THREEWAY, SUB_REAL); - p->owner = p->subs[SUB_REAL].owner; - } - /* Drop the last call and stop the conference */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Dropping three-way call on %s\n", p->subs[SUB_THREEWAY].owner->name); - p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; - p->subs[SUB_REAL].inthreeway = 0; - p->subs[SUB_THREEWAY].inthreeway = 0; - } else { - /* Lets see what we're up to */ - if (((ast->pbx) || (ast->_state == AST_STATE_UP)) && - (p->transfertobusy || (ast->_state != AST_STATE_BUSY))) { - int otherindex = SUB_THREEWAY; - struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner); - int way3bridge = 0, cdr3way = 0; - - if (!other) { - other = ast_bridged_channel(p->subs[SUB_REAL].owner); - } else - way3bridge = 1; - - if (p->subs[SUB_THREEWAY].owner->cdr) - cdr3way = 1; - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Building conference on call on %s and %s\n", p->subs[SUB_THREEWAY].owner->name, p->subs[SUB_REAL].owner->name); - /* Put them in the threeway, and flip */ - p->subs[SUB_THREEWAY].inthreeway = 1; - p->subs[SUB_REAL].inthreeway = 1; - if (ast->_state == AST_STATE_UP) { - swap_subs(p, SUB_THREEWAY, SUB_REAL); - otherindex = SUB_REAL; - } - if (p->subs[otherindex].owner && ast_bridged_channel(p->subs[otherindex].owner)) - ast_queue_control(p->subs[otherindex].owner, AST_CONTROL_UNHOLD); - p->subs[otherindex].needunhold = 1; - p->owner = p->subs[SUB_REAL].owner; - if (ast->_state == AST_STATE_RINGING) { - ast_log(LOG_DEBUG, "Enabling ringtone on real and threeway\n"); - res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); - res = tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE); - } - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Dumping incomplete call on on %s\n", p->subs[SUB_THREEWAY].owner->name); - swap_subs(p, SUB_THREEWAY, SUB_REAL); - p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV; - p->owner = p->subs[SUB_REAL].owner; - if (p->subs[SUB_REAL].owner && ast_bridged_channel(p->subs[SUB_REAL].owner)) - ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD); - p->subs[SUB_REAL].needunhold = 1; - dahdi_enable_ec(p); - } - - } - } - winkflashdone: - update_conf(p); - break; - case SIG_EM: - case SIG_EM_E1: - case SIG_EMWINK: - case SIG_FEATD: - case SIG_SF: - case SIG_SFWINK: - case SIG_SF_FEATD: - case SIG_FXSLS: - case SIG_FXSGS: - if (p->dialing) - ast_log(LOG_DEBUG, "Ignoring wink on channel %d\n", p->channel); - else - ast_log(LOG_DEBUG, "Got wink in weird state %d on channel %d\n", ast->_state, p->channel); - break; - case SIG_FEATDMF_TA: - switch (p->whichwink) { - case 0: - ast_log(LOG_DEBUG, "ANI2 set to '%d' and ANI is '%s'\n", p->owner->cid.cid_ani2, p->owner->cid.cid_ani); - snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%d%s#", p->owner->cid.cid_ani2, p->owner->cid.cid_ani); - break; - case 1: - ast_copy_string(p->dop.dialstr, p->finaldial, sizeof(p->dop.dialstr)); - break; - case 2: - ast_log(LOG_WARNING, "Received unexpected wink on channel of type SIG_FEATDMF_TA\n"); - return NULL; - } - p->whichwink++; - /* Fall through */ - case SIG_FEATDMF: - case SIG_E911: - case SIG_FGC_CAMAMF: - case SIG_FGC_CAMA: - case SIG_FEATB: - case SIG_SF_FEATDMF: - case SIG_SF_FEATB: - /* FGD MF *Must* wait for wink */ - if (!ast_strlen_zero(p->dop.dialstr)) { - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); - p->dop.dialstr[0] = '\0'; - return NULL; - } else - ast_log(LOG_DEBUG, "Sent deferred digit string: %s\n", p->dop.dialstr); - } - p->dop.dialstr[0] = '\0'; - break; - default: - ast_log(LOG_WARNING, "Don't know how to handle ring/off hoook for signalling %d\n", p->sig); - } - break; - case DAHDI_EVENT_HOOKCOMPLETE: - if (p->inalarm) break; - if ((p->radio || (p->oprmode < 0))) break; - switch (mysig) { - case SIG_FXSLS: /* only interesting for FXS */ - case SIG_FXSGS: - case SIG_FXSKS: - case SIG_EM: - case SIG_EM_E1: - case SIG_EMWINK: - case SIG_FEATD: - case SIG_SF: - case SIG_SFWINK: - case SIG_SF_FEATD: - if (!ast_strlen_zero(p->dop.dialstr)) { - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno)); - p->dop.dialstr[0] = '\0'; - return NULL; - } else - ast_log(LOG_DEBUG, "Sent deferred digit string: %s\n", p->dop.dialstr); - } - p->dop.dialstr[0] = '\0'; - p->dop.op = DAHDI_DIAL_OP_REPLACE; - break; - case SIG_FEATDMF: - case SIG_FEATDMF_TA: - case SIG_E911: - case SIG_FGC_CAMA: - case SIG_FGC_CAMAMF: - case SIG_FEATB: - case SIG_SF_FEATDMF: - case SIG_SF_FEATB: - ast_log(LOG_DEBUG, "Got hook complete in MF FGD, waiting for wink now on channel %d\n",p->channel); - break; - default: - break; - } - break; - case DAHDI_EVENT_POLARITY: - /* - * If we get a Polarity Switch event, check to see - * if we should change the polarity state and - * mark the channel as UP or if this is an indication - * of remote end disconnect. - */ - if (p->polarity == POLARITY_IDLE) { - p->polarity = POLARITY_REV; - if (p->answeronpolarityswitch && - ((ast->_state == AST_STATE_DIALING) || - (ast->_state == AST_STATE_RINGING))) { - ast_log(LOG_DEBUG, "Answering on polarity switch!\n"); - ast_setstate(p->owner, AST_STATE_UP); - if (p->hanguponpolarityswitch) { - gettimeofday(&p->polaritydelaytv, NULL); - } - } else - ast_log(LOG_DEBUG, "Ignore switch to REVERSED Polarity on channel %d, state %d\n", p->channel, ast->_state); - } - /* Removed else statement from here as it was preventing hangups from ever happening*/ - /* Added AST_STATE_RING in if statement below to deal with calling party hangups that take place when ringing */ - if (p->hanguponpolarityswitch && - (p->polarityonanswerdelay > 0) && - (p->polarity == POLARITY_REV) && - ((ast->_state == AST_STATE_UP) || (ast->_state == AST_STATE_RING)) ) { - /* Added log_debug information below to provide a better indication of what is going on */ - ast_log(LOG_DEBUG, "Polarity Reversal event occured - DEBUG 1: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) ); - - if (ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) > p->polarityonanswerdelay) { - ast_log(LOG_DEBUG, "Polarity Reversal detected and now Hanging up on channel %d\n", p->channel); - ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT); - p->polarity = POLARITY_IDLE; - } else { - ast_log(LOG_DEBUG, "Polarity Reversal detected but NOT hanging up (too close to answer event) on channel %d, state %d\n", p->channel, ast->_state); - } - } else { - p->polarity = POLARITY_IDLE; - ast_log(LOG_DEBUG, "Ignoring Polarity switch to IDLE on channel %d, state %d\n", p->channel, ast->_state); - } - /* Added more log_debug information below to provide a better indication of what is going on */ - ast_log(LOG_DEBUG, "Polarity Reversal event occured - DEBUG 2: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) ); - break; - default: - ast_log(LOG_DEBUG, "Dunno what to do with event %d on channel %d\n", res, p->channel); - } - return &p->subs[index].f; -} - -static struct ast_frame *__dahdi_exception(struct ast_channel *ast) -{ - struct dahdi_pvt *p = ast->tech_pvt; - int res; - int usedindex=-1; - int index; - struct ast_frame *f; - - - index = dahdi_get_index(ast, p, 1); - - p->subs[index].f.frametype = AST_FRAME_NULL; - p->subs[index].f.datalen = 0; - p->subs[index].f.samples = 0; - p->subs[index].f.mallocd = 0; - p->subs[index].f.offset = 0; - p->subs[index].f.subclass = 0; - p->subs[index].f.delivery = ast_tv(0,0); - p->subs[index].f.src = "dahdi_exception"; - p->subs[index].f.data = NULL; - - - if ((!p->owner) && (!(p->radio || (p->oprmode < 0)))) { - /* If nobody owns us, absorb the event appropriately, otherwise - we loop indefinitely. This occurs when, during call waiting, the - other end hangs up our channel so that it no longer exists, but we - have neither FLASH'd nor ONHOOK'd to signify our desire to - change to the other channel. */ - if (p->fake_event) { - res = p->fake_event; - p->fake_event = 0; - } else - res = dahdi_get_event(p->subs[SUB_REAL].dfd); - /* Switch to real if there is one and this isn't something really silly... */ - if ((res != DAHDI_EVENT_RINGEROFF) && (res != DAHDI_EVENT_RINGERON) && - (res != DAHDI_EVENT_HOOKCOMPLETE)) { - ast_log(LOG_DEBUG, "Restoring owner of channel %d on event %d\n", p->channel, res); - p->owner = p->subs[SUB_REAL].owner; - if (p->owner && ast_bridged_channel(p->owner)) - ast_queue_control(p->owner, AST_CONTROL_UNHOLD); - p->subs[SUB_REAL].needunhold = 1; - } - switch (res) { - case DAHDI_EVENT_ONHOOK: - dahdi_disable_ec(p); - if (p->owner) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %s still has call, ringing phone\n", p->owner->name); - dahdi_ring_phone(p); - p->callwaitingrepeat = 0; - p->cidcwexpire = 0; - } else - ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n"); - update_conf(p); - break; - case DAHDI_EVENT_RINGOFFHOOK: - dahdi_enable_ec(p); - dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_OFFHOOK); - if (p->owner && (p->owner->_state == AST_STATE_RINGING)) { - p->subs[SUB_REAL].needanswer = 1; - p->dialing = 0; - } - break; - case DAHDI_EVENT_HOOKCOMPLETE: - case DAHDI_EVENT_RINGERON: - case DAHDI_EVENT_RINGEROFF: - /* Do nothing */ - break; - case DAHDI_EVENT_WINKFLASH: - gettimeofday(&p->flashtime, NULL); - if (p->owner) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d flashed to other channel %s\n", p->channel, p->owner->name); - if (p->owner->_state != AST_STATE_UP) { - /* Answer if necessary */ - usedindex = dahdi_get_index(p->owner, p, 0); - if (usedindex > -1) { - p->subs[usedindex].needanswer = 1; - } - ast_setstate(p->owner, AST_STATE_UP); - } - p->callwaitingrepeat = 0; - p->cidcwexpire = 0; - if (ast_bridged_channel(p->owner)) - ast_queue_control(p->owner, AST_CONTROL_UNHOLD); - p->subs[SUB_REAL].needunhold = 1; - } else - ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n"); - update_conf(p); - break; - default: - ast_log(LOG_WARNING, "Don't know how to absorb event %s\n", event2str(res)); - } - f = &p->subs[index].f; - return f; - } - if (!(p->radio || (p->oprmode < 0)) && option_debug) - ast_log(LOG_DEBUG, "Exception on %d, channel %d\n", ast->fds[0],p->channel); - /* If it's not us, return NULL immediately */ - if (ast != p->owner) { - ast_log(LOG_WARNING, "We're %s, not %s\n", ast->name, p->owner->name); - f = &p->subs[index].f; - return f; - } - f = dahdi_handle_event(ast); - return f; -} - -static struct ast_frame *dahdi_exception(struct ast_channel *ast) -{ - struct dahdi_pvt *p = ast->tech_pvt; - struct ast_frame *f; - ast_mutex_lock(&p->lock); - f = __dahdi_exception(ast); - ast_mutex_unlock(&p->lock); - return f; -} - -static struct ast_frame *dahdi_read(struct ast_channel *ast) -{ - struct dahdi_pvt *p = ast->tech_pvt; - int res; - int index; - void *readbuf; - struct ast_frame *f; - - while (ast_mutex_trylock(&p->lock)) { - DEADLOCK_AVOIDANCE(&ast->lock); - } - - index = dahdi_get_index(ast, p, 0); - - /* Hang up if we don't really exist */ - if (index < 0) { - ast_log(LOG_WARNING, "We dont exist?\n"); - ast_mutex_unlock(&p->lock); - return NULL; - } - - if ((p->radio || (p->oprmode < 0)) && p->inalarm) return NULL; - - p->subs[index].f.frametype = AST_FRAME_NULL; - p->subs[index].f.datalen = 0; - p->subs[index].f.samples = 0; - p->subs[index].f.mallocd = 0; - p->subs[index].f.offset = 0; - p->subs[index].f.subclass = 0; - p->subs[index].f.delivery = ast_tv(0,0); - p->subs[index].f.src = "dahdi_read"; - p->subs[index].f.data = NULL; - - /* make sure it sends initial key state as first frame */ - if ((p->radio || (p->oprmode < 0)) && (!p->firstradio)) - { - struct dahdi_params ps; - - ps.channo = p->channel; - if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &ps) < 0) { - ast_mutex_unlock(&p->lock); - return NULL; - } - p->firstradio = 1; - p->subs[index].f.frametype = AST_FRAME_CONTROL; - if (ps.rxisoffhook) - { - p->subs[index].f.subclass = AST_CONTROL_RADIO_KEY; - } - else - { - p->subs[index].f.subclass = AST_CONTROL_RADIO_UNKEY; - } - ast_mutex_unlock(&p->lock); - return &p->subs[index].f; - } - if (p->ringt == 1) { - ast_mutex_unlock(&p->lock); - return NULL; - } - else if (p->ringt > 0) - p->ringt--; - - if (p->subs[index].needringing) { - /* Send ringing frame if requested */ - p->subs[index].needringing = 0; - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_RINGING; - ast_setstate(ast, AST_STATE_RINGING); - ast_mutex_unlock(&p->lock); - return &p->subs[index].f; - } - - if (p->subs[index].needbusy) { - /* Send busy frame if requested */ - p->subs[index].needbusy = 0; - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_BUSY; - ast_mutex_unlock(&p->lock); - return &p->subs[index].f; - } - - if (p->subs[index].needcongestion) { - /* Send congestion frame if requested */ - p->subs[index].needcongestion = 0; - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_CONGESTION; - ast_mutex_unlock(&p->lock); - return &p->subs[index].f; - } - - if (p->subs[index].needcallerid) { - ast_set_callerid(ast, S_OR(p->lastcid_num, NULL), - S_OR(p->lastcid_name, NULL), - S_OR(p->lastcid_num, NULL) - ); - p->subs[index].needcallerid = 0; - } - - if (p->subs[index].needanswer) { - /* Send answer frame if requested */ - p->subs[index].needanswer = 0; - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_ANSWER; - ast_mutex_unlock(&p->lock); - return &p->subs[index].f; - } - - if (p->subs[index].needflash) { - /* Send answer frame if requested */ - p->subs[index].needflash = 0; - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_FLASH; - ast_mutex_unlock(&p->lock); - return &p->subs[index].f; - } - - if (p->subs[index].needhold) { - /* Send answer frame if requested */ - p->subs[index].needhold = 0; - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_HOLD; - ast_mutex_unlock(&p->lock); - ast_log(LOG_DEBUG, "Sending hold on '%s'\n", ast->name); - return &p->subs[index].f; - } - - if (p->subs[index].needunhold) { - /* Send answer frame if requested */ - p->subs[index].needunhold = 0; - p->subs[index].f.frametype = AST_FRAME_CONTROL; - p->subs[index].f.subclass = AST_CONTROL_UNHOLD; - ast_mutex_unlock(&p->lock); - ast_log(LOG_DEBUG, "Sending unhold on '%s'\n", ast->name); - return &p->subs[index].f; - } - - if (ast->rawreadformat == AST_FORMAT_SLINEAR) { - if (!p->subs[index].linear) { - p->subs[index].linear = 1; - res = dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear); - if (res) - ast_log(LOG_WARNING, "Unable to set channel %d (index %d) to linear mode.\n", p->channel, index); - } - } else if ((ast->rawreadformat == AST_FORMAT_ULAW) || - (ast->rawreadformat == AST_FORMAT_ALAW)) { - if (p->subs[index].linear) { - p->subs[index].linear = 0; - res = dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear); - if (res) - ast_log(LOG_WARNING, "Unable to set channel %d (index %d) to companded mode.\n", p->channel, index); - } - } else { - ast_log(LOG_WARNING, "Don't know how to read frames in format %s\n", ast_getformatname(ast->rawreadformat)); - ast_mutex_unlock(&p->lock); - return NULL; - } - readbuf = ((unsigned char *)p->subs[index].buffer) + AST_FRIENDLY_OFFSET; - CHECK_BLOCKING(ast); - res = read(p->subs[index].dfd, readbuf, p->subs[index].linear ? READ_SIZE * 2 : READ_SIZE); - ast_clear_flag(ast, AST_FLAG_BLOCKING); - /* Check for hangup */ - if (res < 0) { - f = NULL; - if (res == -1) { - if (errno == EAGAIN) { - /* Return "NULL" frame if there is nobody there */ - ast_mutex_unlock(&p->lock); - return &p->subs[index].f; - } else if (errno == ELAST) { - f = __dahdi_exception(ast); - } else - ast_log(LOG_WARNING, "dahdi_rec: %s\n", strerror(errno)); - } - ast_mutex_unlock(&p->lock); - return f; - } - if (res != (p->subs[index].linear ? READ_SIZE * 2 : READ_SIZE)) { - ast_log(LOG_DEBUG, "Short read (%d/%d), must be an event...\n", res, p->subs[index].linear ? READ_SIZE * 2 : READ_SIZE); - f = __dahdi_exception(ast); - ast_mutex_unlock(&p->lock); - return f; - } - if (p->tdd) { /* if in TDD mode, see if we receive that */ - int c; - - c = tdd_feed(p->tdd,readbuf,READ_SIZE); - if (c < 0) { - ast_log(LOG_DEBUG,"tdd_feed failed\n"); - ast_mutex_unlock(&p->lock); - return NULL; - } - if (c) { /* if a char to return */ - p->subs[index].f.subclass = 0; - p->subs[index].f.frametype = AST_FRAME_TEXT; - p->subs[index].f.mallocd = 0; - p->subs[index].f.offset = AST_FRIENDLY_OFFSET; - p->subs[index].f.data = p->subs[index].buffer + AST_FRIENDLY_OFFSET; - p->subs[index].f.datalen = 1; - *((char *) p->subs[index].f.data) = c; - ast_mutex_unlock(&p->lock); - return &p->subs[index].f; - } - } - /* Ensure the CW timer decrements only on a single subchannel */ - if (p->callwaitingrepeat && dahdi_get_index(ast, p, 1) == SUB_REAL) { - p->callwaitingrepeat--; - } - if (p->cidcwexpire) - p->cidcwexpire--; - /* Repeat callwaiting */ - if (p->callwaitingrepeat == 1) { - p->callwaitrings++; - dahdi_callwait(ast); - } - /* Expire CID/CW */ - if (p->cidcwexpire == 1) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "CPE does not support Call Waiting Caller*ID.\n"); - restore_conference(p); - } - if (p->subs[index].linear) { - p->subs[index].f.datalen = READ_SIZE * 2; - } else - p->subs[index].f.datalen = READ_SIZE; - - /* Handle CallerID Transmission */ - if ((p->owner == ast) && p->cidspill &&((ast->_state == AST_STATE_UP) || (ast->rings == p->cidrings))) { - send_callerid(p); - } - - p->subs[index].f.frametype = AST_FRAME_VOICE; - p->subs[index].f.subclass = ast->rawreadformat; - p->subs[index].f.samples = READ_SIZE; - p->subs[index].f.mallocd = 0; - p->subs[index].f.offset = AST_FRIENDLY_OFFSET; - p->subs[index].f.data = p->subs[index].buffer + AST_FRIENDLY_OFFSET / sizeof(p->subs[index].buffer[0]); -#if 0 - ast_log(LOG_DEBUG, "Read %d of voice on %s\n", p->subs[index].f.datalen, ast->name); -#endif - if (p->dialing || /* Transmitting something */ - (index && (ast->_state != AST_STATE_UP)) || /* Three-way or callwait that isn't up */ - ((index == SUB_CALLWAIT) && !p->subs[SUB_CALLWAIT].inthreeway) /* Inactive and non-confed call-wait */ - ) { - /* Whoops, we're still dialing, or in a state where we shouldn't transmit.... - don't send anything */ - p->subs[index].f.frametype = AST_FRAME_NULL; - p->subs[index].f.subclass = 0; - p->subs[index].f.samples = 0; - p->subs[index].f.mallocd = 0; - p->subs[index].f.offset = 0; - p->subs[index].f.data = NULL; - p->subs[index].f.datalen= 0; - } - if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress) && !index) { - /* Perform busy detection. etc on the dahdi line */ - f = ast_dsp_process(ast, p->dsp, &p->subs[index].f); - if (f) { - if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_BUSY)) { - if ((ast->_state == AST_STATE_UP) && !p->outgoing) { - /* Treat this as a "hangup" instead of a "busy" on the assumption that - a busy */ - f = NULL; - } - } else if (f->frametype == AST_FRAME_DTMF) { -#ifdef HAVE_PRI - if (!p->proceeding && p->sig==SIG_PRI && p->pri && p->pri->overlapdial) { - /* Don't accept in-band DTMF when in overlap dial mode */ - f->frametype = AST_FRAME_NULL; - f->subclass = 0; - } -#endif - /* DSP clears us of being pulse */ - p->pulsedial = 0; - } - } - } else - f = &p->subs[index].f; - - if (f && (f->frametype == AST_FRAME_DTMF)) - dahdi_handle_dtmfup(ast, index, &f); - - /* If we have a fake_event, trigger exception to handle it */ - if (p->fake_event) - ast_set_flag(ast, AST_FLAG_EXCEPTION); - - ast_mutex_unlock(&p->lock); - return f; -} - -static int my_dahdi_write(struct dahdi_pvt *p, unsigned char *buf, int len, int index, int linear) -{ - int sent=0; - int size; - int res; - int fd; - fd = p->subs[index].dfd; - while (len) { - size = len; - if (size > (linear ? READ_SIZE * 2 : READ_SIZE)) - size = (linear ? READ_SIZE * 2 : READ_SIZE); - res = write(fd, buf, size); - if (res != size) { - if (option_debug) - ast_log(LOG_DEBUG, "Write returned %d (%s) on channel %d\n", res, strerror(errno), p->channel); - return sent; - } - len -= size; - buf += size; - } - return sent; -} - -static int dahdi_write(struct ast_channel *ast, struct ast_frame *frame) -{ - struct dahdi_pvt *p = ast->tech_pvt; - int res; - int index; - index = dahdi_get_index(ast, p, 0); - if (index < 0) { - ast_log(LOG_WARNING, "%s doesn't really exist?\n", ast->name); - return -1; - } - -#if 0 -#ifdef HAVE_PRI - ast_mutex_lock(&p->lock); - if (!p->proceeding && p->sig==SIG_PRI && p->pri && !p->outgoing) { - if (p->pri->pri) { - if (!pri_grab(p, p->pri)) { - pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), !p->digital); - pri_rel(p->pri); - } else - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); - } - p->proceeding=1; - } - ast_mutex_unlock(&p->lock); -#endif -#endif - /* Write a frame of (presumably voice) data */ - if (frame->frametype != AST_FRAME_VOICE) { - if (frame->frametype != AST_FRAME_IMAGE) - ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype); - return 0; - } - if ((frame->subclass != AST_FORMAT_SLINEAR) && - (frame->subclass != AST_FORMAT_ULAW) && - (frame->subclass != AST_FORMAT_ALAW)) { - ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass); - return -1; - } - if (p->dialing) { - if (option_debug) - ast_log(LOG_DEBUG, "Dropping frame since I'm still dialing on %s...\n",ast->name); - return 0; - } - if (!p->owner) { - if (option_debug) - ast_log(LOG_DEBUG, "Dropping frame since there is no active owner on %s...\n",ast->name); - return 0; - } - if (p->cidspill) { - if (option_debug) - ast_log(LOG_DEBUG, "Dropping frame since I've still got a callerid spill\n"); - return 0; - } - /* Return if it's not valid data */ - if (!frame->data || !frame->datalen) - return 0; - - if (frame->subclass == AST_FORMAT_SLINEAR) { - if (!p->subs[index].linear) { - p->subs[index].linear = 1; - res = dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear); - if (res) - ast_log(LOG_WARNING, "Unable to set linear mode on channel %d\n", p->channel); - } - res = my_dahdi_write(p, (unsigned char *)frame->data, frame->datalen, index, 1); - } else { - /* x-law already */ - if (p->subs[index].linear) { - p->subs[index].linear = 0; - res = dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear); - if (res) - ast_log(LOG_WARNING, "Unable to set companded mode on channel %d\n", p->channel); - } - res = my_dahdi_write(p, (unsigned char *)frame->data, frame->datalen, index, 0); - } - if (res < 0) { - ast_log(LOG_WARNING, "write failed: %s\n", strerror(errno)); - return -1; - } - return 0; -} - -static int dahdi_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen) -{ - struct dahdi_pvt *p = chan->tech_pvt; - int res=-1; - int index; - int func = DAHDI_FLASH; - ast_mutex_lock(&p->lock); - index = dahdi_get_index(chan, p, 0); - if (option_debug) - ast_log(LOG_DEBUG, "Requested indication %d on channel %s\n", condition, chan->name); - if (index == SUB_REAL) { - switch (condition) { - case AST_CONTROL_BUSY: -#ifdef HAVE_PRI - if (p->priindication_oob && p->sig == SIG_PRI) { - chan->hangupcause = AST_CAUSE_USER_BUSY; - chan->_softhangup |= AST_SOFTHANGUP_DEV; - res = 0; - } else if (!p->progress && p->sig==SIG_PRI && p->pri && !p->outgoing) { - if (p->pri->pri) { - if (!pri_grab(p, p->pri)) { - pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), 1); - pri_rel(p->pri); - } - else - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); - } - p->progress = 1; - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_BUSY); - } else -#endif - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_BUSY); - break; - case AST_CONTROL_RINGING: -#ifdef HAVE_PRI - if ((!p->alerting) && p->sig==SIG_PRI && p->pri && !p->outgoing && (chan->_state != AST_STATE_UP)) { - if (p->pri->pri) { - if (!pri_grab(p, p->pri)) { - pri_acknowledge(p->pri->pri,p->call, PVT_TO_CHANNEL(p), !p->digital); - pri_rel(p->pri); - } - else - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); - } - p->alerting = 1; - } -#endif - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_RINGTONE); - if (chan->_state != AST_STATE_UP) { - if ((chan->_state != AST_STATE_RING) || - ((p->sig != SIG_FXSKS) && - (p->sig != SIG_FXSLS) && - (p->sig != SIG_FXSGS))) - ast_setstate(chan, AST_STATE_RINGING); - } - break; - case AST_CONTROL_PROCEEDING: - ast_log(LOG_DEBUG,"Received AST_CONTROL_PROCEEDING on %s\n",chan->name); -#ifdef HAVE_PRI - if (!p->proceeding && p->sig==SIG_PRI && p->pri && !p->outgoing) { - if (p->pri->pri) { - if (!pri_grab(p, p->pri)) { - pri_proceeding(p->pri->pri,p->call, PVT_TO_CHANNEL(p), !p->digital); - pri_rel(p->pri); - } - else - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); - } - p->proceeding = 1; - } -#endif - /* don't continue in ast_indicate */ - res = 0; - break; - case AST_CONTROL_PROGRESS: - ast_log(LOG_DEBUG,"Received AST_CONTROL_PROGRESS on %s\n",chan->name); -#ifdef HAVE_PRI - p->digital = 0; /* Digital-only calls isn't allows any inband progress messages */ - if (!p->progress && p->sig==SIG_PRI && p->pri && !p->outgoing) { - if (p->pri->pri) { - if (!pri_grab(p, p->pri)) { - pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), 1); - pri_rel(p->pri); - } - else - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); - } - p->progress = 1; - } -#endif - /* don't continue in ast_indicate */ - res = 0; - break; - case AST_CONTROL_CONGESTION: - chan->hangupcause = AST_CAUSE_CONGESTION; -#ifdef HAVE_PRI - if (p->priindication_oob && p->sig == SIG_PRI) { - chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION; - chan->_softhangup |= AST_SOFTHANGUP_DEV; - res = 0; - } else if (!p->progress && p->sig==SIG_PRI && p->pri && !p->outgoing) { - if (p->pri) { - if (!pri_grab(p, p->pri)) { - pri_progress(p->pri->pri,p->call, PVT_TO_CHANNEL(p), 1); - pri_rel(p->pri); - } else - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); - } - p->progress = 1; - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_CONGESTION); - } else -#endif - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_CONGESTION); - break; - case AST_CONTROL_HOLD: -#ifdef HAVE_PRI - if (p->pri && !strcasecmp(p->mohinterpret, "passthrough")) { - if (!pri_grab(p, p->pri)) { - res = pri_notify(p->pri->pri, p->call, p->prioffset, PRI_NOTIFY_REMOTE_HOLD); - pri_rel(p->pri); - } else - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); - } else -#endif - ast_moh_start(chan, data, p->mohinterpret); - break; - case AST_CONTROL_UNHOLD: -#ifdef HAVE_PRI - if (p->pri && !strcasecmp(p->mohinterpret, "passthrough")) { - if (!pri_grab(p, p->pri)) { - res = pri_notify(p->pri->pri, p->call, p->prioffset, PRI_NOTIFY_REMOTE_RETRIEVAL); - pri_rel(p->pri); - } else - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); - } else -#endif - ast_moh_stop(chan); - break; - case AST_CONTROL_RADIO_KEY: - if (p->radio) - res = dahdi_set_hook(p->subs[index].dfd, DAHDI_OFFHOOK); - res = 0; - break; - case AST_CONTROL_RADIO_UNKEY: - if (p->radio) - res = dahdi_set_hook(p->subs[index].dfd, DAHDI_RINGOFF); - res = 0; - break; - case AST_CONTROL_FLASH: - /* flash hookswitch */ - if (ISTRUNK(p) && (p->sig != SIG_PRI)) { - /* Clear out the dial buffer */ - p->dop.dialstr[0] = '\0'; - if ((ioctl(p->subs[SUB_REAL].dfd,DAHDI_HOOK,&func) == -1) && (errno != EINPROGRESS)) { - ast_log(LOG_WARNING, "Unable to flash external trunk on channel %s: %s\n", - chan->name, strerror(errno)); - } else - res = 0; - } else - res = 0; - break; - case AST_CONTROL_SRCUPDATE: - res = 0; - break; - case -1: - res = tone_zone_play_tone(p->subs[index].dfd, -1); - break; - } - } else - res = 0; - ast_mutex_unlock(&p->lock); - return res; -} - -static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpbx, int index, int law, int transfercapability) -{ - struct ast_channel *tmp; - int deflaw; - int res; - int x,y; - int features; - char *b2 = NULL; - struct dahdi_params ps; - char chanprefix[*dahdi_chan_name_len + 4]; - - if (i->subs[index].owner) { - ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[index]); - return NULL; - } - y = 1; - do { - if (b2) - free(b2); -#ifdef HAVE_PRI - if (i->bearer || (i->pri && (i->sig == SIG_FXSKS))) - b2 = ast_safe_string_alloc("%d:%d-%d", i->pri->trunkgroup, i->channel, y); - else -#endif - if (i->channel == CHAN_PSEUDO) - b2 = ast_safe_string_alloc("pseudo-%ld", ast_random()); - else - b2 = ast_safe_string_alloc("%d-%d", i->channel, y); - for (x = 0; x < 3; x++) { - if ((index != x) && i->subs[x].owner && !strcasecmp(b2, i->subs[x].owner->name + (!strncmp(i->subs[x].owner->name, "Zap", 3) ? 4 : 6))) - break; - } - y++; - } while (x < 3); - strcpy(chanprefix, dahdi_chan_name); - strcat(chanprefix, "/%s"); - tmp = ast_channel_alloc(0, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, chanprefix, b2); - if (b2) /*!> b2 can be freed now, it's been copied into the channel structure */ - free(b2); - if (!tmp) - return NULL; - tmp->tech = chan_tech; - ps.channo = i->channel; - res = ioctl(i->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &ps); - if (res) { - ast_log(LOG_WARNING, "Unable to get parameters, assuming MULAW: %s\n", strerror(errno)); - ps.curlaw = DAHDI_LAW_MULAW; - } - if (ps.curlaw == DAHDI_LAW_ALAW) - deflaw = AST_FORMAT_ALAW; - else - deflaw = AST_FORMAT_ULAW; - if (law) { - if (law == DAHDI_LAW_ALAW) - deflaw = AST_FORMAT_ALAW; - else - deflaw = AST_FORMAT_ULAW; - } - tmp->fds[0] = i->subs[index].dfd; - tmp->nativeformats = AST_FORMAT_SLINEAR | deflaw; - /* Start out assuming ulaw since it's smaller :) */ - tmp->rawreadformat = deflaw; - tmp->readformat = deflaw; - tmp->rawwriteformat = deflaw; - tmp->writeformat = deflaw; - i->subs[index].linear = 0; - dahdi_setlinear(i->subs[index].dfd, i->subs[index].linear); - features = 0; - if (index == SUB_REAL) { - if (i->busydetect && CANBUSYDETECT(i)) - features |= DSP_FEATURE_BUSY_DETECT; - if ((i->callprogress & 1) && CANPROGRESSDETECT(i)) - features |= DSP_FEATURE_CALL_PROGRESS; - if ((!i->outgoing && (i->callprogress & 4)) || - (i->outgoing && (i->callprogress & 2))) { - features |= DSP_FEATURE_FAX_DETECT; - } -#ifdef DAHDI_TONEDETECT - x = DAHDI_TONEDETECT_ON | DAHDI_TONEDETECT_MUTE; - if (ioctl(i->subs[index].dfd, DAHDI_TONEDETECT, &x)) { -#endif - i->hardwaredtmf = 0; - features |= DSP_FEATURE_DTMF_DETECT; -#ifdef DAHDI_TONEDETECT - } else if (NEED_MFDETECT(i)) { - i->hardwaredtmf = 1; - features |= DSP_FEATURE_DTMF_DETECT; - } -#endif - } - if (features) { - if (i->dsp) { - ast_log(LOG_DEBUG, "Already have a dsp on %s?\n", tmp->name); - } else { - if (i->channel != CHAN_PSEUDO) - i->dsp = ast_dsp_new(); - else - i->dsp = NULL; - if (i->dsp) { - i->dsp_features = features; -#ifdef HAVE_PRI - /* We cannot do progress detection until receives PROGRESS message */ - if (i->outgoing && (i->sig == SIG_PRI)) { - /* Remember requested DSP features, don't treat - talking as ANSWER */ - i->dsp_features = features & ~DSP_PROGRESS_TALK; - features = 0; - } -#endif - ast_dsp_set_features(i->dsp, features); - ast_dsp_digitmode(i->dsp, DSP_DIGITMODE_DTMF | i->dtmfrelax); - if (!ast_strlen_zero(progzone)) - ast_dsp_set_call_progress_zone(i->dsp, progzone); - if (i->busydetect && CANBUSYDETECT(i)) { - ast_dsp_set_busy_count(i->dsp, i->busycount); - ast_dsp_set_busy_pattern(i->dsp, i->busy_tonelength, i->busy_quietlength); - } - } - } - } - - if (state == AST_STATE_RING) - tmp->rings = 1; - tmp->tech_pvt = i; - if ((i->sig == SIG_FXOKS) || (i->sig == SIG_FXOGS) || (i->sig == SIG_FXOLS)) { - /* Only FXO signalled stuff can be picked up */ - tmp->callgroup = i->callgroup; - tmp->pickupgroup = i->pickupgroup; - } - if (!ast_strlen_zero(i->language)) - ast_string_field_set(tmp, language, i->language); - if (!i->owner) - i->owner = tmp; - if (!ast_strlen_zero(i->accountcode)) - ast_string_field_set(tmp, accountcode, i->accountcode); - if (i->amaflags) - tmp->amaflags = i->amaflags; - i->subs[index].owner = tmp; - ast_copy_string(tmp->context, i->context, sizeof(tmp->context)); - ast_string_field_set(tmp, call_forward, i->call_forward); - /* If we've been told "no ADSI" then enforce it */ - if (!i->adsi) - tmp->adsicpe = AST_ADSI_UNAVAILABLE; - if (!ast_strlen_zero(i->exten)) - ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten)); - if (!ast_strlen_zero(i->rdnis)) - tmp->cid.cid_rdnis = ast_strdup(i->rdnis); - if (!ast_strlen_zero(i->dnid)) - tmp->cid.cid_dnid = ast_strdup(i->dnid); - - /* Don't use ast_set_callerid() here because it will - * generate a needless NewCallerID event */ -#ifdef PRI_ANI - if (!ast_strlen_zero(i->cid_ani)) - tmp->cid.cid_ani = ast_strdup(i->cid_ani); - else - tmp->cid.cid_ani = ast_strdup(i->cid_num); -#else - tmp->cid.cid_ani = ast_strdup(i->cid_num); -#endif - tmp->cid.cid_pres = i->callingpres; - tmp->cid.cid_ton = i->cid_ton; -#ifdef HAVE_PRI - tmp->transfercapability = transfercapability; - pbx_builtin_setvar_helper(tmp, "TRANSFERCAPABILITY", ast_transfercapability2str(transfercapability)); - if (transfercapability & PRI_TRANS_CAP_DIGITAL) - i->digital = 1; - /* Assume calls are not idle calls unless we're told differently */ - i->isidlecall = 0; - i->alreadyhungup = 0; -#endif - /* clear the fake event in case we posted one before we had ast_channel */ - i->fake_event = 0; - /* Assure there is no confmute on this channel */ - dahdi_confmute(i, 0); - /* Configure the new channel jb */ - ast_jb_configure(tmp, &global_jbconf); - if (startpbx) { - if (ast_pbx_start(tmp)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); - ast_hangup(tmp); - i->owner = NULL; - return NULL; - } - } - - ast_module_ref(ast_module_info->self); - - return tmp; -} - - -static int my_getsigstr(struct ast_channel *chan, char *str, const char *term, int ms) -{ - char c; - - *str = 0; /* start with empty output buffer */ - for (;;) - { - /* Wait for the first digit (up to specified ms). */ - c = ast_waitfordigit(chan, ms); - /* if timeout, hangup or error, return as such */ - if (c < 1) - return c; - *str++ = c; - *str = 0; - if (strchr(term, c)) - return 1; - } -} - -static int dahdi_wink(struct dahdi_pvt *p, int index) -{ - int j; - dahdi_set_hook(p->subs[index].dfd, DAHDI_WINK); - for (;;) - { - /* set bits of interest */ - j = DAHDI_IOMUX_SIGEVENT; - /* wait for some happening */ - if (ioctl(p->subs[index].dfd,DAHDI_IOMUX,&j) == -1) return(-1); - /* exit loop if we have it */ - if (j & DAHDI_IOMUX_SIGEVENT) break; - } - /* get the event info */ - if (ioctl(p->subs[index].dfd,DAHDI_GETEVENT,&j) == -1) return(-1); - return 0; -} - -static void *ss_thread(void *data) -{ - struct ast_channel *chan = data; - struct dahdi_pvt *p = chan->tech_pvt; - char exten[AST_MAX_EXTENSION] = ""; - char exten2[AST_MAX_EXTENSION] = ""; - unsigned char buf[256]; - char dtmfcid[300]; - char dtmfbuf[300]; - struct callerid_state *cs = NULL; - char *name = NULL, *number = NULL; - int distMatches; - int curRingData[3]; - int receivedRingT; - int counter1; - int counter; - int samples = 0; - struct ast_smdi_md_message *smdi_msg = NULL; - int flags; - int i; - int timeout; - int getforward = 0; - char *s1, *s2; - int len = 0; - int res; - int index; - - ast_mutex_lock(&ss_thread_lock); - ss_thread_count++; - ast_mutex_unlock(&ss_thread_lock); - /* in the bizarre case where the channel has become a zombie before we - even get started here, abort safely - */ - if (!p) { - ast_log(LOG_WARNING, "Channel became a zombie before simple switch could be started (%s)\n", chan->name); - ast_hangup(chan); - goto quit; - } - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Starting simple switch on '%s'\n", chan->name); - index = dahdi_get_index(chan, p, 1); - if (index < 0) { - ast_log(LOG_WARNING, "Huh?\n"); - ast_hangup(chan); - goto quit; - } - if (p->dsp) - ast_dsp_digitreset(p->dsp); - switch (p->sig) { -#ifdef HAVE_PRI - case SIG_PRI: - /* Now loop looking for an extension */ - ast_copy_string(exten, p->exten, sizeof(exten)); - len = strlen(exten); - res = 0; - while ((len < AST_MAX_EXTENSION-1) && ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) { - if (len && !ast_ignore_pattern(chan->context, exten)) - tone_zone_play_tone(p->subs[index].dfd, -1); - else - tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALTONE); - if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num)) - timeout = matchdigittimeout; - else - timeout = gendigittimeout; - res = ast_waitfordigit(chan, timeout); - if (res < 0) { - ast_log(LOG_DEBUG, "waitfordigit returned < 0...\n"); - ast_hangup(chan); - goto quit; - } else if (res) { - exten[len++] = res; - exten[len] = '\0'; - } else - break; - } - /* if no extension was received ('unspecified') on overlap call, use the 's' extension */ - if (ast_strlen_zero(exten)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Going to extension s|1 because of empty extension received on overlap call\n"); - exten[0] = 's'; - exten[1] = '\0'; - } - tone_zone_play_tone(p->subs[index].dfd, -1); - if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num)) { - /* Start the real PBX */ - ast_copy_string(chan->exten, exten, sizeof(chan->exten)); - if (p->dsp) ast_dsp_digitreset(p->dsp); - dahdi_enable_ec(p); - ast_setstate(chan, AST_STATE_RING); - res = ast_pbx_run(chan); - if (res) { - ast_log(LOG_WARNING, "PBX exited non-zero!\n"); - } - } else { - ast_log(LOG_DEBUG, "No such possible extension '%s' in context '%s'\n", exten, chan->context); - chan->hangupcause = AST_CAUSE_UNALLOCATED; - ast_hangup(chan); - p->exten[0] = '\0'; - /* Since we send release complete here, we won't get one */ - p->call = NULL; - } - goto quit; - break; -#endif - case SIG_FEATD: - case SIG_FEATDMF: - case SIG_FEATDMF_TA: - case SIG_E911: - case SIG_FGC_CAMAMF: - case SIG_FEATB: - case SIG_EMWINK: - case SIG_SF_FEATD: - case SIG_SF_FEATDMF: - case SIG_SF_FEATB: - case SIG_SFWINK: - if (dahdi_wink(p, index)) - goto quit; - /* Fall through */ - case SIG_EM: - case SIG_EM_E1: - case SIG_SF: - case SIG_FGC_CAMA: - res = tone_zone_play_tone(p->subs[index].dfd, -1); - if (p->dsp) - ast_dsp_digitreset(p->dsp); - /* set digit mode appropriately */ - if (p->dsp) { - if (NEED_MFDETECT(p)) - ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MF | p->dtmfrelax); - else - ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax); - } - memset(dtmfbuf, 0, sizeof(dtmfbuf)); - /* Wait for the first digit only if immediate=no */ - if (!p->immediate) - /* Wait for the first digit (up to 5 seconds). */ - res = ast_waitfordigit(chan, 5000); - else - res = 0; - if (res > 0) { - /* save first char */ - dtmfbuf[0] = res; - switch (p->sig) { - case SIG_FEATD: - case SIG_SF_FEATD: - res = my_getsigstr(chan, dtmfbuf + 1, "*", 3000); - if (res > 0) - res = my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000); - if ((res < 1) && (p->dsp)) ast_dsp_digitreset(p->dsp); - break; - case SIG_FEATDMF_TA: - res = my_getsigstr(chan, dtmfbuf + 1, "#", 3000); - if ((res < 1) && (p->dsp)) ast_dsp_digitreset(p->dsp); - if (dahdi_wink(p, index)) goto quit; - dtmfbuf[0] = 0; - /* Wait for the first digit (up to 5 seconds). */ - res = ast_waitfordigit(chan, 5000); - if (res <= 0) break; - dtmfbuf[0] = res; - /* fall through intentionally */ - case SIG_FEATDMF: - case SIG_E911: - case SIG_FGC_CAMAMF: - case SIG_SF_FEATDMF: - res = my_getsigstr(chan, dtmfbuf + 1, "#", 3000); - /* if international caca, do it again to get real ANO */ - if ((p->sig == SIG_FEATDMF) && (dtmfbuf[1] != '0') && (strlen(dtmfbuf) != 14)) - { - if (dahdi_wink(p, index)) goto quit; - dtmfbuf[0] = 0; - /* Wait for the first digit (up to 5 seconds). */ - res = ast_waitfordigit(chan, 5000); - if (res <= 0) break; - dtmfbuf[0] = res; - res = my_getsigstr(chan, dtmfbuf + 1, "#", 3000); - } - if (res > 0) { - /* if E911, take off hook */ - if (p->sig == SIG_E911) - dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_OFFHOOK); - res = my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "#", 3000); - } - if ((res < 1) && (p->dsp)) ast_dsp_digitreset(p->dsp); - break; - case SIG_FEATB: - case SIG_SF_FEATB: - res = my_getsigstr(chan, dtmfbuf + 1, "#", 3000); - if ((res < 1) && (p->dsp)) ast_dsp_digitreset(p->dsp); - break; - case SIG_EMWINK: - /* if we received a '*', we are actually receiving Feature Group D - dial syntax, so use that mode; otherwise, fall through to normal - mode - */ - if (res == '*') { - res = my_getsigstr(chan, dtmfbuf + 1, "*", 3000); - if (res > 0) - res = my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000); - if ((res < 1) && (p->dsp)) ast_dsp_digitreset(p->dsp); - break; - } - default: - /* If we got the first digit, get the rest */ - len = 1; - dtmfbuf[len] = '\0'; - while ((len < AST_MAX_EXTENSION-1) && ast_matchmore_extension(chan, chan->context, dtmfbuf, 1, p->cid_num)) { - if (ast_exists_extension(chan, chan->context, dtmfbuf, 1, p->cid_num)) { - timeout = matchdigittimeout; - } else { - timeout = gendigittimeout; - } - res = ast_waitfordigit(chan, timeout); - if (res < 0) { - ast_log(LOG_DEBUG, "waitfordigit returned < 0...\n"); - ast_hangup(chan); - goto quit; - } else if (res) { - dtmfbuf[len++] = res; - dtmfbuf[len] = '\0'; - } else { - break; - } - } - break; - } - } - if (res == -1) { - ast_log(LOG_WARNING, "getdtmf on channel %d: %s\n", p->channel, strerror(errno)); - ast_hangup(chan); - goto quit; - } else if (res < 0) { - ast_log(LOG_DEBUG, "Got hung up before digits finished\n"); - ast_hangup(chan); - goto quit; - } - - if (p->sig == SIG_FGC_CAMA) { - char anibuf[100]; - - if (ast_safe_sleep(chan,1000) == -1) { - ast_hangup(chan); - goto quit; - } - dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_OFFHOOK); - ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MF | p->dtmfrelax); - res = my_getsigstr(chan, anibuf, "#", 10000); - if ((res > 0) && (strlen(anibuf) > 2)) { - if (anibuf[strlen(anibuf) - 1] == '#') - anibuf[strlen(anibuf) - 1] = 0; - ast_set_callerid(chan, anibuf + 2, NULL, anibuf + 2); - } - ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax); - } - - ast_copy_string(exten, dtmfbuf, sizeof(exten)); - if (ast_strlen_zero(exten)) - ast_copy_string(exten, "s", sizeof(exten)); - if (p->sig == SIG_FEATD || p->sig == SIG_EMWINK) { - /* Look for Feature Group D on all E&M Wink and Feature Group D trunks */ - if (exten[0] == '*') { - char *stringp=NULL; - ast_copy_string(exten2, exten, sizeof(exten2)); - /* Parse out extension and callerid */ - stringp=exten2 +1; - s1 = strsep(&stringp, "*"); - s2 = strsep(&stringp, "*"); - if (s2) { - if (!ast_strlen_zero(p->cid_num)) - ast_set_callerid(chan, p->cid_num, NULL, p->cid_num); - else - ast_set_callerid(chan, s1, NULL, s1); - ast_copy_string(exten, s2, sizeof(exten)); - } else - ast_copy_string(exten, s1, sizeof(exten)); - } else if (p->sig == SIG_FEATD) - ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d. Assuming E&M Wink instead\n", p->channel); - } - if ((p->sig == SIG_FEATDMF) || (p->sig == SIG_FEATDMF_TA)) { - if (exten[0] == '*') { - char *stringp=NULL; - ast_copy_string(exten2, exten, sizeof(exten2)); - /* Parse out extension and callerid */ - stringp=exten2 +1; - s1 = strsep(&stringp, "#"); - s2 = strsep(&stringp, "#"); - if (s2) { - if (!ast_strlen_zero(p->cid_num)) - ast_set_callerid(chan, p->cid_num, NULL, p->cid_num); - else - if (*(s1 + 2)) - ast_set_callerid(chan, s1 + 2, NULL, s1 + 2); - ast_copy_string(exten, s2 + 1, sizeof(exten)); - } else - ast_copy_string(exten, s1 + 2, sizeof(exten)); - } else - ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d. Assuming E&M Wink instead\n", p->channel); - } - if ((p->sig == SIG_E911) || (p->sig == SIG_FGC_CAMAMF)) { - if (exten[0] == '*') { - char *stringp=NULL; - ast_copy_string(exten2, exten, sizeof(exten2)); - /* Parse out extension and callerid */ - stringp=exten2 +1; - s1 = strsep(&stringp, "#"); - s2 = strsep(&stringp, "#"); - if (s2 && (*(s2 + 1) == '0')) { - if (*(s2 + 2)) - ast_set_callerid(chan, s2 + 2, NULL, s2 + 2); - } - if (s1) ast_copy_string(exten, s1, sizeof(exten)); - else ast_copy_string(exten, "911", sizeof(exten)); - } else - ast_log(LOG_WARNING, "Got a non-E911/FGC CAMA input on channel %d. Assuming E&M Wink instead\n", p->channel); - } - if (p->sig == SIG_FEATB) { - if (exten[0] == '*') { - char *stringp=NULL; - ast_copy_string(exten2, exten, sizeof(exten2)); - /* Parse out extension and callerid */ - stringp=exten2 +1; - s1 = strsep(&stringp, "#"); - ast_copy_string(exten, exten2 + 1, sizeof(exten)); - } else - ast_log(LOG_WARNING, "Got a non-Feature Group B input on channel %d. Assuming E&M Wink instead\n", p->channel); - } - if ((p->sig == SIG_FEATDMF) || (p->sig == SIG_FEATDMF_TA)) { - dahdi_wink(p, index); - /* some switches require a minimum guard time between - the last FGD wink and something that answers - immediately. This ensures it */ - if (ast_safe_sleep(chan,100)) goto quit; - } - dahdi_enable_ec(p); - if (NEED_MFDETECT(p)) { - if (p->dsp) { - if (!p->hardwaredtmf) - ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax); - else { - ast_dsp_free(p->dsp); - p->dsp = NULL; - } - } - } - - if (ast_exists_extension(chan, chan->context, exten, 1, chan->cid.cid_num)) { - ast_copy_string(chan->exten, exten, sizeof(chan->exten)); - if (p->dsp) ast_dsp_digitreset(p->dsp); - res = ast_pbx_run(chan); - if (res) { - ast_log(LOG_WARNING, "PBX exited non-zero\n"); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_CONGESTION); - } - goto quit; - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_2 "Unknown extension '%s' in context '%s' requested\n", exten, chan->context); - sleep(2); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_INFO); - if (res < 0) - ast_log(LOG_WARNING, "Unable to start special tone on %d\n", p->channel); - else - sleep(1); - res = ast_streamfile(chan, "ss-noservice", chan->language); - if (res >= 0) - ast_waitstream(chan, ""); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_CONGESTION); - ast_hangup(chan); - goto quit; - } - break; - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FXOKS: - /* Read the first digit */ - timeout = firstdigittimeout; - /* If starting a threeway call, never timeout on the first digit so someone - can use flash-hook as a "hold" feature */ - if (p->subs[SUB_THREEWAY].owner) - timeout = 999999; - while (len < AST_MAX_EXTENSION-1) { - /* Read digit unless it's supposed to be immediate, in which case the - only answer is 's' */ - if (p->immediate) - res = 's'; - else - res = ast_waitfordigit(chan, timeout); - timeout = 0; - if (res < 0) { - ast_log(LOG_DEBUG, "waitfordigit returned < 0...\n"); - res = tone_zone_play_tone(p->subs[index].dfd, -1); - ast_hangup(chan); - goto quit; - } else if (res) { - exten[len++]=res; - exten[len] = '\0'; - } - if (!ast_ignore_pattern(chan->context, exten)) - tone_zone_play_tone(p->subs[index].dfd, -1); - else - tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALTONE); - if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num) && strcmp(exten, ast_parking_ext())) { - if (!res || !ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) { - if (getforward) { - /* Record this as the forwarding extension */ - ast_copy_string(p->call_forward, exten, sizeof(p->call_forward)); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Setting call forward to '%s' on channel %d\n", p->call_forward, p->channel); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALRECALL); - if (res) - break; - usleep(500000); - res = tone_zone_play_tone(p->subs[index].dfd, -1); - sleep(1); - memset(exten, 0, sizeof(exten)); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALTONE); - len = 0; - getforward = 0; - } else { - res = tone_zone_play_tone(p->subs[index].dfd, -1); - ast_copy_string(chan->exten, exten, sizeof(chan->exten)); - if (!ast_strlen_zero(p->cid_num)) { - if (!p->hidecallerid) - ast_set_callerid(chan, p->cid_num, NULL, p->cid_num); - else - ast_set_callerid(chan, NULL, NULL, p->cid_num); - } - if (!ast_strlen_zero(p->cid_name)) { - if (!p->hidecallerid) - ast_set_callerid(chan, NULL, p->cid_name, NULL); - } - ast_setstate(chan, AST_STATE_RING); - dahdi_enable_ec(p); - res = ast_pbx_run(chan); - if (res) { - ast_log(LOG_WARNING, "PBX exited non-zero\n"); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_CONGESTION); - } - goto quit; - } - } else { - /* It's a match, but they just typed a digit, and there is an ambiguous match, - so just set the timeout to matchdigittimeout and wait some more */ - timeout = matchdigittimeout; - } - } else if (res == 0) { - ast_log(LOG_DEBUG, "not enough digits (and no ambiguous match)...\n"); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_CONGESTION); - dahdi_wait_event(p->subs[index].dfd); - ast_hangup(chan); - goto quit; - } else if (p->callwaiting && !strcmp(exten, "*70")) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Disabling call waiting on %s\n", chan->name); - /* Disable call waiting if enabled */ - p->callwaiting = 0; - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALRECALL); - if (res) { - ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", - chan->name, strerror(errno)); - } - len = 0; - ioctl(p->subs[index].dfd,DAHDI_CONFDIAG,&len); - memset(exten, 0, sizeof(exten)); - timeout = firstdigittimeout; - - } else if (!strcmp(exten,ast_pickup_ext())) { - /* Scan all channels and see if there are any - * ringing channels that have call groups - * that equal this channels pickup group - */ - if (index == SUB_REAL) { - /* Switch us from Third call to Call Wait */ - if (p->subs[SUB_THREEWAY].owner) { - /* If you make a threeway call and the *8# a call, it should actually - look like a callwait */ - alloc_sub(p, SUB_CALLWAIT); - swap_subs(p, SUB_CALLWAIT, SUB_THREEWAY); - unalloc_sub(p, SUB_THREEWAY); - } - dahdi_enable_ec(p); - if (ast_pickup_call(chan)) { - ast_log(LOG_DEBUG, "No call pickup possible...\n"); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_CONGESTION); - dahdi_wait_event(p->subs[index].dfd); - } - ast_hangup(chan); - goto quit; - } else { - ast_log(LOG_WARNING, "Huh? Got *8# on call not on real\n"); - ast_hangup(chan); - goto quit; - } - - } else if (!p->hidecallerid && !strcmp(exten, "*67")) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Disabling Caller*ID on %s\n", chan->name); - /* Disable Caller*ID if enabled */ - p->hidecallerid = 1; - if (chan->cid.cid_num) - free(chan->cid.cid_num); - chan->cid.cid_num = NULL; - if (chan->cid.cid_name) - free(chan->cid.cid_name); - chan->cid.cid_name = NULL; - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALRECALL); - if (res) { - ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", - chan->name, strerror(errno)); - } - len = 0; - memset(exten, 0, sizeof(exten)); - timeout = firstdigittimeout; - } else if (p->callreturn && !strcmp(exten, "*69")) { - res = 0; - if (!ast_strlen_zero(p->lastcid_num)) { - res = ast_say_digit_str(chan, p->lastcid_num, "", chan->language); - } - if (!res) - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALRECALL); - break; - } else if (!strcmp(exten, "*78")) { - /* Do not disturb */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Enabled DND on channel %d\n", p->channel); - manager_event(EVENT_FLAG_SYSTEM, "DNDState", - "Channel: %s/%d\r\n" - "Status: enabled\r\n", dahdi_chan_name, p->channel); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALRECALL); - p->dnd = 1; - getforward = 0; - memset(exten, 0, sizeof(exten)); - len = 0; - } else if (!strcmp(exten, "*79")) { - /* Do not disturb */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Disabled DND on channel %d\n", p->channel); - manager_event(EVENT_FLAG_SYSTEM, "DNDState", - "Channel: %s/%d\r\n" - "Status: disabled\r\n", dahdi_chan_name, p->channel); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALRECALL); - p->dnd = 0; - getforward = 0; - memset(exten, 0, sizeof(exten)); - len = 0; - } else if (p->cancallforward && !strcmp(exten, "*72")) { - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALRECALL); - getforward = 1; - memset(exten, 0, sizeof(exten)); - len = 0; - } else if (p->cancallforward && !strcmp(exten, "*73")) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Cancelling call forwarding on channel %d\n", p->channel); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALRECALL); - memset(p->call_forward, 0, sizeof(p->call_forward)); - getforward = 0; - memset(exten, 0, sizeof(exten)); - len = 0; - } else if ((p->transfer || p->canpark) && !strcmp(exten, ast_parking_ext()) && - p->subs[SUB_THREEWAY].owner && - ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) { - /* This is a three way call, the main call being a real channel, - and we're parking the first call. */ - ast_masq_park_call(ast_bridged_channel(p->subs[SUB_THREEWAY].owner), chan, 0, NULL); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Parking call to '%s'\n", chan->name); - break; - } else if (!ast_strlen_zero(p->lastcid_num) && !strcmp(exten, "*60")) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Blacklisting number %s\n", p->lastcid_num); - res = ast_db_put("blacklist", p->lastcid_num, "1"); - if (!res) { - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALRECALL); - memset(exten, 0, sizeof(exten)); - len = 0; - } - } else if (p->hidecallerid && !strcmp(exten, "*82")) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Enabling Caller*ID on %s\n", chan->name); - /* Enable Caller*ID if enabled */ - p->hidecallerid = 0; - if (chan->cid.cid_num) - free(chan->cid.cid_num); - chan->cid.cid_num = NULL; - if (chan->cid.cid_name) - free(chan->cid.cid_name); - chan->cid.cid_name = NULL; - ast_set_callerid(chan, p->cid_num, p->cid_name, NULL); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_DIALRECALL); - if (res) { - ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", - chan->name, strerror(errno)); - } - len = 0; - memset(exten, 0, sizeof(exten)); - timeout = firstdigittimeout; - } else if (!strcmp(exten, "*0")) { - struct ast_channel *nbridge = - p->subs[SUB_THREEWAY].owner; - struct dahdi_pvt *pbridge = NULL; - /* set up the private struct of the bridged one, if any */ - if (nbridge && ast_bridged_channel(nbridge)) - pbridge = ast_bridged_channel(nbridge)->tech_pvt; - if (nbridge && pbridge && - (nbridge->tech == chan_tech) && - (ast_bridged_channel(nbridge)->tech == chan_tech) && - ISTRUNK(pbridge)) { - int func = DAHDI_FLASH; - /* Clear out the dial buffer */ - p->dop.dialstr[0] = '\0'; - /* flash hookswitch */ - if ((ioctl(pbridge->subs[SUB_REAL].dfd,DAHDI_HOOK,&func) == -1) && (errno != EINPROGRESS)) { - ast_log(LOG_WARNING, "Unable to flash external trunk on channel %s: %s\n", - nbridge->name, strerror(errno)); - } - swap_subs(p, SUB_REAL, SUB_THREEWAY); - unalloc_sub(p, SUB_THREEWAY); - p->owner = p->subs[SUB_REAL].owner; - if (ast_bridged_channel(p->subs[SUB_REAL].owner)) - ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD); - ast_hangup(chan); - goto quit; - } else { - tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_CONGESTION); - dahdi_wait_event(p->subs[index].dfd); - tone_zone_play_tone(p->subs[index].dfd, -1); - swap_subs(p, SUB_REAL, SUB_THREEWAY); - unalloc_sub(p, SUB_THREEWAY); - p->owner = p->subs[SUB_REAL].owner; - ast_hangup(chan); - goto quit; - } - } else if (!ast_canmatch_extension(chan, chan->context, exten, 1, chan->cid.cid_num) && - ((exten[0] != '*') || (strlen(exten) > 2))) { - if (option_debug) - ast_log(LOG_DEBUG, "Can't match %s from '%s' in context %s\n", exten, chan->cid.cid_num ? chan->cid.cid_num : "<Unknown Caller>", chan->context); - break; - } - if (!timeout) - timeout = gendigittimeout; - if (len && !ast_ignore_pattern(chan->context, exten)) - tone_zone_play_tone(p->subs[index].dfd, -1); - } - break; - case SIG_FXSLS: - case SIG_FXSGS: - case SIG_FXSKS: -#ifdef HAVE_PRI - if (p->pri) { - /* This is a GR-303 trunk actually. Wait for the first ring... */ - struct ast_frame *f; - int res; - time_t start; - - time(&start); - ast_setstate(chan, AST_STATE_RING); - while (time(NULL) < start + 3) { - res = ast_waitfor(chan, 1000); - if (res) { - f = ast_read(chan); - if (!f) { - ast_log(LOG_WARNING, "Whoa, hangup while waiting for first ring!\n"); - ast_hangup(chan); - goto quit; - } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RING)) { - res = 1; - } else - res = 0; - ast_frfree(f); - if (res) { - ast_log(LOG_DEBUG, "Got ring!\n"); - res = 0; - break; - } - } - } - } -#endif - /* check for SMDI messages */ - if (p->use_smdi && p->smdi_iface) { - smdi_msg = ast_smdi_md_message_wait(p->smdi_iface, SMDI_MD_WAIT_TIMEOUT); - - if (smdi_msg != NULL) { - ast_copy_string(chan->exten, smdi_msg->fwd_st, sizeof(chan->exten)); - - if (smdi_msg->type == 'B') - pbx_builtin_setvar_helper(chan, "_SMDI_VM_TYPE", "b"); - else if (smdi_msg->type == 'N') - pbx_builtin_setvar_helper(chan, "_SMDI_VM_TYPE", "u"); - - ast_log(LOG_DEBUG, "Recieved SMDI message on %s\n", chan->name); - } else { - ast_log(LOG_WARNING, "SMDI enabled but no SMDI message present\n"); - } - } - - if (p->use_callerid && (p->cid_signalling == CID_SIG_SMDI && smdi_msg)) { - number = smdi_msg->calling_st; - - /* If we want caller id, we're in a prering state due to a polarity reversal - * and we're set to use a polarity reversal to trigger the start of caller id, - * grab the caller id and wait for ringing to start... */ - } else if (p->use_callerid && (chan->_state == AST_STATE_PRERING && p->cid_start == CID_START_POLARITY)) { - /* If set to use DTMF CID signalling, listen for DTMF */ - if (p->cid_signalling == CID_SIG_DTMF) { - int i = 0; - cs = NULL; - ast_log(LOG_DEBUG, "Receiving DTMF cid on " - "channel %s\n", chan->name); - dahdi_setlinear(p->subs[index].dfd, 0); - res = 2000; - for (;;) { - struct ast_frame *f; - res = ast_waitfor(chan, res); - if (res <= 0) { - ast_log(LOG_WARNING, "DTMFCID timed out waiting for ring. " - "Exiting simple switch\n"); - ast_hangup(chan); - goto quit; - } - f = ast_read(chan); - if (!f) - break; - if (f->frametype == AST_FRAME_DTMF) { - dtmfbuf[i++] = f->subclass; - ast_log(LOG_DEBUG, "CID got digit '%c'\n", f->subclass); - res = 2000; - } - ast_frfree(f); - if (chan->_state == AST_STATE_RING || - chan->_state == AST_STATE_RINGING) - break; /* Got ring */ - } - dtmfbuf[i] = '\0'; - dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear); - /* Got cid and ring. */ - ast_log(LOG_DEBUG, "CID got string '%s'\n", dtmfbuf); - callerid_get_dtmf(dtmfbuf, dtmfcid, &flags); - ast_log(LOG_DEBUG, "CID is '%s', flags %d\n", - dtmfcid, flags); - /* If first byte is NULL, we have no cid */ - if (!ast_strlen_zero(dtmfcid)) - number = dtmfcid; - else - number = NULL; - /* If set to use V23 Signalling, launch our FSK gubbins and listen for it */ - } else if ((p->cid_signalling == CID_SIG_V23) || (p->cid_signalling == CID_SIG_V23_JP)) { - cs = callerid_new(p->cid_signalling); - if (cs) { - samples = 0; -#if 1 - bump_gains(p); -#endif - /* Take out of linear mode for Caller*ID processing */ - dahdi_setlinear(p->subs[index].dfd, 0); - - /* First we wait and listen for the Caller*ID */ - for (;;) { - i = DAHDI_IOMUX_READ | DAHDI_IOMUX_SIGEVENT; - if ((res = ioctl(p->subs[index].dfd, DAHDI_IOMUX, &i))) { - ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno)); - callerid_free(cs); - ast_hangup(chan); - goto quit; - } - if (i & DAHDI_IOMUX_SIGEVENT) { - res = dahdi_get_event(p->subs[index].dfd); - ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res)); - - if (p->cid_signalling == CID_SIG_V23_JP) { -#ifdef DAHDI_EVENT_RINGBEGIN - if (res == DAHDI_EVENT_RINGBEGIN) { - res = dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_OFFHOOK); - usleep(1); - } -#endif - } else { - res = 0; - break; - } - } else if (i & DAHDI_IOMUX_READ) { - res = read(p->subs[index].dfd, buf, sizeof(buf)); - if (res < 0) { - if (errno != ELAST) { - ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno)); - callerid_free(cs); - ast_hangup(chan); - goto quit; - } - break; - } - samples += res; - - if (p->cid_signalling == CID_SIG_V23_JP) { - res = callerid_feed_jp(cs, buf, res, AST_LAW(p)); - } else { - res = callerid_feed(cs, buf, res, AST_LAW(p)); - } - - if (res < 0) { - ast_log(LOG_WARNING, "CallerID feed failed on channel '%s'\n", chan->name); - break; - } else if (res) - break; - else if (samples > (8000 * 10)) - break; - } - } - if (res == 1) { - callerid_get(cs, &name, &number, &flags); - ast_log(LOG_NOTICE, "CallerID number: %s, name: %s, flags=%d\n", number, name, flags); - } - - if (p->cid_signalling == CID_SIG_V23_JP) { - res = dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_ONHOOK); - usleep(1); - res = 4000; - } else { - - /* Finished with Caller*ID, now wait for a ring to make sure there really is a call coming */ - res = 2000; - } - - for (;;) { - struct ast_frame *f; - res = ast_waitfor(chan, res); - if (res <= 0) { - ast_log(LOG_WARNING, "CID timed out waiting for ring. " - "Exiting simple switch\n"); - ast_hangup(chan); - goto quit; - } - if (!(f = ast_read(chan))) { - ast_log(LOG_WARNING, "Hangup received waiting for ring. Exiting simple switch\n"); - ast_hangup(chan); - goto quit; - } - ast_frfree(f); - if (chan->_state == AST_STATE_RING || - chan->_state == AST_STATE_RINGING) - break; /* Got ring */ - } - - /* We must have a ring by now, so, if configured, lets try to listen for - * distinctive ringing */ - if (p->usedistinctiveringdetection == 1) { - len = 0; - distMatches = 0; - /* Clear the current ring data array so we dont have old data in it. */ - for (receivedRingT = 0; receivedRingT < (sizeof(curRingData) / sizeof(curRingData[0])); receivedRingT++) - curRingData[receivedRingT] = 0; - receivedRingT = 0; - counter = 0; - counter1 = 0; - /* Check to see if context is what it should be, if not set to be. */ - if (strcmp(p->context,p->defcontext) != 0) { - ast_copy_string(p->context, p->defcontext, sizeof(p->context)); - ast_copy_string(chan->context,p->defcontext,sizeof(chan->context)); - } - - for (;;) { - i = DAHDI_IOMUX_READ | DAHDI_IOMUX_SIGEVENT; - if ((res = ioctl(p->subs[index].dfd, DAHDI_IOMUX, &i))) { - ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno)); - callerid_free(cs); - ast_hangup(chan); - goto quit; - } - if (i & DAHDI_IOMUX_SIGEVENT) { - res = dahdi_get_event(p->subs[index].dfd); - ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res)); - res = 0; - /* Let us detect distinctive ring */ - - curRingData[receivedRingT] = p->ringt; - - if (p->ringt < p->ringt_base/2) - break; - /* Increment the ringT counter so we can match it against - values in chan_dahdi.conf for distinctive ring */ - if (++receivedRingT == (sizeof(curRingData) / sizeof(curRingData[0]))) - break; - } else if (i & DAHDI_IOMUX_READ) { - res = read(p->subs[index].dfd, buf, sizeof(buf)); - if (res < 0) { - if (errno != ELAST) { - ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno)); - callerid_free(cs); - ast_hangup(chan); - goto quit; - } - break; - } - if (p->ringt) - p->ringt--; - if (p->ringt == 1) { - res = -1; - break; - } - } - } - if (option_verbose > 2) - /* this only shows up if you have n of the dring patterns filled in */ - ast_verbose( VERBOSE_PREFIX_3 "Detected ring pattern: %d,%d,%d\n",curRingData[0],curRingData[1],curRingData[2]); - - for (counter = 0; counter < 3; counter++) { - /* Check to see if the rings we received match any of the ones in chan_dahdi.conf for this - channel */ - distMatches = 0; - for (counter1 = 0; counter1 < 3; counter1++) { - if (curRingData[counter1] <= (p->drings.ringnum[counter].ring[counter1]+10) && curRingData[counter1] >= - (p->drings.ringnum[counter].ring[counter1]-10)) { - distMatches++; - } - } - if (distMatches == 3) { - /* The ring matches, set the context to whatever is for distinctive ring.. */ - ast_copy_string(p->context, p->drings.ringContext[counter].contextData, sizeof(p->context)); - ast_copy_string(chan->context, p->drings.ringContext[counter].contextData, sizeof(chan->context)); - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Distinctive Ring matched context %s\n",p->context); - break; - } - } - } - /* Restore linear mode (if appropriate) for Caller*ID processing */ - dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear); -#if 1 - restore_gains(p); -#endif - } else - ast_log(LOG_WARNING, "Unable to get caller ID space\n"); - } else { - ast_log(LOG_WARNING, "Channel %s in prering " - "state, but I have nothing to do. " - "Terminating simple switch, should be " - "restarted by the actual ring.\n", - chan->name); - ast_hangup(chan); - goto quit; - } - } else if (p->use_callerid && p->cid_start == CID_START_RING) { - /* FSK Bell202 callerID */ - cs = callerid_new(p->cid_signalling); - if (cs) { -#if 1 - bump_gains(p); -#endif - samples = 0; - len = 0; - distMatches = 0; - /* Clear the current ring data array so we dont have old data in it. */ - for (receivedRingT = 0; receivedRingT < (sizeof(curRingData) / sizeof(curRingData[0])); receivedRingT++) - curRingData[receivedRingT] = 0; - receivedRingT = 0; - counter = 0; - counter1 = 0; - /* Check to see if context is what it should be, if not set to be. */ - if (strcmp(p->context,p->defcontext) != 0) { - ast_copy_string(p->context, p->defcontext, sizeof(p->context)); - ast_copy_string(chan->context,p->defcontext,sizeof(chan->context)); - } - - /* Take out of linear mode for Caller*ID processing */ - dahdi_setlinear(p->subs[index].dfd, 0); - for (;;) { - i = DAHDI_IOMUX_READ | DAHDI_IOMUX_SIGEVENT; - if ((res = ioctl(p->subs[index].dfd, DAHDI_IOMUX, &i))) { - ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno)); - callerid_free(cs); - ast_hangup(chan); - goto quit; - } - if (i & DAHDI_IOMUX_SIGEVENT) { - res = dahdi_get_event(p->subs[index].dfd); - ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res)); - /* If we get a PR event, they hung up while processing calerid */ - if ( res == DAHDI_EVENT_POLARITY && p->hanguponpolarityswitch && p->polarity == POLARITY_REV) { - ast_log(LOG_DEBUG, "Hanging up due to polarity reversal on channel %d while detecting callerid\n", p->channel); - p->polarity = POLARITY_IDLE; - callerid_free(cs); - ast_hangup(chan); - goto quit; - } - res = 0; - /* Let us detect callerid when the telco uses distinctive ring */ - - curRingData[receivedRingT] = p->ringt; - - if (p->ringt < p->ringt_base/2) - break; - /* Increment the ringT counter so we can match it against - values in chan_dahdi.conf for distinctive ring */ - if (++receivedRingT == (sizeof(curRingData) / sizeof(curRingData[0]))) - break; - } else if (i & DAHDI_IOMUX_READ) { - res = read(p->subs[index].dfd, buf, sizeof(buf)); - if (res < 0) { - if (errno != ELAST) { - ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno)); - callerid_free(cs); - ast_hangup(chan); - goto quit; - } - break; - } - if (p->ringt) - p->ringt--; - if (p->ringt == 1) { - res = -1; - break; - } - samples += res; - res = callerid_feed(cs, buf, res, AST_LAW(p)); - if (res < 0) { - ast_log(LOG_WARNING, "CallerID feed failed: %s\n", strerror(errno)); - break; - } else if (res) - break; - else if (samples > (8000 * 10)) - break; - } - } - if (res == 1) { - callerid_get(cs, &name, &number, &flags); - if (option_debug) - ast_log(LOG_DEBUG, "CallerID number: %s, name: %s, flags=%d\n", number, name, flags); - } - if (distinctiveringaftercid == 1) { - /* Clear the current ring data array so we dont have old data in it. */ - for (receivedRingT = 0; receivedRingT < 3; receivedRingT++) { - curRingData[receivedRingT] = 0; - } - receivedRingT = 0; - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Detecting post-CID distinctive ring\n"); - for (;;) { - i = DAHDI_IOMUX_READ | DAHDI_IOMUX_SIGEVENT; - if ((res = ioctl(p->subs[index].dfd, DAHDI_IOMUX, &i))) { - ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno)); - callerid_free(cs); - ast_hangup(chan); - goto quit; - } - if (i & DAHDI_IOMUX_SIGEVENT) { - res = dahdi_get_event(p->subs[index].dfd); - ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res)); - res = 0; - /* Let us detect callerid when the telco uses distinctive ring */ - - curRingData[receivedRingT] = p->ringt; - - if (p->ringt < p->ringt_base/2) - break; - /* Increment the ringT counter so we can match it against - values in chan_dahdi.conf for distinctive ring */ - if (++receivedRingT == (sizeof(curRingData) / sizeof(curRingData[0]))) - break; - } else if (i & DAHDI_IOMUX_READ) { - res = read(p->subs[index].dfd, buf, sizeof(buf)); - if (res < 0) { - if (errno != ELAST) { - ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno)); - callerid_free(cs); - ast_hangup(chan); - goto quit; - } - break; - } - if (p->ringt) - p->ringt--; - if (p->ringt == 1) { - res = -1; - break; - } - } - } - } - if (p->usedistinctiveringdetection == 1) { - if (option_verbose > 2) - /* this only shows up if you have n of the dring patterns filled in */ - ast_verbose( VERBOSE_PREFIX_3 "Detected ring pattern: %d,%d,%d\n",curRingData[0],curRingData[1],curRingData[2]); - - for (counter = 0; counter < 3; counter++) { - /* Check to see if the rings we received match any of the ones in chan_dahdi.conf for this - channel */ - if (option_verbose > 2) - /* this only shows up if you have n of the dring patterns filled in */ - ast_verbose( VERBOSE_PREFIX_3 "Checking %d,%d,%d\n", - p->drings.ringnum[counter].ring[0], - p->drings.ringnum[counter].ring[1], - p->drings.ringnum[counter].ring[2]); - distMatches = 0; - for (counter1 = 0; counter1 < 3; counter1++) { - if (curRingData[counter1] <= (p->drings.ringnum[counter].ring[counter1]+10) && curRingData[counter1] >= - (p->drings.ringnum[counter].ring[counter1]-10)) { - distMatches++; - } - } - if (distMatches == 3) { - /* The ring matches, set the context to whatever is for distinctive ring.. */ - ast_copy_string(p->context, p->drings.ringContext[counter].contextData, sizeof(p->context)); - ast_copy_string(chan->context, p->drings.ringContext[counter].contextData, sizeof(chan->context)); - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Distinctive Ring matched context %s\n",p->context); - break; - } - } - } - /* Restore linear mode (if appropriate) for Caller*ID processing */ - dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear); -#if 1 - restore_gains(p); -#endif - if (res < 0) { - ast_log(LOG_WARNING, "CallerID returned with error on channel '%s'\n", chan->name); - } - } else - ast_log(LOG_WARNING, "Unable to get caller ID space\n"); - } - else - cs = NULL; - - if (number) - ast_shrink_phone_number(number); - ast_set_callerid(chan, number, name, number); - - if (smdi_msg) - ASTOBJ_UNREF(smdi_msg, ast_smdi_md_message_destroy); - - if (cs) - callerid_free(cs); - - ast_setstate(chan, AST_STATE_RING); - chan->rings = 1; - p->ringt = p->ringt_base; - res = ast_pbx_run(chan); - if (res) { - ast_hangup(chan); - ast_log(LOG_WARNING, "PBX exited non-zero\n"); - } - goto quit; - default: - ast_log(LOG_WARNING, "Don't know how to handle simple switch with signalling %s on channel %d\n", sig2str(p->sig), p->channel); - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_CONGESTION); - if (res < 0) - ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel); - } - res = tone_zone_play_tone(p->subs[index].dfd, DAHDI_TONE_CONGESTION); - if (res < 0) - ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel); - ast_hangup(chan); -quit: - ast_mutex_lock(&ss_thread_lock); - ss_thread_count--; - ast_cond_signal(&ss_thread_complete); - ast_mutex_unlock(&ss_thread_lock); - return NULL; -} - -/* destroy a DAHDI channel, identified by its number */ -static int dahdi_destroy_channel_bynum(int channel) -{ - struct dahdi_pvt *tmp = NULL; - struct dahdi_pvt *prev = NULL; - - tmp = iflist; - while (tmp) { - if (tmp->channel == channel) { - int x = DAHDI_FLASH; - ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); /* important to create an event for dahdi_wait_event to register so that all ss_threads terminate */ - destroy_channel(prev, tmp, 1); - ast_module_unref(ast_module_info->self); - return RESULT_SUCCESS; - } - prev = tmp; - tmp = tmp->next; - } - return RESULT_FAILURE; -} - -static int handle_init_event(struct dahdi_pvt *i, int event) -{ - int res; - pthread_t threadid; - pthread_attr_t attr; - struct ast_channel *chan; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - /* Handle an event on a given channel for the monitor thread. */ - switch (event) { - case DAHDI_EVENT_NONE: - case DAHDI_EVENT_BITSCHANGED: - break; - case DAHDI_EVENT_WINKFLASH: - case DAHDI_EVENT_RINGOFFHOOK: - if (i->inalarm) break; - if (i->radio) break; - /* Got a ring/answer. What kind of channel are we? */ - switch (i->sig) { - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FXOKS: - res = dahdi_set_hook(i->subs[SUB_REAL].dfd, DAHDI_OFFHOOK); - if (res && (errno == EBUSY)) - break; - if (i->cidspill) { - /* Cancel VMWI spill */ - free(i->cidspill); - i->cidspill = NULL; - } - if (i->immediate) { - dahdi_enable_ec(i); - /* The channel is immediately up. Start right away */ - res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE); - chan = dahdi_new(i, AST_STATE_RING, 1, SUB_REAL, 0, 0); - if (!chan) { - ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel); - res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); - if (res < 0) - ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel); - } - } else { - /* Check for callerid, digits, etc */ - chan = dahdi_new(i, AST_STATE_RESERVED, 0, SUB_REAL, 0, 0); - if (chan) { - if (has_voicemail(i)) - res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_STUTTER); - else - res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_DIALTONE); - if (res < 0) - ast_log(LOG_WARNING, "Unable to play dialtone on channel %d, do you have defaultzone and loadzone defined?\n", i->channel); - if (ast_pthread_create(&threadid, &attr, ss_thread, chan)) { - ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel); - res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); - if (res < 0) - ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel); - ast_hangup(chan); - } - } else - ast_log(LOG_WARNING, "Unable to create channel\n"); - } - break; - case SIG_FXSLS: - case SIG_FXSGS: - case SIG_FXSKS: - i->ringt = i->ringt_base; - /* Fall through */ - case SIG_EMWINK: - case SIG_FEATD: - case SIG_FEATDMF: - case SIG_FEATDMF_TA: - case SIG_E911: - case SIG_FGC_CAMA: - case SIG_FGC_CAMAMF: - case SIG_FEATB: - case SIG_EM: - case SIG_EM_E1: - case SIG_SFWINK: - case SIG_SF_FEATD: - case SIG_SF_FEATDMF: - case SIG_SF_FEATB: - case SIG_SF: - /* Check for callerid, digits, etc */ - chan = dahdi_new(i, AST_STATE_RING, 0, SUB_REAL, 0, 0); - if (chan && ast_pthread_create(&threadid, &attr, ss_thread, chan)) { - ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel); - res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); - if (res < 0) - ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel); - ast_hangup(chan); - } else if (!chan) { - ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", i->channel); - } - break; - default: - ast_log(LOG_WARNING, "Don't know how to handle ring/answer with signalling %s on channel %d\n", sig2str(i->sig), i->channel); - res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION); - if (res < 0) - ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel); - return -1; - } - break; - case DAHDI_EVENT_NOALARM: - i->inalarm = 0; - if (!i->unknown_alarm) { - ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", i->channel); - manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", - "Channel: %d\r\n", i->channel); - } else { - i->unknown_alarm = 0; - } - break; - case DAHDI_EVENT_ALARM: - i->inalarm = 1; - res = get_alarms(i); - handle_alarms(i, res); - /* fall thru intentionally */ - case DAHDI_EVENT_ONHOOK: - if (i->radio) - break; - /* Back on hook. Hang up. */ - switch (i->sig) { - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FEATD: - case SIG_FEATDMF: - case SIG_FEATDMF_TA: - case SIG_E911: - case SIG_FGC_CAMA: - case SIG_FGC_CAMAMF: - case SIG_FEATB: - case SIG_EM: - case SIG_EM_E1: - case SIG_EMWINK: - case SIG_SF_FEATD: - case SIG_SF_FEATDMF: - case SIG_SF_FEATB: - case SIG_SF: - case SIG_SFWINK: - case SIG_FXSLS: - case SIG_FXSGS: - case SIG_FXSKS: - case SIG_GR303FXSKS: - dahdi_disable_ec(i); - res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, -1); - dahdi_set_hook(i->subs[SUB_REAL].dfd, DAHDI_ONHOOK); - break; - case SIG_GR303FXOKS: - case SIG_FXOKS: - dahdi_disable_ec(i); - /* Diddle the battery for the zhone */ -#ifdef ZHONE_HACK - dahdi_set_hook(i->subs[SUB_REAL].dfd, DAHDI_OFFHOOK); - usleep(1); -#endif - res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, -1); - dahdi_set_hook(i->subs[SUB_REAL].dfd, DAHDI_ONHOOK); - break; - case SIG_PRI: - dahdi_disable_ec(i); - res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, -1); - break; - default: - ast_log(LOG_WARNING, "Don't know how to handle on hook with signalling %s on channel %d\n", sig2str(i->sig), i->channel); - res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, -1); - return -1; - } - break; - case DAHDI_EVENT_POLARITY: - switch (i->sig) { - case SIG_FXSLS: - case SIG_FXSKS: - case SIG_FXSGS: - /* We have already got a PR before the channel was - created, but it wasn't handled. We need polarity - to be REV for remote hangup detection to work. - At least in Spain */ - if (i->hanguponpolarityswitch) - i->polarity = POLARITY_REV; - - if (i->cid_start == CID_START_POLARITY) { - i->polarity = POLARITY_REV; - ast_verbose(VERBOSE_PREFIX_2 "Starting post polarity " - "CID detection on channel %d\n", - i->channel); - chan = dahdi_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0); - if (chan && ast_pthread_create(&threadid, &attr, ss_thread, chan)) { - ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel); - } - } - break; - default: - ast_log(LOG_WARNING, "handle_init_event detected " - "polarity reversal on non-FXO (SIG_FXS) " - "interface %d\n", i->channel); - } - break; - case DAHDI_EVENT_REMOVED: /* destroy channel */ - ast_log(LOG_NOTICE, - "Got DAHDI_EVENT_REMOVED. Destroying channel %d\n", - i->channel); - dahdi_destroy_channel_bynum(i->channel); - break; - } - pthread_attr_destroy(&attr); - return 0; -} - -static void *do_monitor(void *data) -{ - int count, res, res2, spoint, pollres=0; - struct dahdi_pvt *i; - struct dahdi_pvt *last = NULL; - time_t thispass = 0, lastpass = 0; - int found; - char buf[1024]; - struct pollfd *pfds=NULL; - int lastalloc = -1; - /* This thread monitors all the frame relay interfaces which are not yet in use - (and thus do not have a separate thread) indefinitely */ - /* From here on out, we die whenever asked */ -#if 0 - if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) { - ast_log(LOG_WARNING, "Unable to set cancel type to asynchronous\n"); - return NULL; - } - ast_log(LOG_DEBUG, "Monitor starting...\n"); -#endif - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); - - for (;;) { - /* Lock the interface list */ - ast_mutex_lock(&iflock); - if (!pfds || (lastalloc != ifcount)) { - if (pfds) { - free(pfds); - pfds = NULL; - } - if (ifcount) { - if (!(pfds = ast_calloc(1, ifcount * sizeof(*pfds)))) { - ast_mutex_unlock(&iflock); - return NULL; - } - } - lastalloc = ifcount; - } - /* Build the stuff we're going to poll on, that is the socket of every - dahdi_pvt that does not have an associated owner channel */ - count = 0; - i = iflist; - while (i) { - if ((i->subs[SUB_REAL].dfd > -1) && i->sig && (!i->radio)) { - if (!i->owner && !i->subs[SUB_REAL].owner) { - /* This needs to be watched, as it lacks an owner */ - pfds[count].fd = i->subs[SUB_REAL].dfd; - pfds[count].events = POLLPRI; - pfds[count].revents = 0; - /* Message waiting or r2 channels also get watched for reading */ - if (i->cidspill) - pfds[count].events |= POLLIN; - count++; - } - } - i = i->next; - } - /* Okay, now that we know what to do, release the interface lock */ - ast_mutex_unlock(&iflock); - - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); - pthread_testcancel(); - /* Wait at least a second for something to happen */ - res = poll(pfds, count, 1000); - pthread_testcancel(); - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); - - /* Okay, poll has finished. Let's see what happened. */ - if (res < 0) { - if ((errno != EAGAIN) && (errno != EINTR)) - ast_log(LOG_WARNING, "poll return %d: %s\n", res, strerror(errno)); - continue; - } - /* Alright, lock the interface list again, and let's look and see what has - happened */ - ast_mutex_lock(&iflock); - found = 0; - spoint = 0; - lastpass = thispass; - thispass = time(NULL); - i = iflist; - while (i) { - if (thispass != lastpass) { - if (!found && ((i == last) || ((i == iflist) && !last))) { - last = i; - if (last) { - if (!last->cidspill && !last->owner && !ast_strlen_zero(last->mailbox) && (thispass - last->onhooktime > 3) && - (last->sig & __DAHDI_SIG_FXO)) { - res = ast_app_has_voicemail(last->mailbox, NULL); - if (last->msgstate != res) { - int x; - ast_log(LOG_DEBUG, "Message status for %s changed from %d to %d on %d\n", last->mailbox, last->msgstate, res, last->channel); - x = DAHDI_FLUSH_BOTH; - res2 = ioctl(last->subs[SUB_REAL].dfd, DAHDI_FLUSH, &x); - if (res2) - ast_log(LOG_WARNING, "Unable to flush input on channel %d: %s\n", last->channel, strerror(errno)); - if ((last->cidspill = ast_calloc(1, MAX_CALLERID_SIZE))) { - /* Turn on on hook transfer for 4 seconds */ - x = 4000; - ioctl(last->subs[SUB_REAL].dfd, DAHDI_ONHOOKTRANSFER, &x); - last->cidlen = vmwi_generate(last->cidspill, res, 1, AST_LAW(last)); - last->cidpos = 0; - last->msgstate = res; - last->onhooktime = thispass; - } - found ++; - } - } - last = last->next; - } - } - } - if ((i->subs[SUB_REAL].dfd > -1) && i->sig) { - if (i->radio && !i->owner) - { - res = dahdi_get_event(i->subs[SUB_REAL].dfd); - if (res) - { - if (option_debug) - ast_log(LOG_DEBUG, "Monitor doohicky got event %s on radio channel %d\n", event2str(res), i->channel); - /* Don't hold iflock while handling init events */ - ast_mutex_unlock(&iflock); - handle_init_event(i, res); - ast_mutex_lock(&iflock); - } - i = i->next; - continue; - } - pollres = ast_fdisset(pfds, i->subs[SUB_REAL].dfd, count, &spoint); - if (pollres & POLLIN) { - if (i->owner || i->subs[SUB_REAL].owner) { -#ifdef HAVE_PRI - if (!i->pri) -#endif - ast_log(LOG_WARNING, "Whoa.... I'm owned but found (%d) in read...\n", i->subs[SUB_REAL].dfd); - i = i->next; - continue; - } - if (!i->cidspill) { - ast_log(LOG_WARNING, "Whoa.... I'm reading but have no cidspill (%d)...\n", i->subs[SUB_REAL].dfd); - i = i->next; - continue; - } - res = read(i->subs[SUB_REAL].dfd, buf, sizeof(buf)); - if (res > 0) { - /* We read some number of bytes. Write an equal amount of data */ - if (res > i->cidlen - i->cidpos) - res = i->cidlen - i->cidpos; - res2 = write(i->subs[SUB_REAL].dfd, i->cidspill + i->cidpos, res); - if (res2 > 0) { - i->cidpos += res2; - if (i->cidpos >= i->cidlen) { - free(i->cidspill); - i->cidspill = 0; - i->cidpos = 0; - i->cidlen = 0; - } - } else { - ast_log(LOG_WARNING, "Write failed: %s\n", strerror(errno)); - i->msgstate = -1; - } - } else { - ast_log(LOG_WARNING, "Read failed with %d: %s\n", res, strerror(errno)); - } - } - if (pollres & POLLPRI) { - if (i->owner || i->subs[SUB_REAL].owner) { -#ifdef HAVE_PRI - if (!i->pri) -#endif - ast_log(LOG_WARNING, "Whoa.... I'm owned but found (%d)...\n", i->subs[SUB_REAL].dfd); - i = i->next; - continue; - } - res = dahdi_get_event(i->subs[SUB_REAL].dfd); - if (option_debug) - ast_log(LOG_DEBUG, "Monitor doohicky got event %s on channel %d\n", event2str(res), i->channel); - /* Don't hold iflock while handling init events */ - ast_mutex_unlock(&iflock); - handle_init_event(i, res); - ast_mutex_lock(&iflock); - } - } - i=i->next; - } - ast_mutex_unlock(&iflock); - } - /* Never reached */ - return NULL; - -} - -static int restart_monitor(void) -{ - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - /* If we're supposed to be stopped -- stay stopped */ - if (monitor_thread == AST_PTHREADT_STOP) - return 0; - ast_mutex_lock(&monlock); - if (monitor_thread == pthread_self()) { - ast_mutex_unlock(&monlock); - ast_log(LOG_WARNING, "Cannot kill myself\n"); - return -1; - } - if (monitor_thread != AST_PTHREADT_NULL) { - /* Wake up the thread */ - pthread_kill(monitor_thread, SIGURG); - } else { - /* Start a new monitor */ - if (ast_pthread_create_background(&monitor_thread, &attr, do_monitor, NULL) < 0) { - ast_mutex_unlock(&monlock); - ast_log(LOG_ERROR, "Unable to start monitor thread.\n"); - pthread_attr_destroy(&attr); - return -1; - } - } - ast_mutex_unlock(&monlock); - pthread_attr_destroy(&attr); - return 0; -} - -#ifdef HAVE_PRI -static int pri_resolve_span(int *span, int channel, int offset, struct dahdi_spaninfo *si) -{ - int x; - int trunkgroup; - /* Get appropriate trunk group if there is one */ - trunkgroup = pris[*span].mastertrunkgroup; - if (trunkgroup) { - /* Select a specific trunk group */ - for (x = 0; x < NUM_SPANS; x++) { - if (pris[x].trunkgroup == trunkgroup) { - *span = x; - return 0; - } - } - ast_log(LOG_WARNING, "Channel %d on span %d configured to use nonexistent trunk group %d\n", channel, *span, trunkgroup); - *span = -1; - } else { - if (pris[*span].trunkgroup) { - ast_log(LOG_WARNING, "Unable to use span %d implicitly since it is trunk group %d (please use spanmap)\n", *span, pris[*span].trunkgroup); - *span = -1; - } else if (pris[*span].mastertrunkgroup) { - ast_log(LOG_WARNING, "Unable to use span %d implicitly since it is already part of trunk group %d\n", *span, pris[*span].mastertrunkgroup); - *span = -1; - } else { - if (si->totalchans == 31) { - /* E1 */ - pris[*span].dchannels[0] = 16 + offset; - } else if (si->totalchans == 24) { - /* T1 or J1 */ - pris[*span].dchannels[0] = 24 + offset; - } else if (si->totalchans == 3) { - /* BRI */ - pris[*span].dchannels[0] = 3 + offset; - } else { - ast_log(LOG_WARNING, "Unable to use span %d, since the D-channel cannot be located (unexpected span size of %d channels)\n", *span, si->totalchans); - *span = -1; - return 0; - } - pris[*span].dchanavail[0] |= DCHAN_PROVISIONED; - pris[*span].offset = offset; - pris[*span].span = *span + 1; - } - } - return 0; -} - -static int pri_create_trunkgroup(int trunkgroup, int *channels) -{ - struct dahdi_spaninfo si; - struct dahdi_params p; - int fd; - int span; - int ospan=0; - int x,y; - for (x = 0; x < NUM_SPANS; x++) { - if (pris[x].trunkgroup == trunkgroup) { - ast_log(LOG_WARNING, "Trunk group %d already exists on span %d, Primary d-channel %d\n", trunkgroup, x + 1, pris[x].dchannels[0]); - return -1; - } - } - for (y = 0; y < NUM_DCHANS; y++) { - if (!channels[y]) - break; - memset(&si, 0, sizeof(si)); - memset(&p, 0, sizeof(p)); - fd = open(DAHDI_FILE_CHANNEL, O_RDWR); - if (fd < 0) { - ast_log(LOG_WARNING, "Failed to open channel: %s\n", strerror(errno)); - return -1; - } - x = channels[y]; - if (ioctl(fd, DAHDI_SPECIFY, &x)) { - ast_log(LOG_WARNING, "Failed to specify channel %d: %s\n", channels[y], strerror(errno)); - close(fd); - return -1; - } - if (ioctl(fd, DAHDI_GET_PARAMS, &p)) { - ast_log(LOG_WARNING, "Failed to get channel parameters for channel %d: %s\n", channels[y], strerror(errno)); - return -1; - } - if (ioctl(fd, DAHDI_SPANSTAT, &si)) { - ast_log(LOG_WARNING, "Failed go get span information on channel %d (span %d): %s\n", channels[y], p.spanno, strerror(errno)); - close(fd); - return -1; - } - span = p.spanno - 1; - if (pris[span].trunkgroup) { - ast_log(LOG_WARNING, "Span %d is already provisioned for trunk group %d\n", span + 1, pris[span].trunkgroup); - close(fd); - return -1; - } - if (pris[span].pvts[0]) { - ast_log(LOG_WARNING, "Span %d is already provisioned with channels (implicit PRI maybe?)\n", span + 1); - close(fd); - return -1; - } - if (!y) { - pris[span].trunkgroup = trunkgroup; - pris[span].offset = channels[y] - p.chanpos; - ospan = span; - } - pris[ospan].dchannels[y] = channels[y]; - pris[ospan].dchanavail[y] |= DCHAN_PROVISIONED; - pris[span].span = span + 1; - close(fd); - } - return 0; -} - -static int pri_create_spanmap(int span, int trunkgroup, int logicalspan) -{ - if (pris[span].mastertrunkgroup) { - ast_log(LOG_WARNING, "Span %d is already part of trunk group %d, cannot add to trunk group %d\n", span + 1, pris[span].mastertrunkgroup, trunkgroup); - return -1; - } - pris[span].mastertrunkgroup = trunkgroup; - pris[span].prilogicalspan = logicalspan; - return 0; -} - -#endif - -static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, struct dahdi_pri *pri, int reloading) -{ - /* Make a dahdi_pvt structure for this interface (or CRV if "pri" is specified) */ - struct dahdi_pvt *tmp = NULL, *tmp2, *prev = NULL; - char fn[80]; -#if 1 - struct dahdi_bufferinfo bi; -#endif - int res; - int span=0; - int here = 0; - int x; - struct dahdi_pvt **wlist; - struct dahdi_pvt **wend; - struct dahdi_params p; - - wlist = &iflist; - wend = &ifend; - -#ifdef HAVE_PRI - if (pri) { - wlist = &pri->crvs; - wend = &pri->crvend; - } -#endif - - tmp2 = *wlist; - prev = NULL; - - while (tmp2) { - if (!tmp2->destroy) { - if (tmp2->channel == channel) { - tmp = tmp2; - here = 1; - break; - } - if (tmp2->channel > channel) { - break; - } - } - prev = tmp2; - tmp2 = tmp2->next; - } - - if (!here && reloading != 1) { - if (!(tmp = ast_calloc(1, sizeof(*tmp)))) { - if (tmp) - free(tmp); - return NULL; - } - ast_mutex_init(&tmp->lock); - ifcount++; - for (x = 0; x < 3; x++) - tmp->subs[x].dfd = -1; - tmp->channel = channel; - } - - if (tmp) { - int chan_sig = conf->chan.sig; - if (!here) { - if ((channel != CHAN_PSEUDO) && !pri) { - int count = 0; - snprintf(fn, sizeof(fn), "%d", channel); - /* Open non-blocking */ - tmp->subs[SUB_REAL].dfd = dahdi_open(fn); - while (tmp->subs[SUB_REAL].dfd < 0 && reloading == 2 && count < 1000) { /* the kernel may not call dahdi_release fast enough for the open flagbit to be cleared in time */ - usleep(1); - tmp->subs[SUB_REAL].dfd = dahdi_open(fn); - count++; - } - /* Allocate a DAHDI structure */ - if (tmp->subs[SUB_REAL].dfd < 0) { - ast_log(LOG_ERROR, "Unable to open channel %d: %s\nhere = %d, tmp->channel = %d, channel = %d\n", channel, strerror(errno), here, tmp->channel, channel); - destroy_dahdi_pvt(&tmp); - return NULL; - } - memset(&p, 0, sizeof(p)); - res = ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &p); - if (res < 0) { - ast_log(LOG_ERROR, "Unable to get parameters: %s\n", strerror(errno)); - destroy_dahdi_pvt(&tmp); - return NULL; - } - if (p.sigtype != (conf->chan.sig & 0x3ffff)) { - ast_log(LOG_ERROR, "Signalling requested on channel %d is %s but line is in %s signalling\n", channel, sig2str(conf->chan.sig), sig2str(p.sigtype)); - destroy_dahdi_pvt(&tmp); - return NULL; - } - tmp->law = p.curlaw; - tmp->span = p.spanno; - span = p.spanno - 1; - } else { - if (channel == CHAN_PSEUDO) - chan_sig = 0; - else if ((chan_sig != SIG_FXOKS) && (chan_sig != SIG_FXSKS)) { - ast_log(LOG_ERROR, "CRV's must use FXO/FXS Kewl Start (fxo_ks/fxs_ks) signalling only.\n"); - return NULL; - } - } -#ifdef HAVE_PRI - if ((chan_sig == SIG_PRI) || (chan_sig == SIG_GR303FXOKS) || (chan_sig == SIG_GR303FXSKS)) { - int offset; - int myswitchtype; - int matchesdchan; - int x,y; - offset = 0; - if ((chan_sig == SIG_PRI) && ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &offset)) { - ast_log(LOG_ERROR, "Unable to set clear mode on clear channel %d of span %d: %s\n", channel, p.spanno, strerror(errno)); - destroy_dahdi_pvt(&tmp); - return NULL; - } - if (span >= NUM_SPANS) { - ast_log(LOG_ERROR, "Channel %d does not lie on a span I know of (%d)\n", channel, span); - destroy_dahdi_pvt(&tmp); - return NULL; - } else { - struct dahdi_spaninfo si; - si.spanno = 0; - if (ioctl(tmp->subs[SUB_REAL].dfd,DAHDI_SPANSTAT,&si) == -1) { - ast_log(LOG_ERROR, "Unable to get span status: %s\n", strerror(errno)); - destroy_dahdi_pvt(&tmp); - return NULL; - } - /* Store the logical span first based upon the real span */ - tmp->logicalspan = pris[span].prilogicalspan; - pri_resolve_span(&span, channel, (channel - p.chanpos), &si); - if (span < 0) { - ast_log(LOG_WARNING, "Channel %d: Unable to find locate channel/trunk group!\n", channel); - destroy_dahdi_pvt(&tmp); - return NULL; - } - if (chan_sig == SIG_PRI) - myswitchtype = conf->pri.switchtype; - else - myswitchtype = PRI_SWITCH_GR303_TMC; - /* Make sure this isn't a d-channel */ - matchesdchan=0; - for (x = 0; x < NUM_SPANS; x++) { - for (y = 0; y < NUM_DCHANS; y++) { - if (pris[x].dchannels[y] == tmp->channel) { - matchesdchan = 1; - break; - } - } - } - offset = p.chanpos; - if (!matchesdchan) { - if (pris[span].nodetype && (pris[span].nodetype != conf->pri.nodetype)) { - ast_log(LOG_ERROR, "Span %d is already a %s node\n", span + 1, pri_node2str(pris[span].nodetype)); - destroy_dahdi_pvt(&tmp); - return NULL; - } - if (pris[span].switchtype && (pris[span].switchtype != myswitchtype)) { - ast_log(LOG_ERROR, "Span %d is already a %s switch\n", span + 1, pri_switch2str(pris[span].switchtype)); - destroy_dahdi_pvt(&tmp); - return NULL; - } - if ((pris[span].dialplan) && (pris[span].dialplan != conf->pri.dialplan)) { - ast_log(LOG_ERROR, "Span %d is already a %s dialing plan\n", span + 1, dialplan2str(pris[span].dialplan)); - destroy_dahdi_pvt(&tmp); - return NULL; - } - if (!ast_strlen_zero(pris[span].idledial) && strcmp(pris[span].idledial, conf->pri.idledial)) { - ast_log(LOG_ERROR, "Span %d already has idledial '%s'.\n", span + 1, conf->pri.idledial); - destroy_dahdi_pvt(&tmp); - return NULL; - } - if (!ast_strlen_zero(pris[span].idleext) && strcmp(pris[span].idleext, conf->pri.idleext)) { - ast_log(LOG_ERROR, "Span %d already has idleext '%s'.\n", span + 1, conf->pri.idleext); - destroy_dahdi_pvt(&tmp); - return NULL; - } - if (pris[span].minunused && (pris[span].minunused != conf->pri.minunused)) { - ast_log(LOG_ERROR, "Span %d already has minunused of %d.\n", span + 1, conf->pri.minunused); - destroy_dahdi_pvt(&tmp); - return NULL; - } - if (pris[span].minidle && (pris[span].minidle != conf->pri.minidle)) { - ast_log(LOG_ERROR, "Span %d already has minidle of %d.\n", span + 1, conf->pri.minidle); - destroy_dahdi_pvt(&tmp); - return NULL; - } - if (pris[span].numchans >= MAX_CHANNELS) { - ast_log(LOG_ERROR, "Unable to add channel %d: Too many channels in trunk group %d!\n", channel, - pris[span].trunkgroup); - destroy_dahdi_pvt(&tmp); - return NULL; - } - pris[span].nodetype = conf->pri.nodetype; - pris[span].switchtype = myswitchtype; - pris[span].nsf = conf->pri.nsf; - pris[span].dialplan = conf->pri.dialplan; - pris[span].localdialplan = conf->pri.localdialplan; - pris[span].pvts[pris[span].numchans++] = tmp; - pris[span].minunused = conf->pri.minunused; - pris[span].minidle = conf->pri.minidle; - pris[span].overlapdial = conf->pri.overlapdial; -#ifdef HAVE_PRI_INBANDDISCONNECT - pris[span].inbanddisconnect = conf->pri.inbanddisconnect; -#endif - pris[span].facilityenable = conf->pri.facilityenable; - ast_copy_string(pris[span].idledial, conf->pri.idledial, sizeof(pris[span].idledial)); - ast_copy_string(pris[span].idleext, conf->pri.idleext, sizeof(pris[span].idleext)); - ast_copy_string(pris[span].internationalprefix, conf->pri.internationalprefix, sizeof(pris[span].internationalprefix)); - ast_copy_string(pris[span].nationalprefix, conf->pri.nationalprefix, sizeof(pris[span].nationalprefix)); - ast_copy_string(pris[span].localprefix, conf->pri.localprefix, sizeof(pris[span].localprefix)); - ast_copy_string(pris[span].privateprefix, conf->pri.privateprefix, sizeof(pris[span].privateprefix)); - ast_copy_string(pris[span].unknownprefix, conf->pri.unknownprefix, sizeof(pris[span].unknownprefix)); - pris[span].resetinterval = conf->pri.resetinterval; - - tmp->pri = &pris[span]; - tmp->prioffset = offset; - tmp->call = NULL; - } else { - ast_log(LOG_ERROR, "Channel %d is reserved for D-channel.\n", offset); - destroy_dahdi_pvt(&tmp); - return NULL; - } - } - } else { - tmp->prioffset = 0; - } -#endif - } else { - chan_sig = tmp->sig; - memset(&p, 0, sizeof(p)); - if (tmp->subs[SUB_REAL].dfd > -1) - res = ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &p); - } - /* Adjust starttime on loopstart and kewlstart trunks to reasonable values */ - switch (chan_sig) { - case SIG_FXSKS: - case SIG_FXSLS: - case SIG_EM: - case SIG_EM_E1: - case SIG_EMWINK: - case SIG_FEATD: - case SIG_FEATDMF: - case SIG_FEATDMF_TA: - case SIG_FEATB: - case SIG_E911: - case SIG_SF: - case SIG_SFWINK: - case SIG_FGC_CAMA: - case SIG_FGC_CAMAMF: - case SIG_SF_FEATD: - case SIG_SF_FEATDMF: - case SIG_SF_FEATB: - p.starttime = 250; - break; - } - - if (tmp->radio) { - /* XXX Waiting to hear back from Jim if these should be adjustable XXX */ - p.channo = channel; - p.rxwinktime = 1; - p.rxflashtime = 1; - p.starttime = 1; - p.debouncetime = 5; - } - if (!tmp->radio) { - p.channo = channel; - /* Override timing settings based on config file */ - if (conf->timing.prewinktime >= 0) - p.prewinktime = conf->timing.prewinktime; - if (conf->timing.preflashtime >= 0) - p.preflashtime = conf->timing.preflashtime; - if (conf->timing.winktime >= 0) - p.winktime = conf->timing.winktime; - if (conf->timing.flashtime >= 0) - p.flashtime = conf->timing.flashtime; - if (conf->timing.starttime >= 0) - p.starttime = conf->timing.starttime; - if (conf->timing.rxwinktime >= 0) - p.rxwinktime = conf->timing.rxwinktime; - if (conf->timing.rxflashtime >= 0) - p.rxflashtime = conf->timing.rxflashtime; - if (conf->timing.debouncetime >= 0) - p.debouncetime = conf->timing.debouncetime; - } - - /* dont set parms on a pseudo-channel (or CRV) */ - if (tmp->subs[SUB_REAL].dfd >= 0) - { - res = ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_SET_PARAMS, &p); - if (res < 0) { - ast_log(LOG_ERROR, "Unable to set parameters: %s\n", strerror(errno)); - destroy_dahdi_pvt(&tmp); - return NULL; - } - } -#if 1 - if (!here && (tmp->subs[SUB_REAL].dfd > -1)) { - memset(&bi, 0, sizeof(bi)); - res = ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_GET_BUFINFO, &bi); - if (!res) { - bi.txbufpolicy = conf->chan.buf_policy; - bi.rxbufpolicy = conf->chan.buf_policy; - bi.numbufs = conf->chan.buf_no; - res = ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_SET_BUFINFO, &bi); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to set buffer policy on channel %d: %s\n", channel, strerror(errno)); - } - } else - ast_log(LOG_WARNING, "Unable to check buffer policy on channel %d: %s\n", channel, strerror(errno)); - } -#endif - tmp->immediate = conf->chan.immediate; - tmp->transfertobusy = conf->chan.transfertobusy; - tmp->sig = chan_sig; - tmp->outsigmod = conf->chan.outsigmod; - tmp->ringt_base = ringt_base; - tmp->firstradio = 0; - if ((chan_sig == SIG_FXOKS) || (chan_sig == SIG_FXOLS) || (chan_sig == SIG_FXOGS)) - tmp->permcallwaiting = conf->chan.callwaiting; - else - tmp->permcallwaiting = 0; - /* Flag to destroy the channel must be cleared on new mkif. Part of changes for reload to work */ - tmp->destroy = 0; - tmp->drings = drings; - tmp->usedistinctiveringdetection = conf->chan.usedistinctiveringdetection; - tmp->callwaitingcallerid = conf->chan.callwaitingcallerid; - tmp->threewaycalling = conf->chan.threewaycalling; - tmp->adsi = conf->chan.adsi; - tmp->use_smdi = conf->chan.use_smdi; - tmp->permhidecallerid = conf->chan.hidecallerid; - tmp->callreturn = conf->chan.callreturn; - tmp->echocancel = conf->chan.echocancel; - tmp->echotraining = conf->chan.echotraining; - tmp->pulse = conf->chan.pulse; - if (tmp->echocancel) - tmp->echocanbridged = conf->chan.echocanbridged; - else { - if (conf->chan.echocanbridged) - ast_log(LOG_NOTICE, "echocancelwhenbridged requires echocancel to be enabled; ignoring\n"); - tmp->echocanbridged = 0; - } - tmp->busydetect = conf->chan.busydetect; - tmp->busycount = conf->chan.busycount; - tmp->busy_tonelength = conf->chan.busy_tonelength; - tmp->busy_quietlength = conf->chan.busy_quietlength; - tmp->callprogress = conf->chan.callprogress; - tmp->cancallforward = conf->chan.cancallforward; - tmp->dtmfrelax = conf->chan.dtmfrelax; - tmp->callwaiting = tmp->permcallwaiting; - tmp->hidecallerid = tmp->permhidecallerid; - tmp->channel = channel; - tmp->stripmsd = conf->chan.stripmsd; - tmp->use_callerid = conf->chan.use_callerid; - tmp->cid_signalling = conf->chan.cid_signalling; - tmp->cid_start = conf->chan.cid_start; - tmp->dahditrcallerid = conf->chan.dahditrcallerid; - tmp->restrictcid = conf->chan.restrictcid; - tmp->use_callingpres = conf->chan.use_callingpres; - tmp->priindication_oob = conf->chan.priindication_oob; - tmp->priexclusive = conf->chan.priexclusive; - if (tmp->usedistinctiveringdetection) { - if (!tmp->use_callerid) { - ast_log(LOG_NOTICE, "Distinctive Ring detect requires 'usecallerid' be on\n"); - tmp->use_callerid = 1; - } - } - - if (tmp->cid_signalling == CID_SIG_SMDI) { - if (!tmp->use_smdi) { - ast_log(LOG_WARNING, "SMDI callerid requires SMDI to be enabled, enabling...\n"); - tmp->use_smdi = 1; - } - } - if (tmp->use_smdi) { - tmp->smdi_iface = ast_smdi_interface_find(conf->smdi_port); - if (!(tmp->smdi_iface)) { - ast_log(LOG_ERROR, "Invalid SMDI port specfied, disabling SMDI support\n"); - tmp->use_smdi = 0; - } - } - - ast_copy_string(tmp->accountcode, conf->chan.accountcode, sizeof(tmp->accountcode)); - tmp->amaflags = conf->chan.amaflags; - if (!here) { - tmp->confno = -1; - tmp->propconfno = -1; - } - tmp->canpark = conf->chan.canpark; - tmp->transfer = conf->chan.transfer; - ast_copy_string(tmp->defcontext,conf->chan.context,sizeof(tmp->defcontext)); - ast_copy_string(tmp->language, conf->chan.language, sizeof(tmp->language)); - ast_copy_string(tmp->mohinterpret, conf->chan.mohinterpret, sizeof(tmp->mohinterpret)); - ast_copy_string(tmp->mohsuggest, conf->chan.mohsuggest, sizeof(tmp->mohsuggest)); - ast_copy_string(tmp->context, conf->chan.context, sizeof(tmp->context)); - ast_copy_string(tmp->cid_num, conf->chan.cid_num, sizeof(tmp->cid_num)); - tmp->cid_ton = 0; - ast_copy_string(tmp->cid_name, conf->chan.cid_name, sizeof(tmp->cid_name)); - ast_copy_string(tmp->mailbox, conf->chan.mailbox, sizeof(tmp->mailbox)); - tmp->msgstate = -1; - tmp->group = conf->chan.group; - tmp->callgroup = conf->chan.callgroup; - tmp->pickupgroup= conf->chan.pickupgroup; - tmp->rxgain = conf->chan.rxgain; - tmp->txgain = conf->chan.txgain; - tmp->tonezone = conf->chan.tonezone; - tmp->onhooktime = time(NULL); - if (tmp->subs[SUB_REAL].dfd > -1) { - set_actual_gain(tmp->subs[SUB_REAL].dfd, 0, tmp->rxgain, tmp->txgain, tmp->law); - if (tmp->dsp) - ast_dsp_digitmode(tmp->dsp, DSP_DIGITMODE_DTMF | tmp->dtmfrelax); - update_conf(tmp); - if (!here) { - if (chan_sig != SIG_PRI) - /* Hang it up to be sure it's good */ - dahdi_set_hook(tmp->subs[SUB_REAL].dfd, DAHDI_ONHOOK); - } - ioctl(tmp->subs[SUB_REAL].dfd,DAHDI_SETTONEZONE,&tmp->tonezone); -#ifdef HAVE_PRI - /* the dchannel is down so put the channel in alarm */ - if (tmp->pri && !pri_is_up(tmp->pri)) { - tmp->inalarm = 1; - } -#endif - if ((res = get_alarms(tmp)) != DAHDI_ALARM_NONE) { - tmp->inalarm = 1; - handle_alarms(tmp, res); - } else { - /* yes, this looks strange... the unknown_alarm flag is only used to - control whether an 'alarm cleared' message gets generated when we - get an indication that the channel is no longer in alarm status. - however, the channel *could* be in an alarm status that we aren't - aware of (since get_alarms() only reports span alarms, not channel - alarms). setting this flag will cause any potential 'alarm cleared' - message to be suppressed, but if a real alarm occurs before that - happens, this flag will get cleared by it and the situation will - be normal. - */ - tmp->unknown_alarm = 1; - } - } - - tmp->polarityonanswerdelay = conf->chan.polarityonanswerdelay; - tmp->answeronpolarityswitch = conf->chan.answeronpolarityswitch; - tmp->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch; - tmp->sendcalleridafter = conf->chan.sendcalleridafter; - - } - if (tmp && !here) { - /* nothing on the iflist */ - if (!*wlist) { - *wlist = tmp; - tmp->prev = NULL; - tmp->next = NULL; - *wend = tmp; - } else { - /* at least one member on the iflist */ - struct dahdi_pvt *working = *wlist; - - /* check if we maybe have to put it on the begining */ - if (working->channel > tmp->channel) { - tmp->next = *wlist; - tmp->prev = NULL; - (*wlist)->prev = tmp; - *wlist = tmp; - } else { - /* go through all the members and put the member in the right place */ - while (working) { - /* in the middle */ - if (working->next) { - if (working->channel < tmp->channel && working->next->channel > tmp->channel) { - tmp->next = working->next; - tmp->prev = working; - working->next->prev = tmp; - working->next = tmp; - break; - } - } else { - /* the last */ - if (working->channel < tmp->channel) { - working->next = tmp; - tmp->next = NULL; - tmp->prev = working; - *wend = tmp; - break; - } - } - working = working->next; - } - } - } - } - return tmp; -} - -static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched) -{ - int res; - struct dahdi_params par; - - /* First, check group matching */ - if (groupmatch) { - if ((p->group & groupmatch) != groupmatch) - return 0; - *groupmatched = 1; - } - /* Check to see if we have a channel match */ - if (channelmatch != -1) { - if (p->channel != channelmatch) - return 0; - *channelmatched = 1; - } - /* We're at least busy at this point */ - if (busy) { - if ((p->sig == SIG_FXOKS) || (p->sig == SIG_FXOLS) || (p->sig == SIG_FXOGS)) - *busy = 1; - } - /* If do not disturb, definitely not */ - if (p->dnd) - return 0; - /* If guard time, definitely not */ - if (p->guardtime && (time(NULL) < p->guardtime)) - return 0; - - /* If no owner definitely available */ - if (!p->owner) { -#ifdef HAVE_PRI - /* Trust PRI */ - if (p->pri) { - if (p->resetting || p->call) - return 0; - else - return 1; - } -#endif - if (!(p->radio || (p->oprmode < 0))) - { - if (!p->sig || (p->sig == SIG_FXSLS)) - return 1; - /* Check hook state */ - if (p->subs[SUB_REAL].dfd > -1) - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par); - else { - /* Assume not off hook on CVRS */ - res = 0; - par.rxisoffhook = 0; - } - if (res) { - ast_log(LOG_WARNING, "Unable to check hook state on channel %d: %s\n", p->channel, strerror(errno)); - } else if ((p->sig == SIG_FXSKS) || (p->sig == SIG_FXSGS)) { - /* When "onhook" that means no battery on the line, and thus - it is out of service..., if it's on a TDM card... If it's a channel - bank, there is no telling... */ - if (par.rxbits > -1) - return 1; - if (par.rxisoffhook) - return 1; - else -#ifdef DAHDI_CHECK_HOOKSTATE - return 0; -#else - return 1; -#endif - } else if (par.rxisoffhook) { - ast_log(LOG_DEBUG, "Channel %d off hook, can't use\n", p->channel); - /* Not available when the other end is off hook */ - return 0; - } - } - return 1; - } - - /* If it's not an FXO, forget about call wait */ - if ((p->sig != SIG_FXOKS) && (p->sig != SIG_FXOLS) && (p->sig != SIG_FXOGS)) - return 0; - - if (!p->callwaiting) { - /* If they don't have call waiting enabled, then for sure they're unavailable at this point */ - return 0; - } - - if (p->subs[SUB_CALLWAIT].dfd > -1) { - /* If there is already a call waiting call, then we can't take a second one */ - return 0; - } - - if ((p->owner->_state != AST_STATE_UP) && - ((p->owner->_state != AST_STATE_RINGING) || p->outgoing)) { - /* If the current call is not up, then don't allow the call */ - return 0; - } - if ((p->subs[SUB_THREEWAY].owner) && (!p->subs[SUB_THREEWAY].inthreeway)) { - /* Can't take a call wait when the three way calling hasn't been merged yet. */ - return 0; - } - /* We're cool */ - return 1; -} - -static struct dahdi_pvt *chandup(struct dahdi_pvt *src) -{ - struct dahdi_pvt *p; - struct dahdi_bufferinfo bi; - int res; - - if ((p = ast_malloc(sizeof(*p)))) { - memcpy(p, src, sizeof(struct dahdi_pvt)); - ast_mutex_init(&p->lock); - p->subs[SUB_REAL].dfd = dahdi_open(DAHDI_FILE_PSEUDO); - /* Allocate a DAHDI structure */ - if (p->subs[SUB_REAL].dfd < 0) { - ast_log(LOG_ERROR, "Unable to dup channel: %s\n", strerror(errno)); - destroy_dahdi_pvt(&p); - return NULL; - } - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_BUFINFO, &bi); - if (!res) { - bi.txbufpolicy = p->buf_policy; - bi.rxbufpolicy = p->buf_policy; - bi.numbufs = p->buf_no; - res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SET_BUFINFO, &bi); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to set buffer policy on dup channel: %s\n", strerror(errno)); - } - } else - ast_log(LOG_WARNING, "Unable to check buffer policy on dup channel: %s\n", strerror(errno)); - } - p->destroy = 1; - p->next = iflist; - p->prev = NULL; - iflist = p; - if (iflist->next) - iflist->next->prev = p; - return p; -} - - -#ifdef HAVE_PRI -static int pri_find_empty_chan(struct dahdi_pri *pri, int backwards) -{ - int x; - if (backwards) - x = pri->numchans; - else - x = 0; - for (;;) { - if (backwards && (x < 0)) - break; - if (!backwards && (x >= pri->numchans)) - break; - if (pri->pvts[x] && !pri->pvts[x]->inalarm && !pri->pvts[x]->owner) { - ast_log(LOG_DEBUG, "Found empty available channel %d/%d\n", - pri->pvts[x]->logicalspan, pri->pvts[x]->prioffset); - return x; - } - if (backwards) - x--; - else - x++; - } - return -1; -} -#endif - -static struct ast_channel *dahdi_request(const char *type, int format, void *data, int *cause) -{ - ast_group_t groupmatch = 0; - int channelmatch = -1; - int roundrobin = 0; - int callwait = 0; - int busy = 0; - struct dahdi_pvt *p; - struct ast_channel *tmp = NULL; - char *dest=NULL; - int x; - char *s; - char opt=0; - int res=0, y=0; - int backwards = 0; -#ifdef HAVE_PRI - int crv; - int bearer = -1; - int trunkgroup; - struct dahdi_pri *pri=NULL; -#endif - struct dahdi_pvt *exit, *start, *end; - ast_mutex_t *lock; - int channelmatched = 0; - int groupmatched = 0; - - /* Assume we're locking the iflock */ - lock = &iflock; - start = iflist; - end = ifend; - if (data) { - dest = ast_strdupa((char *)data); - } else { - ast_log(LOG_WARNING, "Channel requested with no data\n"); - return NULL; - } - if (toupper(dest[0]) == 'G' || toupper(dest[0])=='R') { - /* Retrieve the group number */ - char *stringp=NULL; - stringp=dest + 1; - s = strsep(&stringp, "/"); - if ((res = sscanf(s, "%d%c%d", &x, &opt, &y)) < 1) { - ast_log(LOG_WARNING, "Unable to determine group for data %s\n", (char *)data); - return NULL; - } - groupmatch = ((ast_group_t) 1 << x); - if (toupper(dest[0]) == 'G') { - if (dest[0] == 'G') { - backwards = 1; - p = ifend; - } else - p = iflist; - } else { - if (dest[0] == 'R') { - backwards = 1; - p = round_robin[x]?round_robin[x]->prev:ifend; - if (!p) - p = ifend; - } else { - p = round_robin[x]?round_robin[x]->next:iflist; - if (!p) - p = iflist; - } - roundrobin = 1; - } - } else { - char *stringp=NULL; - stringp=dest; - s = strsep(&stringp, "/"); - p = iflist; - if (!strcasecmp(s, "pseudo")) { - /* Special case for pseudo */ - x = CHAN_PSEUDO; - channelmatch = x; - } -#ifdef HAVE_PRI - else if ((res = sscanf(s, "%d:%d%c%d", &trunkgroup, &crv, &opt, &y)) > 1) { - if ((trunkgroup < 1) || (crv < 1)) { - ast_log(LOG_WARNING, "Unable to determine trunk group and CRV for data %s\n", (char *)data); - return NULL; - } - res--; - for (x = 0; x < NUM_SPANS; x++) { - if (pris[x].trunkgroup == trunkgroup) { - pri = pris + x; - lock = &pri->lock; - start = pri->crvs; - end = pri->crvend; - break; - } - } - if (!pri) { - ast_log(LOG_WARNING, "Unable to find trunk group %d\n", trunkgroup); - return NULL; - } - channelmatch = crv; - p = pris[x].crvs; - } -#endif - else if ((res = sscanf(s, "%d%c%d", &x, &opt, &y)) < 1) { - ast_log(LOG_WARNING, "Unable to determine channel for data %s\n", (char *)data); - return NULL; - } else { - channelmatch = x; - } - } - /* Search for an unowned channel */ - ast_mutex_lock(lock); - exit = p; - while (p && !tmp) { - if (roundrobin) - round_robin[x] = p; -#if 0 - ast_verbose("name = %s, %d, %d, %d\n",p->owner ? p->owner->name : "<none>", p->channel, channelmatch, groupmatch); -#endif - - if (p && available(p, channelmatch, groupmatch, &busy, &channelmatched, &groupmatched)) { - if (option_debug) - ast_log(LOG_DEBUG, "Using channel %d\n", p->channel); - if (p->inalarm) - goto next; - - callwait = (p->owner != NULL); -#ifdef HAVE_PRI - if (pri && (p->subs[SUB_REAL].dfd < 0)) { - if (p->sig != SIG_FXSKS) { - /* Gotta find an actual channel to use for this - CRV if this isn't a callwait */ - bearer = pri_find_empty_chan(pri, 0); - if (bearer < 0) { - ast_log(LOG_NOTICE, "Out of bearer channels on span %d for call to CRV %d:%d\n", pri->span, trunkgroup, crv); - p = NULL; - break; - } - pri_assign_bearer(p, pri, pri->pvts[bearer]); - } else { - if (alloc_sub(p, 0)) { - ast_log(LOG_NOTICE, "Failed to allocate place holder pseudo channel!\n"); - p = NULL; - break; - } else - ast_log(LOG_DEBUG, "Allocated placeholder pseudo channel\n"); - p->pri = pri; - } - } -#endif - if (p->channel == CHAN_PSEUDO) { - p = chandup(p); - if (!p) { - break; - } - } - if (p->owner) { - if (alloc_sub(p, SUB_CALLWAIT)) { - p = NULL; - break; - } - } - p->outgoing = 1; - tmp = dahdi_new(p, AST_STATE_RESERVED, 0, p->owner ? SUB_CALLWAIT : SUB_REAL, 0, 0); -#ifdef HAVE_PRI - if (p->bearer) { - /* Log owner to bearer channel, too */ - p->bearer->owner = tmp; - } -#endif - /* Make special notes */ - if (res > 1) { - if (opt == 'c') { - /* Confirm answer */ - p->confirmanswer = 1; - } else if (opt == 'r') { - /* Distinctive ring */ - if (res < 3) - ast_log(LOG_WARNING, "Distinctive ring missing identifier in '%s'\n", (char *)data); - else - p->distinctivering = y; - } else if (opt == 'd') { - /* If this is an ISDN call, make it digital */ - p->digital = 1; - if (tmp) - tmp->transfercapability = AST_TRANS_CAP_DIGITAL; - } else { - ast_log(LOG_WARNING, "Unknown option '%c' in '%s'\n", opt, (char *)data); - } - } - /* Note if the call is a call waiting call */ - if (tmp && callwait) - tmp->cdrflags |= AST_CDR_CALLWAIT; - break; - } -next: - if (backwards) { - p = p->prev; - if (!p) - p = end; - } else { - p = p->next; - if (!p) - p = start; - } - /* stop when you roll to the one that we started from */ - if (p == exit) - break; - } - ast_mutex_unlock(lock); - restart_monitor(); - if (callwait) - *cause = AST_CAUSE_BUSY; - else if (!tmp) { - if (channelmatched) { - if (busy) - *cause = AST_CAUSE_BUSY; - } else if (groupmatched) { - *cause = AST_CAUSE_CONGESTION; - } - } - - return tmp; -} - - -#ifdef HAVE_PRI -static struct dahdi_pvt *pri_find_crv(struct dahdi_pri *pri, int crv) -{ - struct dahdi_pvt *p; - p = pri->crvs; - while (p) { - if (p->channel == crv) - return p; - p = p->next; - } - return NULL; -} - - -static int pri_find_principle(struct dahdi_pri *pri, int channel) -{ - int x; - int span = PRI_SPAN(channel); - int spanfd; - struct dahdi_params param; - int principle = -1; - int explicit = PRI_EXPLICIT(channel); - channel = PRI_CHANNEL(channel); - - if (!explicit) { - spanfd = pri_active_dchan_fd(pri); - if (ioctl(spanfd, DAHDI_GET_PARAMS, ¶m)) - return -1; - span = pris[param.spanno - 1].prilogicalspan; - } - - for (x = 0; x < pri->numchans; x++) { - if (pri->pvts[x] && (pri->pvts[x]->prioffset == channel) && (pri->pvts[x]->logicalspan == span)) { - principle = x; - break; - } - } - - return principle; -} - -static int pri_fixup_principle(struct dahdi_pri *pri, int principle, q931_call *c) -{ - int x; - struct dahdi_pvt *crv; - if (!c) { - if (principle < 0) - return -1; - return principle; - } - if ((principle > -1) && - (principle < pri->numchans) && - (pri->pvts[principle]) && - (pri->pvts[principle]->call == c)) - return principle; - /* First, check for other bearers */ - for (x = 0; x < pri->numchans; x++) { - if (!pri->pvts[x]) - continue; - if (pri->pvts[x]->call == c) { - /* Found our call */ - if (principle != x) { - struct dahdi_pvt *new = pri->pvts[principle], *old = pri->pvts[x]; - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Moving call from channel %d to channel %d\n", - old->channel, new->channel); - if (new->owner) { - ast_log(LOG_WARNING, "Can't fix up channel from %d to %d because %d is already in use\n", - old->channel, new->channel, new->channel); - return -1; - } - /* Fix it all up now */ - new->owner = old->owner; - old->owner = NULL; - if (new->owner) { - ast_string_field_build(new->owner, name, "%s/%d:%d-%d", dahdi_chan_name, pri->trunkgroup, new->channel, 1); - new->owner->tech_pvt = new; - new->owner->fds[0] = new->subs[SUB_REAL].dfd; - new->subs[SUB_REAL].owner = old->subs[SUB_REAL].owner; - old->subs[SUB_REAL].owner = NULL; - } else - ast_log(LOG_WARNING, "Whoa, there's no owner, and we're having to fix up channel %d to channel %d\n", old->channel, new->channel); - new->call = old->call; - old->call = NULL; - - /* Copy any DSP that may be present */ - new->dsp = old->dsp; - new->dsp_features = old->dsp_features; - old->dsp = NULL; - old->dsp_features = 0; - } - return principle; - } - } - /* Now check for a CRV with no bearer */ - crv = pri->crvs; - while (crv) { - if (crv->call == c) { - /* This is our match... Perform some basic checks */ - if (crv->bearer) - ast_log(LOG_WARNING, "Trying to fix up call which already has a bearer which isn't the one we think it is\n"); - else if (pri->pvts[principle]->owner) - ast_log(LOG_WARNING, "Tring to fix up a call to a bearer which already has an owner!\n"); - else { - /* Looks good. Drop the pseudo channel now, clear up the assignment, and - wakeup the potential sleeper */ - dahdi_close_sub(crv, SUB_REAL); - pri->pvts[principle]->call = crv->call; - pri_assign_bearer(crv, pri, pri->pvts[principle]); - ast_log(LOG_DEBUG, "Assigning bearer %d/%d to CRV %d:%d\n", - pri->pvts[principle]->logicalspan, pri->pvts[principle]->prioffset, - pri->trunkgroup, crv->channel); - wakeup_sub(crv, SUB_REAL, pri); - } - return principle; - } - crv = crv->next; - } - ast_log(LOG_WARNING, "Call specified, but not found?\n"); - return -1; -} - -static void *do_idle_thread(void *vchan) -{ - struct ast_channel *chan = vchan; - struct dahdi_pvt *pvt = chan->tech_pvt; - struct ast_frame *f; - char ex[80]; - /* Wait up to 30 seconds for an answer */ - int newms, ms = 30000; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Initiating idle call on channel %s\n", chan->name); - snprintf(ex, sizeof(ex), "%d/%s", pvt->channel, pvt->pri->idledial); - if (ast_call(chan, ex, 0)) { - ast_log(LOG_WARNING, "Idle dial failed on '%s' to '%s'\n", chan->name, ex); - ast_hangup(chan); - return NULL; - } - while ((newms = ast_waitfor(chan, ms)) > 0) { - f = ast_read(chan); - if (!f) { - /* Got hangup */ - break; - } - if (f->frametype == AST_FRAME_CONTROL) { - switch (f->subclass) { - case AST_CONTROL_ANSWER: - /* Launch the PBX */ - ast_copy_string(chan->exten, pvt->pri->idleext, sizeof(chan->exten)); - ast_copy_string(chan->context, pvt->pri->idlecontext, sizeof(chan->context)); - chan->priority = 1; - if (option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_3 "Idle channel '%s' answered, sending to %s@%s\n", chan->name, chan->exten, chan->context); - ast_pbx_run(chan); - /* It's already hungup, return immediately */ - return NULL; - case AST_CONTROL_BUSY: - if (option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_3 "Idle channel '%s' busy, waiting...\n", chan->name); - break; - case AST_CONTROL_CONGESTION: - if (option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_3 "Idle channel '%s' congested, waiting...\n", chan->name); - break; - }; - } - ast_frfree(f); - ms = newms; - } - /* Hangup the channel since nothing happend */ - ast_hangup(chan); - return NULL; -} - -#ifndef PRI_RESTART -#error "Upgrade your libpri" -#endif -static void dahdi_pri_message(struct pri *pri, char *s) -{ - int x, y; - int dchan = -1, span = -1; - int dchancount = 0; - - if (pri) { - for (x = 0; x < NUM_SPANS; x++) { - for (y = 0; y < NUM_DCHANS; y++) { - if (pris[x].dchans[y]) - dchancount++; - - if (pris[x].dchans[y] == pri) - dchan = y; - } - if (dchan >= 0) { - span = x; - break; - } - dchancount = 0; - } - if ((dchan >= 0) && (span >= 0)) { - if (dchancount > 1) - ast_verbose("[Span %d D-Channel %d]%s", span, dchan, s); - else - ast_verbose("%s", s); - } else - ast_log(LOG_ERROR, "PRI debug error: could not find pri associated it with debug message output\n"); - } else - ast_verbose("%s", s); - - ast_mutex_lock(&pridebugfdlock); - - if (pridebugfd >= 0) { - if (write(pridebugfd, s, strlen(s)) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } - } - - ast_mutex_unlock(&pridebugfdlock); -} - -static void dahdi_pri_error(struct pri *pri, char *s) -{ - int x, y; - int dchan = -1, span = -1; - int dchancount = 0; - - if (pri) { - for (x = 0; x < NUM_SPANS; x++) { - for (y = 0; y < NUM_DCHANS; y++) { - if (pris[x].dchans[y]) - dchancount++; - - if (pris[x].dchans[y] == pri) - dchan = y; - } - if (dchan >= 0) { - span = x; - break; - } - dchancount = 0; - } - if ((dchan >= 0) && (span >= 0)) { - if (dchancount > 1) - ast_log(LOG_ERROR, "[Span %d D-Channel %d] PRI: %s", span, dchan, s); - else - ast_log(LOG_ERROR, "%s", s); - } else - ast_log(LOG_ERROR, "PRI debug error: could not find pri associated it with debug message output\n"); - } else - ast_log(LOG_ERROR, "%s", s); - - ast_mutex_lock(&pridebugfdlock); - - if (pridebugfd >= 0) { - if (write(pridebugfd, s, strlen(s)) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } - } - - ast_mutex_unlock(&pridebugfdlock); -} - -static int pri_check_restart(struct dahdi_pri *pri) -{ - do { - pri->resetpos++; - } while ((pri->resetpos < pri->numchans) && - (!pri->pvts[pri->resetpos] || - pri->pvts[pri->resetpos]->call || - pri->pvts[pri->resetpos]->resetting)); - if (pri->resetpos < pri->numchans) { - /* Mark the channel as resetting and restart it */ - pri->pvts[pri->resetpos]->resetting = 1; - pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[pri->resetpos])); - } else { - pri->resetting = 0; - time(&pri->lastreset); - } - return 0; -} - -static int pri_hangup_all(struct dahdi_pvt *p, struct dahdi_pri *pri) -{ - int x; - int redo; - ast_mutex_unlock(&pri->lock); - ast_mutex_lock(&p->lock); - do { - redo = 0; - for (x = 0; x < 3; x++) { - while (p->subs[x].owner && ast_mutex_trylock(&p->subs[x].owner->lock)) { - redo++; - DEADLOCK_AVOIDANCE(&p->lock); - } - if (p->subs[x].owner) { - ast_queue_hangup(p->subs[x].owner); - ast_mutex_unlock(&p->subs[x].owner->lock); - } - } - } while (redo); - ast_mutex_unlock(&p->lock); - ast_mutex_lock(&pri->lock); - return 0; -} - -static char * redirectingreason2str(int redirectingreason) -{ - switch (redirectingreason) { - case 0: - return "UNKNOWN"; - case 1: - return "BUSY"; - case 2: - return "NO_REPLY"; - case 0xF: - return "UNCONDITIONAL"; - default: - return "NOREDIRECT"; - } -} - -static void apply_plan_to_number(char *buf, size_t size, const struct dahdi_pri *pri, const char *number, const int plan) -{ - switch (plan) { - case PRI_INTERNATIONAL_ISDN: /* Q.931 dialplan == 0x11 international dialplan => prepend international prefix digits */ - snprintf(buf, size, "%s%s", pri->internationalprefix, number); - break; - case PRI_NATIONAL_ISDN: /* Q.931 dialplan == 0x21 national dialplan => prepend national prefix digits */ - snprintf(buf, size, "%s%s", pri->nationalprefix, number); - break; - case PRI_LOCAL_ISDN: /* Q.931 dialplan == 0x41 local dialplan => prepend local prefix digits */ - snprintf(buf, size, "%s%s", pri->localprefix, number); - break; - case PRI_PRIVATE: /* Q.931 dialplan == 0x49 private dialplan => prepend private prefix digits */ - snprintf(buf, size, "%s%s", pri->privateprefix, number); - break; - case PRI_UNKNOWN: /* Q.931 dialplan == 0x00 unknown dialplan => prepend unknown prefix digits */ - snprintf(buf, size, "%s%s", pri->unknownprefix, number); - break; - default: /* other Q.931 dialplan => don't twiddle with callingnum */ - snprintf(buf, size, "%s", number); - break; - } -} - -static int dahdi_setlaw(int dfd, int law) -{ - int res; - res = ioctl(dfd, DAHDI_SETLAW, &law); - if (res) - return res; - return 0; -} - -static void *pri_dchannel(void *vpri) -{ - struct dahdi_pri *pri = vpri; - pri_event *e; - struct pollfd fds[NUM_DCHANS]; - int res; - int chanpos = 0; - int x; - int haveidles; - int activeidles; - int nextidle = -1; - struct ast_channel *c; - struct timeval tv, lowest, *next; - struct timeval lastidle = { 0, 0 }; - int doidling=0; - char *cc; - char idlen[80]; - struct ast_channel *idle; - pthread_t p; - time_t t; - int i, which=-1; - int numdchans; - int cause=0; - struct dahdi_pvt *crv; - pthread_t threadid; - pthread_attr_t attr; - char ani2str[6]; - char plancallingnum[256]; - char plancallingani[256]; - char calledtonstr[10]; - - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); - - gettimeofday(&lastidle, NULL); - if (!ast_strlen_zero(pri->idledial) && !ast_strlen_zero(pri->idleext)) { - /* Need to do idle dialing, check to be sure though */ - cc = strchr(pri->idleext, '@'); - if (cc) { - *cc = '\0'; - cc++; - ast_copy_string(pri->idlecontext, cc, sizeof(pri->idlecontext)); -#if 0 - /* Extensions may not be loaded yet */ - if (!ast_exists_extension(NULL, pri->idlecontext, pri->idleext, 1, NULL)) - ast_log(LOG_WARNING, "Extension '%s @ %s' does not exist\n", pri->idleext, pri->idlecontext); - else -#endif - doidling = 1; - } else - ast_log(LOG_WARNING, "Idle dial string '%s' lacks '@context'\n", pri->idleext); - } - for (;;) { - for (i = 0; i < NUM_DCHANS; i++) { - if (!pri->dchannels[i]) - break; - fds[i].fd = pri->fds[i]; - fds[i].events = POLLIN | POLLPRI; - fds[i].revents = 0; - } - numdchans = i; - time(&t); - ast_mutex_lock(&pri->lock); - if (pri->switchtype != PRI_SWITCH_GR303_TMC && (pri->resetinterval > 0)) { - if (pri->resetting && pri_is_up(pri)) { - if (pri->resetpos < 0) - pri_check_restart(pri); - } else { - if (!pri->resetting && (t - pri->lastreset) >= pri->resetinterval) { - pri->resetting = 1; - pri->resetpos = -1; - } - } - } - /* Look for any idle channels if appropriate */ - if (doidling && pri_is_up(pri)) { - nextidle = -1; - haveidles = 0; - activeidles = 0; - for (x = pri->numchans; x >= 0; x--) { - if (pri->pvts[x] && !pri->pvts[x]->owner && - !pri->pvts[x]->call) { - if (haveidles < pri->minunused) { - haveidles++; - } else if (!pri->pvts[x]->resetting) { - nextidle = x; - break; - } - } else if (pri->pvts[x] && pri->pvts[x]->owner && pri->pvts[x]->isidlecall) - activeidles++; - } - if (nextidle > -1) { - if (ast_tvdiff_ms(ast_tvnow(), lastidle) > 1000) { - /* Don't create a new idle call more than once per second */ - snprintf(idlen, sizeof(idlen), "%d/%s", pri->pvts[nextidle]->channel, pri->idledial); - idle = dahdi_request(dahdi_chan_name, AST_FORMAT_ULAW, idlen, &cause); - if (idle) { - pri->pvts[nextidle]->isidlecall = 1; - if (ast_pthread_create_background(&p, NULL, do_idle_thread, idle)) { - ast_log(LOG_WARNING, "Unable to start new thread for idle channel '%s'\n", idle->name); - dahdi_hangup(idle); - } - } else - ast_log(LOG_WARNING, "Unable to request channel 'DAHDI/%s' for idle call\n", idlen); - gettimeofday(&lastidle, NULL); - } - } else if ((haveidles < pri->minunused) && - (activeidles > pri->minidle)) { - /* Mark something for hangup if there is something - that can be hungup */ - for (x = pri->numchans; x >= 0; x--) { - /* find a candidate channel */ - if (pri->pvts[x] && pri->pvts[x]->owner && pri->pvts[x]->isidlecall) { - pri->pvts[x]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - haveidles++; - /* Stop if we have enough idle channels or - can't spare any more active idle ones */ - if ((haveidles >= pri->minunused) || - (activeidles <= pri->minidle)) - break; - } - } - } - } - /* Start with reasonable max */ - lowest = ast_tv(60, 0); - for (i = 0; i < NUM_DCHANS; i++) { - /* Find lowest available d-channel */ - if (!pri->dchannels[i]) - break; - if ((next = pri_schedule_next(pri->dchans[i]))) { - /* We need relative time here */ - tv = ast_tvsub(*next, ast_tvnow()); - if (tv.tv_sec < 0) { - tv = ast_tv(0,0); - } - if (doidling || pri->resetting) { - if (tv.tv_sec > 1) { - tv = ast_tv(1, 0); - } - } else { - if (tv.tv_sec > 60) { - tv = ast_tv(60, 0); - } - } - } else if (doidling || pri->resetting) { - /* Make sure we stop at least once per second if we're - monitoring idle channels */ - tv = ast_tv(1,0); - } else { - /* Don't poll for more than 60 seconds */ - tv = ast_tv(60, 0); - } - if (!i || ast_tvcmp(tv, lowest) < 0) { - lowest = tv; - } - } - ast_mutex_unlock(&pri->lock); - - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); - pthread_testcancel(); - e = NULL; - res = poll(fds, numdchans, lowest.tv_sec * 1000 + lowest.tv_usec / 1000); - pthread_testcancel(); - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); - - ast_mutex_lock(&pri->lock); - if (!res) { - for (which = 0; which < NUM_DCHANS; which++) { - if (!pri->dchans[which]) - break; - /* Just a timeout, run the scheduler */ - e = pri_schedule_run(pri->dchans[which]); - if (e) - break; - } - } else if (res > -1) { - for (which = 0; which < NUM_DCHANS; which++) { - if (!pri->dchans[which]) - break; - if (fds[which].revents & POLLPRI) { - /* Check for an event */ - x = 0; - res = ioctl(pri->fds[which], DAHDI_GETEVENT, &x); - if (x) - ast_log(LOG_NOTICE, "PRI got event: %s (%d) on %s D-channel of span %d\n", event2str(x), x, pri_order(which), pri->span); - /* Keep track of alarm state */ - if (x == DAHDI_EVENT_ALARM) { - pri->dchanavail[which] &= ~(DCHAN_NOTINALARM | DCHAN_UP); - pri_find_dchan(pri); - } else if (x == DAHDI_EVENT_NOALARM) { - pri->dchanavail[which] |= DCHAN_NOTINALARM; - pri_restart(pri->dchans[which]); - } - - if (option_debug) - ast_log(LOG_DEBUG, "Got event %s (%d) on D-channel for span %d\n", event2str(x), x, pri->span); - } else if (fds[which].revents & POLLIN) { - e = pri_check_event(pri->dchans[which]); - } - if (e) - break; - } - } else if (errno != EINTR) - ast_log(LOG_WARNING, "pri_event returned error %d (%s)\n", errno, strerror(errno)); - - if (e) { - if (pri->debug) - pri_dump_event(pri->dchans[which], e); - - if (e->e != PRI_EVENT_DCHAN_DOWN) { - if (!(pri->dchanavail[which] & DCHAN_UP)) { - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up\n", pri_order(which), pri->span); - } - pri->dchanavail[which] |= DCHAN_UP; - } else { - if (pri->dchanavail[which] & DCHAN_UP) { - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down\n", pri_order(which), pri->span); - } - pri->dchanavail[which] &= ~DCHAN_UP; - } - - if ((e->e != PRI_EVENT_DCHAN_UP) && (e->e != PRI_EVENT_DCHAN_DOWN) && (pri->pri != pri->dchans[which])) - /* Must be an NFAS group that has the secondary dchan active */ - pri->pri = pri->dchans[which]; - - switch (e->e) { - case PRI_EVENT_DCHAN_UP: - if (!pri->pri) pri_find_dchan(pri); - - /* Note presense of D-channel */ - time(&pri->lastreset); - - /* Restart in 5 seconds */ - if (pri->resetinterval > -1) { - pri->lastreset -= pri->resetinterval; - pri->lastreset += 5; - } - pri->resetting = 0; - /* Take the channels from inalarm condition */ - for (i = 0; i < pri->numchans; i++) - if (pri->pvts[i]) { - pri->pvts[i]->inalarm = 0; - } - break; - case PRI_EVENT_DCHAN_DOWN: - pri_find_dchan(pri); - if (!pri_is_up(pri)) { - pri->resetting = 0; - /* Hangup active channels and put them in alarm mode */ - for (i = 0; i < pri->numchans; i++) { - struct dahdi_pvt *p = pri->pvts[i]; - if (p) { - if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) { - /* T309 is not enabled : hangup calls when alarm occurs */ - if (p->call) { - if (p->pri && p->pri->pri) { - pri_hangup(p->pri->pri, p->call, -1); - pri_destroycall(p->pri->pri, p->call); - p->call = NULL; - } else - ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n"); - } - if (p->realcall) { - pri_hangup_all(p->realcall, pri); - } else if (p->owner) - p->owner->_softhangup |= AST_SOFTHANGUP_DEV; - } - p->inalarm = 1; - } - } - } - break; - case PRI_EVENT_RESTART: - if (e->restart.channel > -1) { - chanpos = pri_find_principle(pri, e->restart.channel); - if (chanpos < 0) - ast_log(LOG_WARNING, "Restart requested on odd/unavailable channel number %d/%d on span %d\n", - PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span); - else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "B-channel %d/%d restarted on span %d\n", - PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span); - ast_mutex_lock(&pri->pvts[chanpos]->lock); - if (pri->pvts[chanpos]->call) { - pri_destroycall(pri->pri, pri->pvts[chanpos]->call); - pri->pvts[chanpos]->call = NULL; - } - /* Force soft hangup if appropriate */ - if (pri->pvts[chanpos]->realcall) - pri_hangup_all(pri->pvts[chanpos]->realcall, pri); - else if (pri->pvts[chanpos]->owner) - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_2 "Restart on requested on entire span %d\n", pri->span); - for (x = 0; x < pri->numchans; x++) - if (pri->pvts[x]) { - ast_mutex_lock(&pri->pvts[x]->lock); - if (pri->pvts[x]->call) { - pri_destroycall(pri->pri, pri->pvts[x]->call); - pri->pvts[x]->call = NULL; - } - if (pri->pvts[chanpos]->realcall) - pri_hangup_all(pri->pvts[chanpos]->realcall, pri); - else if (pri->pvts[x]->owner) - pri->pvts[x]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - ast_mutex_unlock(&pri->pvts[x]->lock); - } - } - break; - case PRI_EVENT_KEYPAD_DIGIT: - chanpos = pri_find_principle(pri, e->digit.channel); - if (chanpos < 0) { - ast_log(LOG_WARNING, "KEYPAD_DIGITs received on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->digit.channel), PRI_CHANNEL(e->digit.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->digit.call); - if (chanpos > -1) { - ast_mutex_lock(&pri->pvts[chanpos]->lock); - /* queue DTMF frame if the PBX for this call was already started (we're forwarding KEYPAD_DIGITs further on */ - if (pri->overlapdial && pri->pvts[chanpos]->call==e->digit.call && pri->pvts[chanpos]->owner) { - /* how to do that */ - int digitlen = strlen(e->digit.digits); - char digit; - int i; - for (i = 0; i < digitlen; i++) { - digit = e->digit.digits[i]; - { - struct ast_frame f = { AST_FRAME_DTMF, digit, }; - dahdi_queue_frame(pri->pvts[chanpos], &f, pri); - } - } - } - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } - } - break; - - case PRI_EVENT_INFO_RECEIVED: - chanpos = pri_find_principle(pri, e->ring.channel); - if (chanpos < 0) { - ast_log(LOG_WARNING, "INFO received on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->ring.call); - if (chanpos > -1) { - ast_mutex_lock(&pri->pvts[chanpos]->lock); - /* queue DTMF frame if the PBX for this call was already started (we're forwarding INFORMATION further on */ - if (pri->overlapdial && pri->pvts[chanpos]->call==e->ring.call && pri->pvts[chanpos]->owner) { - /* how to do that */ - int digitlen = strlen(e->ring.callednum); - char digit; - int i; - for (i = 0; i < digitlen; i++) { - digit = e->ring.callednum[i]; - { - struct ast_frame f = { AST_FRAME_DTMF, digit, }; - dahdi_queue_frame(pri->pvts[chanpos], &f, pri); - } - } - } - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } - } - break; - case PRI_EVENT_RING: - crv = NULL; - if (e->ring.channel == -1) - chanpos = pri_find_empty_chan(pri, 1); - else - chanpos = pri_find_principle(pri, e->ring.channel); - /* if no channel specified find one empty */ - if (chanpos < 0) { - ast_log(LOG_WARNING, "Ring requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - } else { - ast_mutex_lock(&pri->pvts[chanpos]->lock); - if (pri->pvts[chanpos]->owner) { - if (pri->pvts[chanpos]->call == e->ring.call) { - ast_log(LOG_WARNING, "Duplicate setup requested on channel %d/%d already in use on span %d\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - break; - } else { - /* This is where we handle initial glare */ - ast_log(LOG_DEBUG, "Ring requested on channel %d/%d already in use or previously requested on span %d. Attempting to renegotiating channel.\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - chanpos = -1; - } - } - if (chanpos > -1) - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } - if ((chanpos < 0) && (e->ring.flexible)) - chanpos = pri_find_empty_chan(pri, 1); - if (chanpos > -1) { - ast_mutex_lock(&pri->pvts[chanpos]->lock); - if (pri->switchtype == PRI_SWITCH_GR303_TMC) { - /* Should be safe to lock CRV AFAIK while bearer is still locked */ - crv = pri_find_crv(pri, pri_get_crv(pri->pri, e->ring.call, NULL)); - if (crv) - ast_mutex_lock(&crv->lock); - if (!crv || crv->owner) { - pri->pvts[chanpos]->call = NULL; - if (crv) { - if (crv->owner) - crv->owner->_softhangup |= AST_SOFTHANGUP_DEV; - ast_log(LOG_WARNING, "Call received for busy CRV %d on span %d\n", pri_get_crv(pri->pri, e->ring.call, NULL), pri->span); - } else - ast_log(LOG_NOTICE, "Call received for unconfigured CRV %d on span %d\n", pri_get_crv(pri->pri, e->ring.call, NULL), pri->span); - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_INVALID_CALL_REFERENCE); - if (crv) - ast_mutex_unlock(&crv->lock); - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - break; - } - } - pri->pvts[chanpos]->call = e->ring.call; - apply_plan_to_number(plancallingnum, sizeof(plancallingnum), pri, e->ring.callingnum, e->ring.callingplan); - if (pri->pvts[chanpos]->use_callerid) { - ast_shrink_phone_number(plancallingnum); - ast_copy_string(pri->pvts[chanpos]->cid_num, plancallingnum, sizeof(pri->pvts[chanpos]->cid_num)); -#ifdef PRI_ANI - if (!ast_strlen_zero(e->ring.callingani)) { - apply_plan_to_number(plancallingani, sizeof(plancallingani), pri, e->ring.callingani, e->ring.callingplanani); - ast_shrink_phone_number(plancallingani); - ast_copy_string(pri->pvts[chanpos]->cid_ani, plancallingani, sizeof(pri->pvts[chanpos]->cid_ani)); - } else { - pri->pvts[chanpos]->cid_ani[0] = '\0'; - } -#endif - ast_copy_string(pri->pvts[chanpos]->cid_name, e->ring.callingname, sizeof(pri->pvts[chanpos]->cid_name)); - pri->pvts[chanpos]->cid_ton = e->ring.callingplan; /* this is the callingplan (TON/NPI), e->ring.callingplan>>4 would be the TON */ - } else { - pri->pvts[chanpos]->cid_num[0] = '\0'; - pri->pvts[chanpos]->cid_ani[0] = '\0'; - pri->pvts[chanpos]->cid_name[0] = '\0'; - pri->pvts[chanpos]->cid_ton = 0; - } - apply_plan_to_number(pri->pvts[chanpos]->rdnis, sizeof(pri->pvts[chanpos]->rdnis), pri, - e->ring.redirectingnum, e->ring.callingplanrdnis); - /* If immediate=yes go to s|1 */ - if (pri->pvts[chanpos]->immediate) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Going to extension s|1 because of immediate=yes\n"); - pri->pvts[chanpos]->exten[0] = 's'; - pri->pvts[chanpos]->exten[1] = '\0'; - } - /* Get called number */ - else if (!ast_strlen_zero(e->ring.callednum)) { - ast_copy_string(pri->pvts[chanpos]->exten, e->ring.callednum, sizeof(pri->pvts[chanpos]->exten)); - ast_copy_string(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid)); - } else if (pri->overlapdial) - pri->pvts[chanpos]->exten[0] = '\0'; - else { - /* Some PRI circuits are set up to send _no_ digits. Handle them as 's'. */ - pri->pvts[chanpos]->exten[0] = 's'; - pri->pvts[chanpos]->exten[1] = '\0'; - } - /* Set DNID on all incoming calls -- even immediate */ - if (!ast_strlen_zero(e->ring.callednum)) - ast_copy_string(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid)); - /* No number yet, but received "sending complete"? */ - if (e->ring.complete && (ast_strlen_zero(e->ring.callednum))) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Going to extension s|1 because of Complete received\n"); - pri->pvts[chanpos]->exten[0] = 's'; - pri->pvts[chanpos]->exten[1] = '\0'; - } - /* Make sure extension exists (or in overlap dial mode, can exist) */ - if ((pri->overlapdial && ast_canmatch_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) || - ast_exists_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) { - /* Setup law */ - int law; - if (pri->switchtype != PRI_SWITCH_GR303_TMC) { - /* Set to audio mode at this point */ - law = 1; - if (ioctl(pri->pvts[chanpos]->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &law) == -1) - ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d: %s\n", pri->pvts[chanpos]->channel, law, strerror(errno)); - } - if (e->ring.layer1 == PRI_LAYER_1_ALAW) - law = DAHDI_LAW_ALAW; - else - law = DAHDI_LAW_MULAW; - res = dahdi_setlaw(pri->pvts[chanpos]->subs[SUB_REAL].dfd, law); - if (res < 0) - ast_log(LOG_WARNING, "Unable to set law on channel %d\n", pri->pvts[chanpos]->channel); - res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].dfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law); - if (res < 0) - ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", pri->pvts[chanpos]->channel); - if (e->ring.complete || !pri->overlapdial) { - /* Just announce proceeding */ - pri->pvts[chanpos]->proceeding = 1; - pri_proceeding(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 0); - } else { - if (pri->switchtype != PRI_SWITCH_GR303_TMC) - pri_need_more_info(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1); - else - pri_answer(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1); - } - /* Get the use_callingpres state */ - pri->pvts[chanpos]->callingpres = e->ring.callingpres; - - /* Start PBX */ - if (!e->ring.complete && pri->overlapdial && ast_matchmore_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) { - /* Release the PRI lock while we create the channel */ - ast_mutex_unlock(&pri->lock); - if (crv) { - /* Set bearer and such */ - pri_assign_bearer(crv, pri, pri->pvts[chanpos]); - c = dahdi_new(crv, AST_STATE_RESERVED, 0, SUB_REAL, law, e->ring.ctype); - pri->pvts[chanpos]->owner = &inuse; - ast_log(LOG_DEBUG, "Started up crv %d:%d on bearer channel %d\n", pri->trunkgroup, crv->channel, crv->bearer->channel); - } else { - c = dahdi_new(pri->pvts[chanpos], AST_STATE_RESERVED, 0, SUB_REAL, law, e->ring.ctype); - } - - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - - if (!ast_strlen_zero(e->ring.callingsubaddr)) { - pbx_builtin_setvar_helper(c, "CALLINGSUBADDR", e->ring.callingsubaddr); - } - if (e->ring.ani2 >= 0) { - snprintf(ani2str, 5, "%.2d", e->ring.ani2); - pbx_builtin_setvar_helper(c, "ANI2", ani2str); - } - -#ifdef SUPPORT_USERUSER - if (!ast_strlen_zero(e->ring.useruserinfo)) { - pbx_builtin_setvar_helper(c, "USERUSERINFO", e->ring.useruserinfo); - } -#endif - - snprintf(calledtonstr, sizeof(calledtonstr)-1, "%d", e->ring.calledplan); - pbx_builtin_setvar_helper(c, "CALLEDTON", calledtonstr); - if (e->ring.redirectingreason >= 0) - pbx_builtin_setvar_helper(c, "PRIREDIRECTREASON", redirectingreason2str(e->ring.redirectingreason)); - - ast_mutex_lock(&pri->pvts[chanpos]->lock); - ast_mutex_lock(&pri->lock); - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (c && !ast_pthread_create(&threadid, &attr, ss_thread, c)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepting overlap call from '%s' to '%s' on channel %d/%d, span %d\n", - plancallingnum, S_OR(pri->pvts[chanpos]->exten, "<unspecified>"), - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); - } else { - ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); - if (c) - ast_hangup(c); - else { - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION); - pri->pvts[chanpos]->call = NULL; - } - } - pthread_attr_destroy(&attr); - } else { - ast_mutex_unlock(&pri->lock); - /* Release PRI lock while we create the channel */ - c = dahdi_new(pri->pvts[chanpos], AST_STATE_RING, 1, SUB_REAL, law, e->ring.ctype); - if (c) { - char calledtonstr[10]; - - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - - if (e->ring.ani2 >= 0) { - snprintf(ani2str, 5, "%d", e->ring.ani2); - pbx_builtin_setvar_helper(c, "ANI2", ani2str); - } - -#ifdef SUPPORT_USERUSER - if (!ast_strlen_zero(e->ring.useruserinfo)) { - pbx_builtin_setvar_helper(c, "USERUSERINFO", e->ring.useruserinfo); - } -#endif - - if (e->ring.redirectingreason >= 0) - pbx_builtin_setvar_helper(c, "PRIREDIRECTREASON", redirectingreason2str(e->ring.redirectingreason)); - - snprintf(calledtonstr, sizeof(calledtonstr)-1, "%d", e->ring.calledplan); - pbx_builtin_setvar_helper(c, "CALLEDTON", calledtonstr); - - ast_mutex_lock(&pri->pvts[chanpos]->lock); - ast_mutex_lock(&pri->lock); - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepting call from '%s' to '%s' on channel %d/%d, span %d\n", - plancallingnum, pri->pvts[chanpos]->exten, - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); - dahdi_enable_ec(pri->pvts[chanpos]); - } else { - - ast_mutex_lock(&pri->lock); - - ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span); - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION); - pri->pvts[chanpos]->call = NULL; - } - } - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Extension '%s' in context '%s' from '%s' does not exist. Rejecting call on channel %d/%d, span %d\n", - pri->pvts[chanpos]->exten, pri->pvts[chanpos]->context, pri->pvts[chanpos]->cid_num, pri->pvts[chanpos]->logicalspan, - pri->pvts[chanpos]->prioffset, pri->span); - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED); - pri->pvts[chanpos]->call = NULL; - pri->pvts[chanpos]->exten[0] = '\0'; - } - if (crv) - ast_mutex_unlock(&crv->lock); - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } else { - if (e->ring.flexible) - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION); - else - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL); - } - break; - case PRI_EVENT_RINGING: - chanpos = pri_find_principle(pri, e->ringing.channel); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Ringing requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->ringing.channel), PRI_CHANNEL(e->ringing.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->ringing.call); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Ringing requested on channel %d/%d not in use on span %d\n", - PRI_SPAN(e->ringing.channel), PRI_CHANNEL(e->ringing.channel), pri->span); - } else { - ast_mutex_lock(&pri->pvts[chanpos]->lock); - if (ast_strlen_zero(pri->pvts[chanpos]->dop.dialstr)) { - dahdi_enable_ec(pri->pvts[chanpos]); - pri->pvts[chanpos]->subs[SUB_REAL].needringing = 1; - pri->pvts[chanpos]->alerting = 1; - } else - ast_log(LOG_DEBUG, "Deferring ringing notification because of extra digits to dial...\n"); -#ifdef PRI_PROGRESS_MASK - if (e->ringing.progressmask & PRI_PROG_INBAND_AVAILABLE) { -#else - if (e->ringing.progress == 8) { -#endif - /* Now we can do call progress detection */ - if (pri->pvts[chanpos]->dsp && pri->pvts[chanpos]->dsp_features) { - /* RINGING detection isn't required because we got ALERTING signal */ - ast_dsp_set_features(pri->pvts[chanpos]->dsp, pri->pvts[chanpos]->dsp_features & ~DSP_PROGRESS_RINGING); - pri->pvts[chanpos]->dsp_features = 0; - } - } - -#ifdef SUPPORT_USERUSER - if (!ast_strlen_zero(e->ringing.useruserinfo)) { - struct ast_channel *owner = pri->pvts[chanpos]->owner; - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - pbx_builtin_setvar_helper(owner, "USERUSERINFO", e->ringing.useruserinfo); - ast_mutex_lock(&pri->pvts[chanpos]->lock); - } -#endif - - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } - } - break; - case PRI_EVENT_PROGRESS: - /* Get chan value if e->e is not PRI_EVNT_RINGING */ - chanpos = pri_find_principle(pri, e->proceeding.channel); - if (chanpos > -1) { -#ifdef PRI_PROGRESS_MASK - if ((!pri->pvts[chanpos]->progress) || (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE)) { -#else - if ((!pri->pvts[chanpos]->progress) || (e->proceeding.progress == 8)) { -#endif - struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, }; - - if (e->proceeding.cause > -1) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "PROGRESS with cause code %d received\n", e->proceeding.cause); - - /* Work around broken, out of spec USER_BUSY cause in a progress message */ - if (e->proceeding.cause == AST_CAUSE_USER_BUSY) { - if (pri->pvts[chanpos]->owner) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "PROGRESS with 'user busy' received, signaling AST_CONTROL_BUSY instead of AST_CONTROL_PROGRESS\n"); - - pri->pvts[chanpos]->owner->hangupcause = e->proceeding.cause; - f.subclass = AST_CONTROL_BUSY; - } - } - } - - ast_mutex_lock(&pri->pvts[chanpos]->lock); - ast_log(LOG_DEBUG, "Queuing frame from PRI_EVENT_PROGRESS on channel %d/%d span %d\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset,pri->span); - dahdi_queue_frame(pri->pvts[chanpos], &f, pri); -#ifdef PRI_PROGRESS_MASK - if (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) { -#else - if (e->proceeding.progress == 8) { -#endif - /* Now we can do call progress detection */ - if (pri->pvts[chanpos]->dsp && pri->pvts[chanpos]->dsp_features) { - ast_dsp_set_features(pri->pvts[chanpos]->dsp, pri->pvts[chanpos]->dsp_features); - pri->pvts[chanpos]->dsp_features = 0; - } - } - pri->pvts[chanpos]->progress = 1; - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } - } - break; - case PRI_EVENT_PROCEEDING: - chanpos = pri_find_principle(pri, e->proceeding.channel); - if (chanpos > -1) { - if (!pri->pvts[chanpos]->proceeding) { - struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_PROCEEDING, }; - - ast_mutex_lock(&pri->pvts[chanpos]->lock); - ast_log(LOG_DEBUG, "Queuing frame from PRI_EVENT_PROCEEDING on channel %d/%d span %d\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset,pri->span); - dahdi_queue_frame(pri->pvts[chanpos], &f, pri); -#ifdef PRI_PROGRESS_MASK - if (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) { -#else - if (e->proceeding.progress == 8) { -#endif - /* Now we can do call progress detection */ - if (pri->pvts[chanpos]->dsp && pri->pvts[chanpos]->dsp_features) { - ast_dsp_set_features(pri->pvts[chanpos]->dsp, pri->pvts[chanpos]->dsp_features); - pri->pvts[chanpos]->dsp_features = 0; - } - /* Bring voice path up */ - f.subclass = AST_CONTROL_PROGRESS; - dahdi_queue_frame(pri->pvts[chanpos], &f, pri); - } - pri->pvts[chanpos]->proceeding = 1; - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } - } - break; - case PRI_EVENT_FACNAME: - chanpos = pri_find_principle(pri, e->facname.channel); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Facility Name requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->facname.channel), PRI_CHANNEL(e->facname.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->facname.call); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Facility Name requested on channel %d/%d not in use on span %d\n", - PRI_SPAN(e->facname.channel), PRI_CHANNEL(e->facname.channel), pri->span); - } else { - /* Re-use *69 field for PRI */ - ast_mutex_lock(&pri->pvts[chanpos]->lock); - ast_copy_string(pri->pvts[chanpos]->lastcid_num, e->facname.callingnum, sizeof(pri->pvts[chanpos]->lastcid_num)); - ast_copy_string(pri->pvts[chanpos]->lastcid_name, e->facname.callingname, sizeof(pri->pvts[chanpos]->lastcid_name)); - pri->pvts[chanpos]->subs[SUB_REAL].needcallerid =1; - dahdi_enable_ec(pri->pvts[chanpos]); - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } - } - break; - case PRI_EVENT_ANSWER: - chanpos = pri_find_principle(pri, e->answer.channel); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Answer on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->answer.call); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Answer requested on channel %d/%d not in use on span %d\n", - PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), pri->span); - } else { - ast_mutex_lock(&pri->pvts[chanpos]->lock); - /* Now we can do call progress detection */ - - /* We changed this so it turns on the DSP no matter what... progress or no progress. - * By this time, we need DTMF detection and other features that were previously disabled - * -- Matt F */ - if (pri->pvts[chanpos]->dsp && pri->pvts[chanpos]->dsp_features) { - ast_dsp_set_features(pri->pvts[chanpos]->dsp, pri->pvts[chanpos]->dsp_features); - pri->pvts[chanpos]->dsp_features = 0; - } - if (pri->pvts[chanpos]->realcall && (pri->pvts[chanpos]->realcall->sig == SIG_FXSKS)) { - ast_log(LOG_DEBUG, "Starting up GR-303 trunk now that we got CONNECT...\n"); - x = DAHDI_START; - res = ioctl(pri->pvts[chanpos]->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); - if (res < 0) { - if (errno != EINPROGRESS) { - ast_log(LOG_WARNING, "Unable to start channel: %s\n", strerror(errno)); - } - } - } else if (!ast_strlen_zero(pri->pvts[chanpos]->dop.dialstr)) { - pri->pvts[chanpos]->dialing = 1; - /* Send any "w" waited stuff */ - res = ioctl(pri->pvts[chanpos]->subs[SUB_REAL].dfd, DAHDI_DIAL, &pri->pvts[chanpos]->dop); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", pri->pvts[chanpos]->channel, strerror(errno)); - pri->pvts[chanpos]->dop.dialstr[0] = '\0'; - } else - ast_log(LOG_DEBUG, "Sent deferred digit string: %s\n", pri->pvts[chanpos]->dop.dialstr); - pri->pvts[chanpos]->dop.dialstr[0] = '\0'; - } else if (pri->pvts[chanpos]->confirmanswer) { - ast_log(LOG_DEBUG, "Waiting on answer confirmation on channel %d!\n", pri->pvts[chanpos]->channel); - } else { - pri->pvts[chanpos]->subs[SUB_REAL].needanswer =1; - /* Enable echo cancellation if it's not on already */ - dahdi_enable_ec(pri->pvts[chanpos]); - } - -#ifdef SUPPORT_USERUSER - if (!ast_strlen_zero(e->answer.useruserinfo)) { - struct ast_channel *owner = pri->pvts[chanpos]->owner; - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - pbx_builtin_setvar_helper(owner, "USERUSERINFO", e->answer.useruserinfo); - ast_mutex_lock(&pri->pvts[chanpos]->lock); - } -#endif - - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } - } - break; - case PRI_EVENT_HANGUP: - chanpos = pri_find_principle(pri, e->hangup.channel); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Hangup requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call); - if (chanpos > -1) { - ast_mutex_lock(&pri->pvts[chanpos]->lock); - if (!pri->pvts[chanpos]->alreadyhungup) { - /* we're calling here dahdi_hangup so once we get there we need to clear p->call after calling pri_hangup */ - pri->pvts[chanpos]->alreadyhungup = 1; - if (pri->pvts[chanpos]->realcall) - pri_hangup_all(pri->pvts[chanpos]->realcall, pri); - else if (pri->pvts[chanpos]->owner) { - /* Queue a BUSY instead of a hangup if our cause is appropriate */ - pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; - if (pri->pvts[chanpos]->owner->_state == AST_STATE_UP) - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - else { - switch (e->hangup.cause) { - case PRI_CAUSE_USER_BUSY: - pri->pvts[chanpos]->subs[SUB_REAL].needbusy =1; - break; - case PRI_CAUSE_CALL_REJECTED: - case PRI_CAUSE_NETWORK_OUT_OF_ORDER: - case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: - case PRI_CAUSE_SWITCH_CONGESTION: - case PRI_CAUSE_DESTINATION_OUT_OF_ORDER: - case PRI_CAUSE_NORMAL_TEMPORARY_FAILURE: - pri->pvts[chanpos]->subs[SUB_REAL].needcongestion =1; - break; - default: - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - } - } - } - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d got hangup, cause %d\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, e->hangup.cause); - } else { - pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause); - pri->pvts[chanpos]->call = NULL; - } - if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Forcing restart of channel %d/%d on span %d since channel reported in use\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); - pri->pvts[chanpos]->resetting = 1; - } - if (e->hangup.aoc_units > -1) - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); - -#ifdef SUPPORT_USERUSER - if (pri->pvts[chanpos]->owner && !ast_strlen_zero(e->hangup.useruserinfo)) { - struct ast_channel *owner = pri->pvts[chanpos]->owner; - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - pbx_builtin_setvar_helper(owner, "USERUSERINFO", e->hangup.useruserinfo); - ast_mutex_lock(&pri->pvts[chanpos]->lock); - } -#endif - - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } else { - ast_log(LOG_WARNING, "Hangup on bad channel %d/%d on span %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - } - } - break; -#ifndef PRI_EVENT_HANGUP_REQ -#error please update libpri -#endif - case PRI_EVENT_HANGUP_REQ: - chanpos = pri_find_principle(pri, e->hangup.channel); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call); - if (chanpos > -1) { - ast_mutex_lock(&pri->pvts[chanpos]->lock); - if (pri->pvts[chanpos]->realcall) - pri_hangup_all(pri->pvts[chanpos]->realcall, pri); - else if (pri->pvts[chanpos]->owner) { - pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; - if (pri->pvts[chanpos]->owner->_state == AST_STATE_UP) - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - else { - switch (e->hangup.cause) { - case PRI_CAUSE_USER_BUSY: - pri->pvts[chanpos]->subs[SUB_REAL].needbusy =1; - break; - case PRI_CAUSE_CALL_REJECTED: - case PRI_CAUSE_NETWORK_OUT_OF_ORDER: - case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: - case PRI_CAUSE_SWITCH_CONGESTION: - case PRI_CAUSE_DESTINATION_OUT_OF_ORDER: - case PRI_CAUSE_NORMAL_TEMPORARY_FAILURE: - pri->pvts[chanpos]->subs[SUB_REAL].needcongestion =1; - break; - default: - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - } - } - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d got hangup request, cause %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span, e->hangup.cause); - if (e->hangup.aoc_units > -1) - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s"); - } else { - pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause); - pri->pvts[chanpos]->call = NULL; - } - if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Forcing restart of channel %d/%d span %d since channel reported in use\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); - pri->pvts[chanpos]->resetting = 1; - } - -#ifdef SUPPORT_USERUSER - if (!ast_strlen_zero(e->hangup.useruserinfo)) { - struct ast_channel *owner = pri->pvts[chanpos]->owner; - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - pbx_builtin_setvar_helper(owner, "USERUSERINFO", e->hangup.useruserinfo); - ast_mutex_lock(&pri->pvts[chanpos]->lock); - } -#endif - - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } else { - ast_log(LOG_WARNING, "Hangup REQ on bad channel %d/%d on span %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - } - } - break; - case PRI_EVENT_HANGUP_ACK: - chanpos = pri_find_principle(pri, e->hangup.channel); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Hangup ACK requested on unconfigured channel number %d/%d span %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call); - if (chanpos > -1) { - ast_mutex_lock(&pri->pvts[chanpos]->lock); - pri->pvts[chanpos]->call = NULL; - pri->pvts[chanpos]->resetting = 0; - if (pri->pvts[chanpos]->owner) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d got hangup ACK\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - } - -#ifdef SUPPORT_USERUSER - if (!ast_strlen_zero(e->hangup.useruserinfo)) { - struct ast_channel *owner = pri->pvts[chanpos]->owner; - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - pbx_builtin_setvar_helper(owner, "USERUSERINFO", e->hangup.useruserinfo); - ast_mutex_lock(&pri->pvts[chanpos]->lock); - } -#endif - - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } - } - break; - case PRI_EVENT_CONFIG_ERR: - ast_log(LOG_WARNING, "PRI Error on span %d: %s\n", pri->trunkgroup, e->err.err); - break; - case PRI_EVENT_RESTART_ACK: - chanpos = pri_find_principle(pri, e->restartack.channel); - if (chanpos < 0) { - /* Sometime switches (e.g. I421 / British Telecom) don't give us the - channel number, so we have to figure it out... This must be why - everybody resets exactly a channel at a time. */ - for (x = 0; x < pri->numchans; x++) { - if (pri->pvts[x] && pri->pvts[x]->resetting) { - chanpos = x; - ast_mutex_lock(&pri->pvts[chanpos]->lock); - ast_log(LOG_DEBUG, "Assuming restart ack is really for channel %d/%d span %d\n", pri->pvts[chanpos]->logicalspan, - pri->pvts[chanpos]->prioffset, pri->span); - if (pri->pvts[chanpos]->realcall) - pri_hangup_all(pri->pvts[chanpos]->realcall, pri); - else if (pri->pvts[chanpos]->owner) { - ast_log(LOG_WARNING, "Got restart ack on channel %d/%d with owner on span %d\n", pri->pvts[chanpos]->logicalspan, - pri->pvts[chanpos]->prioffset, pri->span); - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - } - pri->pvts[chanpos]->resetting = 0; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "B-channel %d/%d successfully restarted on span %d\n", pri->pvts[chanpos]->logicalspan, - pri->pvts[chanpos]->prioffset, pri->span); - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - if (pri->resetting) - pri_check_restart(pri); - break; - } - } - if (chanpos < 0) { - ast_log(LOG_WARNING, "Restart ACK requested on strange channel %d/%d span %d\n", - PRI_SPAN(e->restartack.channel), PRI_CHANNEL(e->restartack.channel), pri->span); - } - } else { - if (pri->pvts[chanpos]) { - ast_mutex_lock(&pri->pvts[chanpos]->lock); - if (pri->pvts[chanpos]->realcall) - pri_hangup_all(pri->pvts[chanpos]->realcall, pri); - else if (pri->pvts[chanpos]->owner) { - ast_log(LOG_WARNING, "Got restart ack on channel %d/%d span %d with owner\n", - PRI_SPAN(e->restartack.channel), PRI_CHANNEL(e->restartack.channel), pri->span); - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - } - pri->pvts[chanpos]->resetting = 0; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "B-channel %d/%d successfully restarted on span %d\n", pri->pvts[chanpos]->logicalspan, - pri->pvts[chanpos]->prioffset, pri->span); - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - if (pri->resetting) - pri_check_restart(pri); - } - } - break; - case PRI_EVENT_SETUP_ACK: - chanpos = pri_find_principle(pri, e->setup_ack.channel); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Received SETUP_ACKNOWLEDGE on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->setup_ack.channel), PRI_CHANNEL(e->setup_ack.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->setup_ack.call); - if (chanpos > -1) { - ast_mutex_lock(&pri->pvts[chanpos]->lock); - pri->pvts[chanpos]->setup_ack = 1; - /* Send any queued digits */ - for (x = 0;x < strlen(pri->pvts[chanpos]->dialdest); x++) { - ast_log(LOG_DEBUG, "Sending pending digit '%c'\n", pri->pvts[chanpos]->dialdest[x]); - pri_information(pri->pri, pri->pvts[chanpos]->call, - pri->pvts[chanpos]->dialdest[x]); - } - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } else - ast_log(LOG_WARNING, "Unable to move channel %d!\n", e->setup_ack.channel); - } - break; - case PRI_EVENT_NOTIFY: - chanpos = pri_find_principle(pri, e->notify.channel); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Received NOTIFY on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->notify.channel), PRI_CHANNEL(e->notify.channel), pri->span); - } else { - struct ast_frame f = { AST_FRAME_CONTROL, }; - ast_mutex_lock(&pri->pvts[chanpos]->lock); - switch (e->notify.info) { - case PRI_NOTIFY_REMOTE_HOLD: - f.subclass = AST_CONTROL_HOLD; - dahdi_queue_frame(pri->pvts[chanpos], &f, pri); - break; - case PRI_NOTIFY_REMOTE_RETRIEVAL: - f.subclass = AST_CONTROL_UNHOLD; - dahdi_queue_frame(pri->pvts[chanpos], &f, pri); - break; - } - ast_mutex_unlock(&pri->pvts[chanpos]->lock); - } - break; - default: - ast_log(LOG_DEBUG, "Event: %d\n", e->e); - } - } - ast_mutex_unlock(&pri->lock); - } - /* Never reached */ - return NULL; -} - -static int start_pri(struct dahdi_pri *pri) -{ - int res, x; - struct dahdi_params p; - struct dahdi_bufferinfo bi; - struct dahdi_spaninfo si; - int i; - - for (i = 0; i < NUM_DCHANS; i++) { - if (!pri->dchannels[i]) - break; - pri->fds[i] = open(DAHDI_FILE_CHANNEL, O_RDWR, 0600); - x = pri->dchannels[i]; - if ((pri->fds[i] < 0) || (ioctl(pri->fds[i],DAHDI_SPECIFY,&x) == -1)) { - ast_log(LOG_ERROR, "Unable to open D-channel %d (%s)\n", x, strerror(errno)); - return -1; - } - res = ioctl(pri->fds[i], DAHDI_GET_PARAMS, &p); - if (res) { - dahdi_close_pri_fd(pri, i); - ast_log(LOG_ERROR, "Unable to get parameters for D-channel %d (%s)\n", x, strerror(errno)); - return -1; - } - if ((p.sigtype != DAHDI_SIG_HDLCFCS) && (p.sigtype != DAHDI_SIG_HARDHDLC)) { - dahdi_close_pri_fd(pri, i); - ast_log(LOG_ERROR, "D-channel %d is not in HDLC/FCS mode. See /etc/dahdi/system.conf\n", x); - return -1; - } - memset(&si, 0, sizeof(si)); - res = ioctl(pri->fds[i], DAHDI_SPANSTAT, &si); - if (res) { - dahdi_close_pri_fd(pri, i); - ast_log(LOG_ERROR, "Unable to get span state for D-channel %d (%s)\n", x, strerror(errno)); - } - if (!si.alarms) - pri->dchanavail[i] |= DCHAN_NOTINALARM; - else - pri->dchanavail[i] &= ~DCHAN_NOTINALARM; - bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE; - bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE; - bi.numbufs = 32; - bi.bufsize = 1024; - if (ioctl(pri->fds[i], DAHDI_SET_BUFINFO, &bi)) { - ast_log(LOG_ERROR, "Unable to set appropriate buffering on channel %d: %s\n", x, strerror(errno)); - dahdi_close_pri_fd(pri, i); - return -1; - } - pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype); - /* Force overlap dial if we're doing GR-303! */ - if (pri->switchtype == PRI_SWITCH_GR303_TMC) - pri->overlapdial = 1; - pri_set_overlapdial(pri->dchans[i],pri->overlapdial); -#ifdef HAVE_PRI_INBANDDISCONNECT - pri_set_inbanddisconnect(pri->dchans[i], pri->inbanddisconnect); -#endif - /* Enslave to master if appropriate */ - if (i) - pri_enslave(pri->dchans[0], pri->dchans[i]); - if (!pri->dchans[i]) { - dahdi_close_pri_fd(pri, i); - ast_log(LOG_ERROR, "Unable to create PRI structure\n"); - return -1; - } - pri_set_debug(pri->dchans[i], DEFAULT_PRI_DEBUG); - pri_set_nsf(pri->dchans[i], pri->nsf); -#ifdef PRI_GETSET_TIMERS - for (x = 0; x < PRI_MAX_TIMERS; x++) { - if (pritimers[x] != 0) - pri_set_timer(pri->dchans[i], x, pritimers[x]); - } -#endif - } - /* Assume primary is the one we use */ - pri->pri = pri->dchans[0]; - pri->resetpos = -1; - if (ast_pthread_create_background(&pri->master, NULL, pri_dchannel, pri)) { - for (i = 0; i < NUM_DCHANS; i++) { - if (!pri->dchannels[i]) - break; - dahdi_close_pri_fd(pri, i); - } - ast_log(LOG_ERROR, "Unable to spawn D-channel: %s\n", strerror(errno)); - return -1; - } - return 0; -} - -static char *complete_span_helper(const char *line, const char *word, int pos, int state, int rpos) -{ - int which, span; - char *ret = NULL; - - if (pos != rpos) - return ret; - - for (which = span = 0; span < NUM_SPANS; span++) { - if (pris[span].pri && ++which > state) { - if (asprintf(&ret, "%d", span + 1) < 0) { /* user indexes start from 1 */ - ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); - } - break; - } - } - return ret; -} - -static char *complete_span_4(const char *line, const char *word, int pos, int state) -{ - return complete_span_helper(line,word,pos,state,3); -} - -static char *complete_span_5(const char *line, const char *word, int pos, int state) -{ - return complete_span_helper(line,word,pos,state,4); -} - -static int handle_pri_set_debug_file(int fd, int argc, char **argv) -{ - int myfd; - - if (!strncasecmp(argv[1], "set", 3)) { - if (argc < 5) - return RESULT_SHOWUSAGE; - - if (ast_strlen_zero(argv[4])) - return RESULT_SHOWUSAGE; - - myfd = open(argv[4], O_CREAT|O_WRONLY, 0600); - if (myfd < 0) { - ast_cli(fd, "Unable to open '%s' for writing\n", argv[4]); - return RESULT_SUCCESS; - } - - ast_mutex_lock(&pridebugfdlock); - - if (pridebugfd >= 0) - close(pridebugfd); - - pridebugfd = myfd; - ast_copy_string(pridebugfilename,argv[4],sizeof(pridebugfilename)); - - ast_mutex_unlock(&pridebugfdlock); - - ast_cli(fd, "PRI debug output will be sent to '%s'\n", argv[4]); - } else { - /* Assume it is unset */ - ast_mutex_lock(&pridebugfdlock); - close(pridebugfd); - pridebugfd = -1; - ast_cli(fd, "PRI debug output to file disabled\n"); - ast_mutex_unlock(&pridebugfdlock); - } - - return RESULT_SUCCESS; -} - -#ifdef HAVE_PRI_VERSION -static int handle_pri_version(int fd, int agc, char *argv[]) { - ast_cli(fd, "libpri version: %s\n", pri_get_version()); - return RESULT_SUCCESS; -} -#endif - -static int handle_pri_debug(int fd, int argc, char *argv[]) -{ - int span; - int x; - if (argc < 4) { - return RESULT_SHOWUSAGE; - } - span = atoi(argv[3]); - if ((span < 1) || (span > NUM_SPANS)) { - ast_cli(fd, "Invalid span %s. Should be a number %d to %d\n", argv[3], 1, NUM_SPANS); - return RESULT_SUCCESS; - } - if (!pris[span-1].pri) { - ast_cli(fd, "No PRI running on span %d\n", span); - return RESULT_SUCCESS; - } - for (x = 0; x < NUM_DCHANS; x++) { - if (pris[span-1].dchans[x]) - pri_set_debug(pris[span-1].dchans[x], PRI_DEBUG_APDU | - PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | - PRI_DEBUG_Q921_STATE); - } - ast_cli(fd, "Enabled debugging on span %d\n", span); - return RESULT_SUCCESS; -} - - - -static int handle_pri_no_debug(int fd, int argc, char *argv[]) -{ - int span; - int x; - if (argc < 5) - return RESULT_SHOWUSAGE; - span = atoi(argv[4]); - if ((span < 1) || (span > NUM_SPANS)) { - ast_cli(fd, "Invalid span %s. Should be a number %d to %d\n", argv[4], 1, NUM_SPANS); - return RESULT_SUCCESS; - } - if (!pris[span-1].pri) { - ast_cli(fd, "No PRI running on span %d\n", span); - return RESULT_SUCCESS; - } - for (x = 0; x < NUM_DCHANS; x++) { - if (pris[span-1].dchans[x]) - pri_set_debug(pris[span-1].dchans[x], 0); - } - ast_cli(fd, "Disabled debugging on span %d\n", span); - return RESULT_SUCCESS; -} - -static int handle_pri_really_debug(int fd, int argc, char *argv[]) -{ - int span; - int x; - if (argc < 5) - return RESULT_SHOWUSAGE; - span = atoi(argv[4]); - if ((span < 1) || (span > NUM_SPANS)) { - ast_cli(fd, "Invalid span %s. Should be a number %d to %d\n", argv[4], 1, NUM_SPANS); - return RESULT_SUCCESS; - } - if (!pris[span-1].pri) { - ast_cli(fd, "No PRI running on span %d\n", span); - return RESULT_SUCCESS; - } - for (x = 0; x < NUM_DCHANS; x++) { - if (pris[span-1].dchans[x]) - pri_set_debug(pris[span-1].dchans[x], PRI_DEBUG_APDU | - PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE | - PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_STATE); - } - ast_cli(fd, "Enabled EXTENSIVE debugging on span %d\n", span); - return RESULT_SUCCESS; -} - -static void build_status(char *s, size_t len, int status, int active) -{ - if (!s || len < 1) { - return; - } - s[0] = '\0'; - if (status & DCHAN_PROVISIONED) - strncat(s, "Provisioned, ", len - strlen(s) - 1); - if (!(status & DCHAN_NOTINALARM)) - strncat(s, "In Alarm, ", len - strlen(s) - 1); - if (status & DCHAN_UP) - strncat(s, "Up", len - strlen(s) - 1); - else - strncat(s, "Down", len - strlen(s) - 1); - if (active) - strncat(s, ", Active", len - strlen(s) - 1); - else - strncat(s, ", Standby", len - strlen(s) - 1); - s[len - 1] = '\0'; -} - -static int handle_pri_show_spans(int fd, int argc, char *argv[]) -{ - int span; - int x; - char status[256]; - if (argc != 3) - return RESULT_SHOWUSAGE; - - for (span = 0; span < NUM_SPANS; span++) { - if (pris[span].pri) { - for (x = 0; x < NUM_DCHANS; x++) { - if (pris[span].dchannels[x]) { - build_status(status, sizeof(status), pris[span].dchanavail[x], pris[span].dchans[x] == pris[span].pri); - ast_cli(fd, "PRI span %d/%d: %s\n", span + 1, x, status); - } - } - } - } - return RESULT_SUCCESS; -} - -static int handle_pri_show_span(int fd, int argc, char *argv[]) -{ - int span; - int x; - char status[256]; - if (argc < 4) - return RESULT_SHOWUSAGE; - span = atoi(argv[3]); - if ((span < 1) || (span > NUM_SPANS)) { - ast_cli(fd, "Invalid span '%s'. Should be a number from %d to %d\n", argv[3], 1, NUM_SPANS); - return RESULT_SUCCESS; - } - if (!pris[span-1].pri) { - ast_cli(fd, "No PRI running on span %d\n", span); - return RESULT_SUCCESS; - } - for (x = 0; x < NUM_DCHANS; x++) { - if (pris[span-1].dchannels[x]) { -#ifdef PRI_DUMP_INFO_STR - char *info_str = NULL; -#endif - ast_cli(fd, "%s D-channel: %d\n", pri_order(x), pris[span-1].dchannels[x]); - build_status(status, sizeof(status), pris[span-1].dchanavail[x], pris[span-1].dchans[x] == pris[span-1].pri); - ast_cli(fd, "Status: %s\n", status); -#ifdef PRI_DUMP_INFO_STR - info_str = pri_dump_info_str(pris[span-1].pri); - if (info_str) { - ast_cli(fd, "%s", info_str); - free(info_str); - } -#else - pri_dump_info(pris[span-1].pri); -#endif - ast_cli(fd, "\n"); - } - } - return RESULT_SUCCESS; -} - -static int handle_pri_show_debug(int fd, int argc, char *argv[]) -{ - int x; - int span; - int count=0; - int debug=0; - - for (span = 0; span < NUM_SPANS; span++) { - if (pris[span].pri) { - for (x = 0; x < NUM_DCHANS; x++) { - debug = 0; - if (pris[span].dchans[x]) { - debug = pri_get_debug(pris[span].dchans[x]); - ast_cli(fd, "Span %d: Debug: %s\tIntense: %s\n", span+1, (debug&PRI_DEBUG_Q931_STATE)? "Yes" : "No" ,(debug&PRI_DEBUG_Q921_RAW)? "Yes" : "No" ); - count++; - } - } - } - - } - ast_mutex_lock(&pridebugfdlock); - if (pridebugfd >= 0) - ast_cli(fd, "Logging PRI debug to file %s\n", pridebugfilename); - ast_mutex_unlock(&pridebugfdlock); - - if (!count) - ast_cli(fd, "No debug set or no PRI running\n"); - return RESULT_SUCCESS; -} - -static const char pri_debug_help[] = - "Usage: pri debug span <span>\n" - " Enables debugging on a given PRI span\n"; - -static const char pri_no_debug_help[] = - "Usage: pri no debug span <span>\n" - " Disables debugging on a given PRI span\n"; - -static const char pri_really_debug_help[] = - "Usage: pri intensive debug span <span>\n" - " Enables debugging down to the Q.921 level\n"; - -static const char pri_show_span_help[] = - "Usage: pri show span <span>\n" - " Displays PRI Information on a given PRI span\n"; - -static const char pri_show_spans_help[] = - "Usage: pri show spans\n" - " Displays PRI Information\n"; - -static struct ast_cli_entry dahdi_pri_cli[] = { - { { "pri", "debug", "span", NULL }, - handle_pri_debug, "Enables PRI debugging on a span", - pri_debug_help, complete_span_4 }, - - { { "pri", "no", "debug", "span", NULL }, - handle_pri_no_debug, "Disables PRI debugging on a span", - pri_no_debug_help, complete_span_5 }, - - { { "pri", "intense", "debug", "span", NULL }, - handle_pri_really_debug, "Enables REALLY INTENSE PRI debugging", - pri_really_debug_help, complete_span_5 }, - - { { "pri", "show", "spans", NULL }, - handle_pri_show_spans, "Displays PRI Information", - pri_show_spans_help }, - - { { "pri", "show", "span", NULL }, - handle_pri_show_span, "Displays PRI Information", - pri_show_span_help, complete_span_4 }, - - { { "pri", "show", "debug", NULL }, - handle_pri_show_debug, "Displays current PRI debug settings" }, - - { { "pri", "set", "debug", "file", NULL }, - handle_pri_set_debug_file, "Sends PRI debug output to the specified file" }, - - { { "pri", "unset", "debug", "file", NULL }, - handle_pri_set_debug_file, "Ends PRI debug output to file" }, - -#ifdef HAVE_PRI_VERSION - { { "pri", "show", "version", NULL }, - handle_pri_version, "Displays version of libpri" }, -#endif -}; - -#endif /* HAVE_PRI */ - -static int dahdi_destroy_channel(int fd, int argc, char **argv) -{ - int channel; - - if (argc != 4) - return RESULT_SHOWUSAGE; - - channel = atoi(argv[3]); - - return dahdi_destroy_channel_bynum(channel); -} - -static void dahdi_softhangup_all(void) -{ - struct dahdi_pvt *p; -retry: - ast_mutex_lock(&iflock); - for (p = iflist; p; p = p->next) { - ast_mutex_lock(&p->lock); - if (p->owner && !p->restartpending) { - if (ast_channel_trylock(p->owner)) { - if (option_debug > 2) - ast_verbose("Avoiding deadlock\n"); - /* Avoid deadlock since you're not supposed to lock iflock or pvt before a channel */ - ast_mutex_unlock(&p->lock); - ast_mutex_unlock(&iflock); - goto retry; - } - if (option_debug > 2) - ast_verbose("Softhanging up on %s\n", p->owner->name); - ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_EXPLICIT); - p->restartpending = 1; - num_restart_pending++; - ast_channel_unlock(p->owner); - } - ast_mutex_unlock(&p->lock); - } - ast_mutex_unlock(&iflock); -} - -static int setup_dahdi(int reload); -static int dahdi_restart(void) -{ -#if defined(HAVE_PRI) - int i, j; -#endif - int cancel_code; - struct dahdi_pvt *p; - - ast_mutex_lock(&restart_lock); - - if (option_verbose) - ast_verbose("Destroying channels and reloading DAHDI configuration.\n"); - dahdi_softhangup_all(); - if (option_verbose > 3) - ast_verbose("Initial softhangup of all DAHDI channels complete.\n"); - - #if defined(HAVE_PRI) - for (i = 0; i < NUM_SPANS; i++) { - if (pris[i].master && (pris[i].master != AST_PTHREADT_NULL)) { - cancel_code = pthread_cancel(pris[i].master); - pthread_kill(pris[i].master, SIGURG); - if (option_debug > 3) - ast_verbose("Waiting to join thread of span %d with pid=%p, cancel_code=%d\n", i, (void *) pris[i].master, cancel_code); - pthread_join(pris[i].master, NULL); - if (option_debug > 3) - ast_verbose("Joined thread of span %d\n", i); - } - } - #endif - - ast_mutex_lock(&monlock); - if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) { - cancel_code = pthread_cancel(monitor_thread); - pthread_kill(monitor_thread, SIGURG); - if (option_debug > 3) - ast_verbose("Waiting to join monitor thread with pid=%p, cancel_code=%d\n", (void *) monitor_thread, cancel_code); - pthread_join(monitor_thread, NULL); - if (option_debug > 3) - ast_verbose("Joined monitor thread\n"); - } - monitor_thread = AST_PTHREADT_NULL; /* prepare to restart thread in setup_dahdi once channels are reconfigured */ - - ast_mutex_lock(&ss_thread_lock); - while (ss_thread_count > 0) { /* let ss_threads finish and run dahdi_hangup before dahvi_pvts are destroyed */ - int x = DAHDI_FLASH; - if (option_debug > 2) - ast_verbose("Waiting on %d ss_thread(s) to finish\n", ss_thread_count); - - for (p = iflist; p; p = p->next) { - if (p->owner) - ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); /* important to create an event for dahdi_wait_event to register so that all ss_threads terminate */ - } - ast_cond_wait(&ss_thread_complete, &ss_thread_lock); - } - - /* ensure any created channels before monitor threads were stopped are hungup */ - dahdi_softhangup_all(); - if (option_verbose > 3) - ast_verbose("Final softhangup of all DAHDI channels complete.\n"); - destroy_all_channels(); - if (option_debug) - ast_verbose("Channels destroyed. Now re-reading config. %d active channels remaining.\n", ast_active_channels()); - - ast_mutex_unlock(&monlock); - - #ifdef HAVE_PRI - for (i = 0; i < NUM_SPANS; i++) { - for (j = 0; j < NUM_DCHANS; j++) - dahdi_close_pri_fd(&(pris[i]), j); - } - - memset(pris, 0, sizeof(pris)); - for (i = 0; i < NUM_SPANS; i++) { - ast_mutex_init(&pris[i].lock); - pris[i].offset = -1; - pris[i].master = AST_PTHREADT_NULL; - for (j = 0; j < NUM_DCHANS; j++) - pris[i].fds[j] = -1; - } - pri_set_error(dahdi_pri_error); - pri_set_message(dahdi_pri_message); - #endif - - if (setup_dahdi(2) != 0) { - ast_log(LOG_WARNING, "Reload channels from dahdi config failed!\n"); - ast_mutex_unlock(&ss_thread_lock); - return 1; - } - ast_mutex_unlock(&ss_thread_lock); - ast_mutex_unlock(&restart_lock); - return 0; -} - -static int dahdi_restart_cmd(int fd, int argc, char **argv) -{ - if (argc != 2) { - return RESULT_SHOWUSAGE; - } - - if (dahdi_restart() != 0) - return RESULT_FAILURE; - return RESULT_SUCCESS; -} - -static int dahdi_show_channels(int fd, int argc, char **argv) -{ -#define FORMAT "%7s %-10.10s %-15.15s %-10.10s %-20.20s\n" -#define FORMAT2 "%7s %-10.10s %-15.15s %-10.10s %-20.20s\n" - struct dahdi_pvt *tmp = NULL; - char tmps[20] = ""; - ast_mutex_t *lock; - struct dahdi_pvt *start; -#ifdef HAVE_PRI - int trunkgroup; - struct dahdi_pri *pri = NULL; - int x; -#endif - - lock = &iflock; - start = iflist; - -#ifdef HAVE_PRI - if (argc == 4) { - if ((trunkgroup = atoi(argv[3])) < 1) - return RESULT_SHOWUSAGE; - for (x = 0; x < NUM_SPANS; x++) { - if (pris[x].trunkgroup == trunkgroup) { - pri = pris + x; - break; - } - } - if (pri) { - start = pri->crvs; - lock = &pri->lock; - } else { - ast_cli(fd, "No such trunk group %d\n", trunkgroup); - return RESULT_FAILURE; - } - } else -#endif - if (argc != 3) - return RESULT_SHOWUSAGE; - - ast_mutex_lock(lock); -#ifdef HAVE_PRI - ast_cli(fd, FORMAT2, pri ? "CRV" : "Chan", "Extension", "Context", "Language", "MOH Interpret"); -#else - ast_cli(fd, FORMAT2, "Chan", "Extension", "Context", "Language", "MOH Interpret"); -#endif - - tmp = start; - while (tmp) { - if (tmp->channel > 0) { - snprintf(tmps, sizeof(tmps), "%d", tmp->channel); - } else - ast_copy_string(tmps, "pseudo", sizeof(tmps)); - ast_cli(fd, FORMAT, tmps, tmp->exten, tmp->context, tmp->language, tmp->mohinterpret); - tmp = tmp->next; - } - ast_mutex_unlock(lock); - return RESULT_SUCCESS; -#undef FORMAT -#undef FORMAT2 -} - -static int dahdi_show_channel(int fd, int argc, char **argv) -{ - int channel; - struct dahdi_pvt *tmp = NULL; - struct dahdi_confinfo ci; - struct dahdi_params ps; - int x; - ast_mutex_t *lock; - struct dahdi_pvt *start; -#ifdef HAVE_PRI - char *c; - int trunkgroup; - struct dahdi_pri *pri=NULL; -#endif - - lock = &iflock; - start = iflist; - - if (argc != 4) - return RESULT_SHOWUSAGE; -#ifdef HAVE_PRI - if ((c = strchr(argv[3], ':'))) { - if (sscanf(argv[3], "%d:%d", &trunkgroup, &channel) != 2) - return RESULT_SHOWUSAGE; - if ((trunkgroup < 1) || (channel < 1)) - return RESULT_SHOWUSAGE; - for (x = 0; x < NUM_SPANS; x++) { - if (pris[x].trunkgroup == trunkgroup) { - pri = pris + x; - break; - } - } - if (pri) { - start = pri->crvs; - lock = &pri->lock; - } else { - ast_cli(fd, "No such trunk group %d\n", trunkgroup); - return RESULT_FAILURE; - } - } else -#endif - channel = atoi(argv[3]); - - ast_mutex_lock(lock); - tmp = start; - while (tmp) { - if (tmp->channel == channel) { -#ifdef HAVE_PRI - if (pri) - ast_cli(fd, "Trunk/CRV: %d/%d\n", trunkgroup, tmp->channel); - else -#endif - ast_cli(fd, "Channel: %d\n", tmp->channel); - ast_cli(fd, "File Descriptor: %d\n", tmp->subs[SUB_REAL].dfd); - ast_cli(fd, "Span: %d\n", tmp->span); - ast_cli(fd, "Extension: %s\n", tmp->exten); - ast_cli(fd, "Dialing: %s\n", tmp->dialing ? "yes" : "no"); - ast_cli(fd, "Context: %s\n", tmp->context); - ast_cli(fd, "Caller ID: %s\n", tmp->cid_num); - ast_cli(fd, "Calling TON: %d\n", tmp->cid_ton); - ast_cli(fd, "Caller ID name: %s\n", tmp->cid_name); - ast_cli(fd, "Destroy: %d\n", tmp->destroy); - ast_cli(fd, "InAlarm: %d\n", tmp->inalarm); - ast_cli(fd, "Signalling Type: %s\n", sig2str(tmp->sig)); - ast_cli(fd, "Radio: %d\n", tmp->radio); - ast_cli(fd, "Owner: %s\n", tmp->owner ? tmp->owner->name : "<None>"); - ast_cli(fd, "Real: %s%s%s\n", tmp->subs[SUB_REAL].owner ? tmp->subs[SUB_REAL].owner->name : "<None>", tmp->subs[SUB_REAL].inthreeway ? " (Confed)" : "", tmp->subs[SUB_REAL].linear ? " (Linear)" : ""); - ast_cli(fd, "Callwait: %s%s%s\n", tmp->subs[SUB_CALLWAIT].owner ? tmp->subs[SUB_CALLWAIT].owner->name : "<None>", tmp->subs[SUB_CALLWAIT].inthreeway ? " (Confed)" : "", tmp->subs[SUB_CALLWAIT].linear ? " (Linear)" : ""); - ast_cli(fd, "Threeway: %s%s%s\n", tmp->subs[SUB_THREEWAY].owner ? tmp->subs[SUB_THREEWAY].owner->name : "<None>", tmp->subs[SUB_THREEWAY].inthreeway ? " (Confed)" : "", tmp->subs[SUB_THREEWAY].linear ? " (Linear)" : ""); - ast_cli(fd, "Confno: %d\n", tmp->confno); - ast_cli(fd, "Propagated Conference: %d\n", tmp->propconfno); - ast_cli(fd, "Real in conference: %d\n", tmp->inconference); - ast_cli(fd, "DSP: %s\n", tmp->dsp ? "yes" : "no"); - ast_cli(fd, "Relax DTMF: %s\n", tmp->dtmfrelax ? "yes" : "no"); - ast_cli(fd, "Dialing/CallwaitCAS: %d/%d\n", tmp->dialing, tmp->callwaitcas); - ast_cli(fd, "Default law: %s\n", tmp->law == DAHDI_LAW_MULAW ? "ulaw" : tmp->law == DAHDI_LAW_ALAW ? "alaw" : "unknown"); - ast_cli(fd, "Fax Handled: %s\n", tmp->faxhandled ? "yes" : "no"); - ast_cli(fd, "Pulse phone: %s\n", tmp->pulsedial ? "yes" : "no"); - ast_cli(fd, "Echo Cancellation: %d taps%s, currently %s\n", tmp->echocancel, tmp->echocanbridged ? "" : " unless TDM bridged", tmp->echocanon ? "ON" : "OFF"); - if (tmp->master) - ast_cli(fd, "Master Channel: %d\n", tmp->master->channel); - for (x = 0; x < MAX_SLAVES; x++) { - if (tmp->slaves[x]) - ast_cli(fd, "Slave Channel: %d\n", tmp->slaves[x]->channel); - } -#ifdef HAVE_PRI - if (tmp->pri) { - ast_cli(fd, "PRI Flags: "); - if (tmp->resetting) - ast_cli(fd, "Resetting "); - if (tmp->call) - ast_cli(fd, "Call "); - if (tmp->bearer) - ast_cli(fd, "Bearer "); - ast_cli(fd, "\n"); - if (tmp->logicalspan) - ast_cli(fd, "PRI Logical Span: %d\n", tmp->logicalspan); - else - ast_cli(fd, "PRI Logical Span: Implicit\n"); - } - -#endif - memset(&ci, 0, sizeof(ci)); - ps.channo = tmp->channel; - if (tmp->subs[SUB_REAL].dfd > -1) { - if (!ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_GETCONF, &ci)) { - ast_cli(fd, "Actual Confinfo: Num/%d, Mode/0x%04x\n", ci.confno, ci.confmode); - } -#ifdef DAHDI_GETCONFMUTE - if (!ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_GETCONFMUTE, &x)) { - ast_cli(fd, "Actual Confmute: %s\n", x ? "Yes" : "No"); - } -#endif - if (ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &ps) < 0) { - ast_log(LOG_WARNING, "Failed to get parameters on channel %d: %s\n", tmp->channel, strerror(errno)); - } else { - ast_cli(fd, "Hookstate (FXS only): %s\n", ps.rxisoffhook ? "Offhook" : "Onhook"); - } - } - ast_mutex_unlock(lock); - return RESULT_SUCCESS; - } - tmp = tmp->next; - } - - ast_cli(fd, "Unable to find given channel %d\n", channel); - ast_mutex_unlock(lock); - return RESULT_FAILURE; -} - -static char dahdi_show_cadences_usage[] = -"Usage: dahdi show cadences\n" -" Shows all cadences currently defined\n"; - -static int handle_dahdi_show_cadences(int fd, int argc, char *argv[]) -{ - int i, j; - for (i = 0; i < num_cadence; i++) { - char output[1024]; - char tmp[16], tmp2[64]; - snprintf(tmp, sizeof(tmp), "r%d: ", i + 1); - term_color(output, tmp, COLOR_GREEN, COLOR_BLACK, sizeof(output)); - - for (j = 0; j < 16; j++) { - if (cadences[i].ringcadence[j] == 0) - break; - snprintf(tmp, sizeof(tmp), "%d", cadences[i].ringcadence[j]); - if (cidrings[i] * 2 - 1 == j) - term_color(tmp2, tmp, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp2) - 1); - else - term_color(tmp2, tmp, COLOR_GREEN, COLOR_BLACK, sizeof(tmp2) - 1); - if (j != 0) - strncat(output, ",", sizeof(output) - strlen(output) - 1); - strncat(output, tmp2, sizeof(output) - strlen(output) - 1); - } - ast_cli(fd,"%s\n",output); - } - return 0; -} - -/* Based on irqmiss.c */ -static int dahdi_show_status(int fd, int argc, char *argv[]) { - #define FORMAT "%-40.40s %-10.10s %-10d %-10d %-10d\n" - #define FORMAT2 "%-40.40s %-10.10s %-10.10s %-10.10s %-10.10s\n" - - int span; - int res; - char alarms[50]; - - int ctl; - struct dahdi_spaninfo s; - - if ((ctl = open(DAHDI_FILE_CTL, O_RDWR)) < 0) { - ast_log(LOG_WARNING, "Unable to open " DAHDI_FILE_CTL ": %s\n", strerror(errno)); - ast_cli(fd, "No " DAHDI_NAME " interface found.\n"); - return RESULT_FAILURE; - } - ast_cli(fd, FORMAT2, "Description", "Alarms", "IRQ", "bpviol", "CRC4"); - - for (span = 1; span < DAHDI_MAX_SPANS; ++span) { - s.spanno = span; - res = ioctl(ctl, DAHDI_SPANSTAT, &s); - if (res) { - continue; - } - alarms[0] = '\0'; - if (s.alarms > 0) { - if (s.alarms & DAHDI_ALARM_BLUE) - strcat(alarms, "BLU/"); - if (s.alarms & DAHDI_ALARM_YELLOW) - strcat(alarms, "YEL/"); - if (s.alarms & DAHDI_ALARM_RED) - strcat(alarms, "RED/"); - if (s.alarms & DAHDI_ALARM_LOOPBACK) - strcat(alarms, "LB/"); - if (s.alarms & DAHDI_ALARM_RECOVER) - strcat(alarms, "REC/"); - if (s.alarms & DAHDI_ALARM_NOTOPEN) - strcat(alarms, "NOP/"); - if (!strlen(alarms)) - strcat(alarms, "UUU/"); - if (strlen(alarms)) { - /* Strip trailing / */ - alarms[strlen(alarms) - 1] = '\0'; - } - } else { - if (s.numchans) - strcpy(alarms, "OK"); - else - strcpy(alarms, "UNCONFIGURED"); - } - - ast_cli(fd, FORMAT, s.desc, alarms, s.irqmisses, s.bpvcount, s.crc4count); - } - close(ctl); - - return RESULT_SUCCESS; -#undef FORMAT -#undef FORMAT2 -} - -static char show_channels_usage[] = - "Usage: dahdi show channels\n" - " Shows a list of available channels\n"; - -static char show_channel_usage[] = - "Usage: dahdi show channel <chan num>\n" - " Detailed information about a given channel\n"; - -static char dahdi_show_status_usage[] = - "Usage: dahdi show status\n" - " Shows a list of DAHDI cards with status\n"; - -static char destroy_channel_usage[] = - "Usage: dahdi destroy channel <chan num>\n" - " DON'T USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING. Immediately removes a given channel, whether it is in use or not\n"; - -static char dahdi_restart_usage[] = - "Usage: dahdi restart\n" - " Restarts the DAHDI channels: destroys them all and then\n" - " re-reads them from chan_dahdi.conf.\n" - " Note that this will STOP any running CALL on DAHDI channels.\n" - ""; - -static struct ast_cli_entry cli_zap_show_cadences_deprecated = { - { "zap", "show", "cadences", NULL }, - handle_dahdi_show_cadences, NULL, - NULL }; - -static struct ast_cli_entry cli_zap_show_channels_deprecated = { - { "zap", "show", "channels", NULL }, - dahdi_show_channels, NULL, - NULL }; - -static struct ast_cli_entry cli_zap_show_channel_deprecated = { - { "zap", "show", "channel", NULL }, - dahdi_show_channel, NULL, - NULL }; - -static struct ast_cli_entry cli_zap_destroy_channel_deprecated = { - { "zap", "destroy", "channel", NULL }, - dahdi_destroy_channel, NULL, - NULL }; - -static struct ast_cli_entry cli_zap_restart_deprecated = { - { "zap", "restart", NULL }, - dahdi_restart_cmd, NULL, - NULL }; - -static struct ast_cli_entry cli_zap_show_status_deprecated = { - { "zap", "show", "status", NULL }, - dahdi_show_status, NULL, - NULL }; - -static struct ast_cli_entry dahdi_cli[] = { - { { "dahdi", "show", "cadences", NULL }, - handle_dahdi_show_cadences, "List cadences", - dahdi_show_cadences_usage, NULL, &cli_zap_show_cadences_deprecated }, - - { { "dahdi", "show", "channels", NULL}, - dahdi_show_channels, "Show active DAHDI channels", - show_channels_usage, NULL, &cli_zap_show_channels_deprecated }, - - { { "dahdi", "show", "channel", NULL}, - dahdi_show_channel, "Show information on a channel", - show_channel_usage, NULL, &cli_zap_show_channel_deprecated }, - - { { "dahdi", "destroy", "channel", NULL}, - dahdi_destroy_channel, "Destroy a channel", - destroy_channel_usage, NULL, &cli_zap_destroy_channel_deprecated }, - - { { "dahdi", "restart", NULL}, - dahdi_restart_cmd, "Fully restart DAHDI channels", - dahdi_restart_usage, NULL, &cli_zap_restart_deprecated }, - - { { "dahdi", "show", "status", NULL}, - dahdi_show_status, "Show all DAHDI cards status", - dahdi_show_status_usage, NULL, &cli_zap_show_status_deprecated }, -}; - -#define TRANSFER 0 -#define HANGUP 1 - -static int dahdi_fake_event(struct dahdi_pvt *p, int mode) -{ - if (p) { - switch (mode) { - case TRANSFER: - p->fake_event = DAHDI_EVENT_WINKFLASH; - break; - case HANGUP: - p->fake_event = DAHDI_EVENT_ONHOOK; - break; - default: - ast_log(LOG_WARNING, "I don't know how to handle transfer event with this: %d on channel %s\n",mode, p->owner->name); - } - } - return 0; -} -static struct dahdi_pvt *find_channel(int channel) -{ - struct dahdi_pvt *p = iflist; - while (p) { - if (p->channel == channel) { - break; - } - p = p->next; - } - return p; -} - -#define local_astman_ack(s, m, msg, zap) do { if (!zap) astman_send_ack(s, m, "DAHDI" msg); else astman_send_ack(s, m, "Zap" msg); } while (0) -#define local_astman_header(m, hdr, zap) astman_get_header(m, (!zap) ? "DAHDI" hdr : "Zap" hdr) - -static int __action_dnd(struct mansession *s, const struct message *m, int zap_mode, int dnd) -{ - struct dahdi_pvt *p = NULL; - const char *channel = local_astman_header(m, "Channel", zap_mode); - - if (ast_strlen_zero(channel)) { - astman_send_error(s, m, "No channel specified"); - return 0; - } - if (!(p = find_channel(atoi(channel)))) { - astman_send_error(s, m, "No such channel"); - return 0; - } - p->dnd = dnd; - local_astman_ack(s, m, "DND", zap_mode); - - return 0; -} - -static int zap_action_dndon(struct mansession *s, const struct message *m) -{ - return __action_dnd(s, m, 1, 1); -} - -static int dahdi_action_dndon(struct mansession *s, const struct message *m) -{ - return __action_dnd(s, m, 0, 1); -} - -static int zap_action_dndoff(struct mansession *s, const struct message *m) -{ - return __action_dnd(s, m, 1, 0); -} - -static int dahdi_action_dndoff(struct mansession *s, const struct message *m) -{ - return __action_dnd(s, m, 0, 0); -} - -static int __action_transfer(struct mansession *s, const struct message *m, int zap_mode) -{ - struct dahdi_pvt *p = NULL; - const char *channel = local_astman_header(m, "Channel", zap_mode); - - if (ast_strlen_zero(channel)) { - astman_send_error(s, m, "No channel specified"); - return 0; - } - if (!(p = find_channel(atoi(channel)))) { - astman_send_error(s, m, "No such channel"); - return 0; - } - dahdi_fake_event(p,TRANSFER); - local_astman_ack(s, m, "Transfer", zap_mode); - - return 0; -} - -static int zap_action_transfer(struct mansession *s, const struct message *m) -{ - return __action_transfer(s, m, 1); -} - -static int dahdi_action_transfer(struct mansession *s, const struct message *m) -{ - return __action_transfer(s, m, 0); -} - -static int __action_transferhangup(struct mansession *s, const struct message *m, int zap_mode) -{ - struct dahdi_pvt *p = NULL; - const char *channel = local_astman_header(m, "Channel", zap_mode); - - if (ast_strlen_zero(channel)) { - astman_send_error(s, m, "No channel specified"); - return 0; - } - if (!(p = find_channel(atoi(channel)))) { - astman_send_error(s, m, "No such channel"); - return 0; - } - dahdi_fake_event(p, HANGUP); - local_astman_ack(s, m, "Hangup", zap_mode); - return 0; -} - -static int zap_action_transferhangup(struct mansession *s, const struct message *m) -{ - return __action_transferhangup(s, m, 1); -} - -static int dahdi_action_transferhangup(struct mansession *s, const struct message *m) -{ - return __action_transferhangup(s, m, 0); -} - -static int __action_dialoffhook(struct mansession *s, const struct message *m, int zap_mode) -{ - struct dahdi_pvt *p = NULL; - const char *channel = local_astman_header(m, "Channel", zap_mode); - const char *number = astman_get_header(m, "Number"); - int i; - - if (ast_strlen_zero(channel)) { - astman_send_error(s, m, "No channel specified"); - return 0; - } - if (ast_strlen_zero(number)) { - astman_send_error(s, m, "No number specified"); - return 0; - } - if (!(p = find_channel(atoi(channel)))) { - astman_send_error(s, m, "No such channel"); - return 0; - } - if (!p->owner) { - astman_send_error(s, m, "Channel does not have an owner"); - return 0; - } - for (i = 0; i < strlen(number); i++) { - struct ast_frame f = { AST_FRAME_DTMF, number[i] }; - - dahdi_queue_frame(p, &f, NULL); - } - local_astman_ack(s, m, "DialOffHook", zap_mode); - - return 0; -} - -static int zap_action_dialoffhook(struct mansession *s, const struct message *m) -{ - return __action_dialoffhook(s, m, 1); -} - -static int dahdi_action_dialoffhook(struct mansession *s, const struct message *m) -{ - return __action_dialoffhook(s, m, 0); -} - -static int __action_showchannels(struct mansession *s, const struct message *m, int zap_mode) -{ - struct dahdi_pvt *tmp = NULL; - const char *id = astman_get_header(m, "ActionID"); - char idText[256] = ""; - - local_astman_ack(s, m, " channel status will follow", zap_mode); - if (!ast_strlen_zero(id)) - snprintf(idText, sizeof(idText) - 1, "ActionID: %s\r\n", id); - - ast_mutex_lock(&iflock); - - tmp = iflist; - while (tmp) { - if (tmp->channel > 0) { - int alarm = get_alarms(tmp); - astman_append(s, - "Event: %sShowChannels\r\n" - "Channel: %d\r\n" - "Signalling: %s\r\n" - "Context: %s\r\n" - "DND: %s\r\n" - "Alarm: %s\r\n" - "%s" - "\r\n", - dahdi_chan_name, - tmp->channel, sig2str(tmp->sig), tmp->context, - tmp->dnd ? "Enabled" : "Disabled", - alarm2str(alarm), idText); - } - - tmp = tmp->next; - } - - ast_mutex_unlock(&iflock); - - astman_append(s, - "Event: %sShowChannelsComplete\r\n" - "%s" - "\r\n", - dahdi_chan_name, - idText); - return 0; -} - -static int zap_action_showchannels(struct mansession *s, const struct message *m) -{ - return __action_showchannels(s, m, 1); -} - -static int dahdi_action_showchannels(struct mansession *s, const struct message *m) -{ - return __action_showchannels(s, m, 0); -} - -static int __action_restart(struct mansession *s, const struct message *m, int zap_mode) -{ - if (dahdi_restart() != 0) { - if (zap_mode) { - astman_send_error(s, m, "Failed to restart Zap"); - } else { - astman_send_error(s, m, "Failed to restart DAHDI"); - } - return 1; - } - local_astman_ack(s, m, "Restart: Success", zap_mode); - return 0; -} - -static int zap_action_restart(struct mansession *s, const struct message *m) -{ - return __action_restart(s, m, 1); -} - -static int dahdi_action_restart(struct mansession *s, const struct message *m) -{ - return __action_restart(s, m, 0); -} - -#define local_astman_unregister(a) do { \ - if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) { \ - ast_manager_unregister("DAHDI" a); \ - } \ - ast_manager_unregister("Zap" a); \ - } while (0) - -static int __unload_module(void) -{ - struct dahdi_pvt *p; - -#ifdef HAVE_PRI - int i, j; - for (i = 0; i < NUM_SPANS; i++) { - if (pris[i].master != AST_PTHREADT_NULL) - pthread_cancel(pris[i].master); - } - ast_cli_unregister_multiple(dahdi_pri_cli, sizeof(dahdi_pri_cli) / sizeof(struct ast_cli_entry)); - - if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) { - ast_unregister_application(dahdi_send_keypad_facility_app); - } - ast_unregister_application(zap_send_keypad_facility_app); -#endif - ast_cli_unregister_multiple(dahdi_cli, sizeof(dahdi_cli) / sizeof(struct ast_cli_entry)); - local_astman_unregister("DialOffHook"); - local_astman_unregister("Hangup"); - local_astman_unregister("Transfer"); - local_astman_unregister("DNDoff"); - local_astman_unregister("DNDon"); - local_astman_unregister("ShowChannels"); - local_astman_unregister("Restart"); - ast_channel_unregister(chan_tech); - ast_mutex_lock(&iflock); - /* Hangup all interfaces if they have an owner */ - p = iflist; - while (p) { - if (p->owner) - ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD); - p = p->next; - } - ast_mutex_unlock(&iflock); - ast_mutex_lock(&monlock); - if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) { - pthread_cancel(monitor_thread); - pthread_kill(monitor_thread, SIGURG); - pthread_join(monitor_thread, NULL); - } - monitor_thread = AST_PTHREADT_STOP; - ast_mutex_unlock(&monlock); - - destroy_all_channels(); -#ifdef HAVE_PRI - for (i = 0; i < NUM_SPANS; i++) { - if (pris[i].master && (pris[i].master != AST_PTHREADT_NULL)) - pthread_join(pris[i].master, NULL); - for (j = 0; j < NUM_DCHANS; j++) { - dahdi_close_pri_fd(&(pris[i]), j); - } - } -#endif - ast_cond_destroy(&ss_thread_complete); - return 0; -} - -static int unload_module(void) -{ -#ifdef HAVE_PRI - int y; - for (y = 0; y < NUM_SPANS; y++) - ast_mutex_destroy(&pris[y].lock); -#endif - return __unload_module(); -} - -static int build_channels(struct dahdi_chan_conf *conf, int iscrv, const char *value, int reload, int lineno, int *found_pseudo) -{ - char *c, *chan; - int x, start, finish; - struct dahdi_pvt *tmp; -#ifdef HAVE_PRI - struct dahdi_pri *pri; - int trunkgroup, y; -#endif - - if ((reload == 0) && (conf->chan.sig < 0)) { - ast_log(LOG_ERROR, "Signalling must be specified before any channels are.\n"); - return -1; - } - - c = ast_strdupa(value); - -#ifdef HAVE_PRI - pri = NULL; - if (iscrv) { - if (sscanf(c, "%d:%n", &trunkgroup, &y) != 1) { - ast_log(LOG_WARNING, "CRV must begin with trunkgroup followed by a colon at line %d\n", lineno); - return -1; - } - if (trunkgroup < 1) { - ast_log(LOG_WARNING, "CRV trunk group must be a positive number at line %d\n", lineno); - return -1; - } - c += y; - for (y = 0; y < NUM_SPANS; y++) { - if (pris[y].trunkgroup == trunkgroup) { - pri = pris + y; - break; - } - } - if (!pri) { - ast_log(LOG_WARNING, "No such trunk group %d at CRV declaration at line %d\n", trunkgroup, lineno); - return -1; - } - } -#endif - - while ((chan = strsep(&c, ","))) { - if (sscanf(chan, "%d-%d", &start, &finish) == 2) { - /* Range */ - } else if (sscanf(chan, "%d", &start)) { - /* Just one */ - finish = start; - } else if (!strcasecmp(chan, "pseudo")) { - finish = start = CHAN_PSEUDO; - if (found_pseudo) - *found_pseudo = 1; - } else { - ast_log(LOG_ERROR, "Syntax error parsing '%s' at '%s'\n", value, chan); - return -1; - } - if (finish < start) { - ast_log(LOG_WARNING, "Sillyness: %d < %d\n", start, finish); - x = finish; - finish = start; - start = x; - } - - for (x = start; x <= finish; x++) { -#ifdef HAVE_PRI - tmp = mkintf(x, conf, pri, reload); -#else - tmp = mkintf(x, conf, NULL, reload); -#endif - - if (tmp) { - if (option_verbose > 2) { -#ifdef HAVE_PRI - if (pri) - ast_verbose(VERBOSE_PREFIX_3 "%s CRV %d:%d, %s signalling\n", reload ? "Reconfigured" : "Registered", trunkgroup, x, sig2str(tmp->sig)); - else -#endif - ast_verbose(VERBOSE_PREFIX_3 "%s channel %d, %s signalling\n", reload ? "Reconfigured" : "Registered", x, sig2str(tmp->sig)); - } - } else { - ast_log(LOG_ERROR, "Unable to %s channel '%s'\n", - (reload == 1) ? "reconfigure" : "register", value); - return -1; - } - } - } - - return 0; -} - -/** The length of the parameters list of 'dahdichan'. - * \todo Move definition of MAX_CHANLIST_LEN to a proper place. */ -#define MAX_CHANLIST_LEN 80 -static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct ast_variable *v, int reload, int skipchannels) -{ - struct dahdi_pvt *tmp; - int y; - int found_pseudo = 0; - char dahdichan[MAX_CHANLIST_LEN] = {}; - - for (; v; v = v->next) { - if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) - continue; - - /* Create the interface list */ - if (!strcasecmp(v->name, "channel") -#ifdef HAVE_PRI - || !strcasecmp(v->name, "crv") -#endif - ) { - int iscrv; - if (skipchannels) - continue; - iscrv = !strcasecmp(v->name, "crv"); - if (build_channels(confp, iscrv, v->value, reload, v->lineno, &found_pseudo)) - return -1; - } else if (!strcasecmp(v->name, "buffers")) { - int res; - char policy[21] = ""; - - res = sscanf(v->value, "%d,%20s", &confp->chan.buf_no, policy); - if (res != 2) { - ast_log(LOG_WARNING, "Parsing buffers option data failed, using defaults.\n"); - confp->chan.buf_no = numbufs; - continue; - } - if (confp->chan.buf_no < 0) - confp->chan.buf_no = numbufs; - if (!strcasecmp(policy, "full")) { - confp->chan.buf_policy = DAHDI_POLICY_WHEN_FULL; - } else if (!strcasecmp(policy, "immediate")) { - confp->chan.buf_policy = DAHDI_POLICY_IMMEDIATE; - } else { - ast_log(LOG_WARNING, "Invalid policy name given (%s).\n", policy); - } - } else if (!strcasecmp(v->name, "zapchan") || !strcasecmp(v->name, "dahdichan")) { - ast_copy_string(dahdichan, v->value, sizeof(dahdichan)); - if (v->name[0] == 'z' || v->name[0] == 'Z') { - ast_log(LOG_WARNING, "Option zapchan has been deprecated in favor of dahdichan (found in [%s])\n", cat); - } - } else if (!strcasecmp(v->name, "usedistinctiveringdetection")) { - if (ast_true(v->value)) - confp->chan.usedistinctiveringdetection = 1; - } else if (!strcasecmp(v->name, "distinctiveringaftercid")) { - if (ast_true(v->value)) - distinctiveringaftercid = 1; - } else if (!strcasecmp(v->name, "dring1context")) { - ast_copy_string(drings.ringContext[0].contextData, v->value, sizeof(drings.ringContext[0].contextData)); - } else if (!strcasecmp(v->name, "dring2context")) { - ast_copy_string(drings.ringContext[1].contextData, v->value, sizeof(drings.ringContext[1].contextData)); - } else if (!strcasecmp(v->name, "dring3context")) { - ast_copy_string(drings.ringContext[2].contextData, v->value, sizeof(drings.ringContext[2].contextData)); - } else if (!strcasecmp(v->name, "dring1")) { - sscanf(v->value, "%d,%d,%d", &drings.ringnum[0].ring[0], &drings.ringnum[0].ring[1], &drings.ringnum[0].ring[2]); - } else if (!strcasecmp(v->name, "dring2")) { - sscanf(v->value, "%d,%d,%d", &drings.ringnum[1].ring[0], &drings.ringnum[1].ring[1], &drings.ringnum[1].ring[2]); - } else if (!strcasecmp(v->name, "dring3")) { - sscanf(v->value, "%d,%d,%d", &drings.ringnum[2].ring[0], &drings.ringnum[2].ring[1], &drings.ringnum[2].ring[2]); - } else if (!strcasecmp(v->name, "usecallerid")) { - confp->chan.use_callerid = ast_true(v->value); - } else if (!strcasecmp(v->name, "cidsignalling")) { - if (!strcasecmp(v->value, "bell")) - confp->chan.cid_signalling = CID_SIG_BELL; - else if (!strcasecmp(v->value, "v23")) - confp->chan.cid_signalling = CID_SIG_V23; - else if (!strcasecmp(v->value, "dtmf")) - confp->chan.cid_signalling = CID_SIG_DTMF; - else if (!strcasecmp(v->value, "smdi")) - confp->chan.cid_signalling = CID_SIG_SMDI; - else if (!strcasecmp(v->value, "v23_jp")) - confp->chan.cid_signalling = CID_SIG_V23_JP; - else if (ast_true(v->value)) - confp->chan.cid_signalling = CID_SIG_BELL; - } else if (!strcasecmp(v->name, "cidstart")) { - if (!strcasecmp(v->value, "ring")) - confp->chan.cid_start = CID_START_RING; - else if (!strcasecmp(v->value, "polarity")) - confp->chan.cid_start = CID_START_POLARITY; - else if (ast_true(v->value)) - confp->chan.cid_start = CID_START_RING; - } else if (!strcasecmp(v->name, "threewaycalling")) { - confp->chan.threewaycalling = ast_true(v->value); - } else if (!strcasecmp(v->name, "cancallforward")) { - confp->chan.cancallforward = ast_true(v->value); - } else if (!strcasecmp(v->name, "relaxdtmf")) { - if (ast_true(v->value)) - confp->chan.dtmfrelax = DSP_DIGITMODE_RELAXDTMF; - else - confp->chan.dtmfrelax = 0; - } else if (!strcasecmp(v->name, "mailbox")) { - ast_copy_string(confp->chan.mailbox, v->value, sizeof(confp->chan.mailbox)); - } else if (!strcasecmp(v->name, "hasvoicemail")) { - if (ast_true(v->value) && ast_strlen_zero(confp->chan.mailbox)) { - ast_copy_string(confp->chan.mailbox, cat, sizeof(confp->chan.mailbox)); - } - } else if (!strcasecmp(v->name, "adsi")) { - confp->chan.adsi = ast_true(v->value); - } else if (!strcasecmp(v->name, "usesmdi")) { - confp->chan.use_smdi = ast_true(v->value); - } else if (!strcasecmp(v->name, "smdiport")) { - ast_copy_string(confp->smdi_port, v->value, sizeof(confp->smdi_port)); - } else if (!strcasecmp(v->name, "transfer")) { - confp->chan.transfer = ast_true(v->value); - } else if (!strcasecmp(v->name, "canpark")) { - confp->chan.canpark = ast_true(v->value); - } else if (!strcasecmp(v->name, "echocancelwhenbridged")) { - confp->chan.echocanbridged = ast_true(v->value); - } else if (!strcasecmp(v->name, "busydetect")) { - confp->chan.busydetect = ast_true(v->value); - } else if (!strcasecmp(v->name, "busycount")) { - confp->chan.busycount = atoi(v->value); - } else if (!strcasecmp(v->name, "busypattern")) { - if (sscanf(v->value, "%d,%d", &confp->chan.busy_tonelength, &confp->chan.busy_quietlength) != 2) { - ast_log(LOG_ERROR, "busypattern= expects busypattern=tonelength,quietlength\n"); - } - } else if (!strcasecmp(v->name, "callprogress")) { - if (ast_true(v->value)) - confp->chan.callprogress |= 1; - else - confp->chan.callprogress &= ~1; - } else if (!strcasecmp(v->name, "faxdetect")) { - if (!strcasecmp(v->value, "incoming")) { - confp->chan.callprogress |= 4; - confp->chan.callprogress &= ~2; - } else if (!strcasecmp(v->value, "outgoing")) { - confp->chan.callprogress &= ~4; - confp->chan.callprogress |= 2; - } else if (!strcasecmp(v->value, "both") || ast_true(v->value)) - confp->chan.callprogress |= 6; - else - confp->chan.callprogress &= ~6; - } else if (!strcasecmp(v->name, "echocancel")) { - if (!ast_strlen_zero(v->value)) { - y = atoi(v->value); - } else - y = 0; - if ((y == 32) || (y == 64) || (y == 128) || (y == 256) || (y == 512) || (y == 1024)) - confp->chan.echocancel = y; - else { - confp->chan.echocancel = ast_true(v->value); - if (confp->chan.echocancel) - confp->chan.echocancel=128; - } - } else if (!strcasecmp(v->name, "echotraining")) { - if (sscanf(v->value, "%d", &y) == 1) { - if ((y < 10) || (y > 4000)) { - ast_log(LOG_WARNING, "Echo training time must be within the range of 10 to 4000 ms at line %d\n", v->lineno); - } else { - confp->chan.echotraining = y; - } - } else if (ast_true(v->value)) { - confp->chan.echotraining = 400; - } else - confp->chan.echotraining = 0; - } else if (!strcasecmp(v->name, "hidecallerid")) { - confp->chan.hidecallerid = ast_true(v->value); - } else if (!strcasecmp(v->name, "hidecalleridname")) { - confp->chan.hidecalleridname = ast_true(v->value); - } else if (!strcasecmp(v->name, "pulsedial")) { - confp->chan.pulse = ast_true(v->value); - } else if (!strcasecmp(v->name, "callreturn")) { - confp->chan.callreturn = ast_true(v->value); - } else if (!strcasecmp(v->name, "callwaiting")) { - confp->chan.callwaiting = ast_true(v->value); - } else if (!strcasecmp(v->name, "callwaitingcallerid")) { - confp->chan.callwaitingcallerid = ast_true(v->value); - } else if (!strcasecmp(v->name, "context")) { - ast_copy_string(confp->chan.context, v->value, sizeof(confp->chan.context)); - } else if (!strcasecmp(v->name, "language")) { - ast_copy_string(confp->chan.language, v->value, sizeof(confp->chan.language)); - } else if (!strcasecmp(v->name, "progzone")) { - ast_copy_string(progzone, v->value, sizeof(progzone)); - } else if (!strcasecmp(v->name, "mohinterpret") - ||!strcasecmp(v->name, "musiconhold") || !strcasecmp(v->name, "musicclass")) { - ast_copy_string(confp->chan.mohinterpret, v->value, sizeof(confp->chan.mohinterpret)); - } else if (!strcasecmp(v->name, "mohsuggest")) { - ast_copy_string(confp->chan.mohsuggest, v->value, sizeof(confp->chan.mohsuggest)); - } else if (!strcasecmp(v->name, "stripmsd")) { - confp->chan.stripmsd = atoi(v->value); - } else if (!strcasecmp(v->name, "jitterbuffers")) { - numbufs = atoi(v->value); - } else if (!strcasecmp(v->name, "group")) { - confp->chan.group = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "callgroup")) { - confp->chan.callgroup = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "pickupgroup")) { - confp->chan.pickupgroup = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "immediate")) { - confp->chan.immediate = ast_true(v->value); - } else if (!strcasecmp(v->name, "transfertobusy")) { - confp->chan.transfertobusy = ast_true(v->value); - } else if (!strcasecmp(v->name, "rxgain")) { - if (sscanf(v->value, "%f", &confp->chan.rxgain) != 1) { - ast_log(LOG_WARNING, "Invalid rxgain: %s\n", v->value); - } - } else if (!strcasecmp(v->name, "txgain")) { - if (sscanf(v->value, "%f", &confp->chan.txgain) != 1) { - ast_log(LOG_WARNING, "Invalid txgain: %s\n", v->value); - } - } else if (!strcasecmp(v->name, "tonezone")) { - if (sscanf(v->value, "%d", &confp->chan.tonezone) != 1) { - ast_log(LOG_WARNING, "Invalid tonezone: %s\n", v->value); - } - } else if (!strcasecmp(v->name, "callerid")) { - if (!strcasecmp(v->value, "asreceived")) { - confp->chan.cid_num[0] = '\0'; - confp->chan.cid_name[0] = '\0'; - } else { - ast_callerid_split(v->value, confp->chan.cid_name, sizeof(confp->chan.cid_name), confp->chan.cid_num, sizeof(confp->chan.cid_num)); - } - } else if (!strcasecmp(v->name, "fullname")) { - ast_copy_string(confp->chan.cid_name, v->value, sizeof(confp->chan.cid_name)); - } else if (!strcasecmp(v->name, "cid_number")) { - ast_copy_string(confp->chan.cid_num, v->value, sizeof(confp->chan.cid_num)); - } else if (!strcasecmp(v->name, "useincomingcalleridondahditransfer") || !strcasecmp(v->name, "useincomingcalleridonzaptransfer")) { - confp->chan.dahditrcallerid = ast_true(v->value); - if (strstr(v->name, "zap")) { - ast_log(LOG_WARNING, "Option useincomingcalleridonzaptransfer has been deprecated in favor of useincomingcalleridondahditransfer (in [%s]).\n", cat); - } - } else if (!strcasecmp(v->name, "restrictcid")) { - confp->chan.restrictcid = ast_true(v->value); - } else if (!strcasecmp(v->name, "usecallingpres")) { - confp->chan.use_callingpres = ast_true(v->value); - } else if (!strcasecmp(v->name, "accountcode")) { - ast_copy_string(confp->chan.accountcode, v->value, sizeof(confp->chan.accountcode)); - } else if (!strcasecmp(v->name, "amaflags")) { - y = ast_cdr_amaflags2int(v->value); - if (y < 0) - ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno); - else - confp->chan.amaflags = y; - } else if (!strcasecmp(v->name, "polarityonanswerdelay")) { - confp->chan.polarityonanswerdelay = atoi(v->value); - } else if (!strcasecmp(v->name, "answeronpolarityswitch")) { - confp->chan.answeronpolarityswitch = ast_true(v->value); - } else if (!strcasecmp(v->name, "hanguponpolarityswitch")) { - confp->chan.hanguponpolarityswitch = ast_true(v->value); - } else if (!strcasecmp(v->name, "sendcalleridafter")) { - confp->chan.sendcalleridafter = atoi(v->value); - } else if (reload != 1){ - if (!strcasecmp(v->name, "signalling")) { - confp->chan.outsigmod = -1; - if (!strcasecmp(v->value, "em")) { - confp->chan.sig = SIG_EM; - } else if (!strcasecmp(v->value, "em_e1")) { - confp->chan.sig = SIG_EM_E1; - } else if (!strcasecmp(v->value, "em_w")) { - confp->chan.sig = SIG_EMWINK; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "fxs_ls")) { - confp->chan.sig = SIG_FXSLS; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "fxs_gs")) { - confp->chan.sig = SIG_FXSGS; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "fxs_ks")) { - confp->chan.sig = SIG_FXSKS; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "fxo_ls")) { - confp->chan.sig = SIG_FXOLS; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "fxo_gs")) { - confp->chan.sig = SIG_FXOGS; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "fxo_ks")) { - confp->chan.sig = SIG_FXOKS; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "fxs_rx")) { - confp->chan.sig = SIG_FXSKS; - confp->chan.radio = 1; - } else if (!strcasecmp(v->value, "fxo_rx")) { - confp->chan.sig = SIG_FXOLS; - confp->chan.radio = 1; - } else if (!strcasecmp(v->value, "fxs_tx")) { - confp->chan.sig = SIG_FXSLS; - confp->chan.radio = 1; - } else if (!strcasecmp(v->value, "fxo_tx")) { - confp->chan.sig = SIG_FXOGS; - confp->chan.radio = 1; - } else if (!strcasecmp(v->value, "em_rx")) { - confp->chan.sig = SIG_EM; - confp->chan.radio = 1; - } else if (!strcasecmp(v->value, "em_tx")) { - confp->chan.sig = SIG_EM; - confp->chan.radio = 1; - } else if (!strcasecmp(v->value, "em_rxtx")) { - confp->chan.sig = SIG_EM; - confp->chan.radio = 2; - } else if (!strcasecmp(v->value, "em_txrx")) { - confp->chan.sig = SIG_EM; - confp->chan.radio = 2; - } else if (!strcasecmp(v->value, "sf")) { - confp->chan.sig = SIG_SF; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "sf_w")) { - confp->chan.sig = SIG_SFWINK; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "sf_featd")) { - confp->chan.sig = SIG_FEATD; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "sf_featdmf")) { - confp->chan.sig = SIG_FEATDMF; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "sf_featb")) { - confp->chan.sig = SIG_SF_FEATB; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "sf")) { - confp->chan.sig = SIG_SF; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "sf_rx")) { - confp->chan.sig = SIG_SF; - confp->chan.radio = 1; - } else if (!strcasecmp(v->value, "sf_tx")) { - confp->chan.sig = SIG_SF; - confp->chan.radio = 1; - } else if (!strcasecmp(v->value, "sf_rxtx")) { - confp->chan.sig = SIG_SF; - confp->chan.radio = 2; - } else if (!strcasecmp(v->value, "sf_txrx")) { - confp->chan.sig = SIG_SF; - confp->chan.radio = 2; - } else if (!strcasecmp(v->value, "featd")) { - confp->chan.sig = SIG_FEATD; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "featdmf")) { - confp->chan.sig = SIG_FEATDMF; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "featdmf_ta")) { - confp->chan.sig = SIG_FEATDMF_TA; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "e911")) { - confp->chan.sig = SIG_E911; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "fgccama")) { - confp->chan.sig = SIG_FGC_CAMA; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "fgccamamf")) { - confp->chan.sig = SIG_FGC_CAMAMF; - confp->chan.radio = 0; - } else if (!strcasecmp(v->value, "featb")) { - confp->chan.sig = SIG_FEATB; - confp->chan.radio = 0; -#ifdef HAVE_PRI - } else if (!strcasecmp(v->value, "pri_net")) { - confp->chan.radio = 0; - confp->chan.sig = SIG_PRI; - confp->pri.nodetype = PRI_NETWORK; - } else if (!strcasecmp(v->value, "pri_cpe")) { - confp->chan.sig = SIG_PRI; - confp->chan.radio = 0; - confp->pri.nodetype = PRI_CPE; - } else if (!strcasecmp(v->value, "gr303fxoks_net")) { - confp->chan.sig = SIG_GR303FXOKS; - confp->chan.radio = 0; - confp->pri.nodetype = PRI_NETWORK; - } else if (!strcasecmp(v->value, "gr303fxsks_cpe")) { - confp->chan.sig = SIG_GR303FXSKS; - confp->chan.radio = 0; - confp->pri.nodetype = PRI_CPE; -#endif - } else { - ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value); - } - } else if (!strcasecmp(v->name, "outsignalling")) { - if (!strcasecmp(v->value, "em")) { - confp->chan.outsigmod = SIG_EM; - } else if (!strcasecmp(v->value, "em_e1")) { - confp->chan.outsigmod = SIG_EM_E1; - } else if (!strcasecmp(v->value, "em_w")) { - confp->chan.outsigmod = SIG_EMWINK; - } else if (!strcasecmp(v->value, "sf")) { - confp->chan.outsigmod = SIG_SF; - } else if (!strcasecmp(v->value, "sf_w")) { - confp->chan.outsigmod = SIG_SFWINK; - } else if (!strcasecmp(v->value, "sf_featd")) { - confp->chan.outsigmod = SIG_FEATD; - } else if (!strcasecmp(v->value, "sf_featdmf")) { - confp->chan.outsigmod = SIG_FEATDMF; - } else if (!strcasecmp(v->value, "sf_featb")) { - confp->chan.outsigmod = SIG_SF_FEATB; - } else if (!strcasecmp(v->value, "sf")) { - confp->chan.outsigmod = SIG_SF; - } else if (!strcasecmp(v->value, "featd")) { - confp->chan.outsigmod = SIG_FEATD; - } else if (!strcasecmp(v->value, "featdmf")) { - confp->chan.outsigmod = SIG_FEATDMF; - } else if (!strcasecmp(v->value, "featdmf_ta")) { - confp->chan.outsigmod = SIG_FEATDMF_TA; - } else if (!strcasecmp(v->value, "e911")) { - confp->chan.outsigmod = SIG_E911; - } else if (!strcasecmp(v->value, "fgccama")) { - confp->chan.outsigmod = SIG_FGC_CAMA; - } else if (!strcasecmp(v->value, "fgccamamf")) { - confp->chan.outsigmod = SIG_FGC_CAMAMF; - } else if (!strcasecmp(v->value, "featb")) { - confp->chan.outsigmod = SIG_FEATB; - } else { - ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value); - } -#ifdef HAVE_PRI - } else if (!strcasecmp(v->name, "pridialplan")) { - if (!strcasecmp(v->value, "national")) { - confp->pri.dialplan = PRI_NATIONAL_ISDN + 1; - } else if (!strcasecmp(v->value, "unknown")) { - confp->pri.dialplan = PRI_UNKNOWN + 1; - } else if (!strcasecmp(v->value, "private")) { - confp->pri.dialplan = PRI_PRIVATE + 1; - } else if (!strcasecmp(v->value, "international")) { - confp->pri.dialplan = PRI_INTERNATIONAL_ISDN + 1; - } else if (!strcasecmp(v->value, "local")) { - confp->pri.dialplan = PRI_LOCAL_ISDN + 1; - } else if (!strcasecmp(v->value, "dynamic")) { - confp->pri.dialplan = -1; - } else { - ast_log(LOG_WARNING, "Unknown PRI dialplan '%s' at line %d.\n", v->value, v->lineno); - } - } else if (!strcasecmp(v->name, "prilocaldialplan")) { - if (!strcasecmp(v->value, "national")) { - confp->pri.localdialplan = PRI_NATIONAL_ISDN + 1; - } else if (!strcasecmp(v->value, "unknown")) { - confp->pri.localdialplan = PRI_UNKNOWN + 1; - } else if (!strcasecmp(v->value, "private")) { - confp->pri.localdialplan = PRI_PRIVATE + 1; - } else if (!strcasecmp(v->value, "international")) { - confp->pri.localdialplan = PRI_INTERNATIONAL_ISDN + 1; - } else if (!strcasecmp(v->value, "local")) { - confp->pri.localdialplan = PRI_LOCAL_ISDN + 1; - } else if (!strcasecmp(v->value, "dynamic")) { - confp->pri.localdialplan = -1; - } else { - ast_log(LOG_WARNING, "Unknown PRI dialplan '%s' at line %d.\n", v->value, v->lineno); - } - } else if (!strcasecmp(v->name, "switchtype")) { - if (!strcasecmp(v->value, "national")) - confp->pri.switchtype = PRI_SWITCH_NI2; - else if (!strcasecmp(v->value, "ni1")) - confp->pri.switchtype = PRI_SWITCH_NI1; - else if (!strcasecmp(v->value, "dms100")) - confp->pri.switchtype = PRI_SWITCH_DMS100; - else if (!strcasecmp(v->value, "4ess")) - confp->pri.switchtype = PRI_SWITCH_ATT4ESS; - else if (!strcasecmp(v->value, "5ess")) - confp->pri.switchtype = PRI_SWITCH_LUCENT5E; - else if (!strcasecmp(v->value, "euroisdn")) - confp->pri.switchtype = PRI_SWITCH_EUROISDN_E1; - else if (!strcasecmp(v->value, "qsig")) - confp->pri.switchtype = PRI_SWITCH_QSIG; - else { - ast_log(LOG_ERROR, "Unknown switchtype '%s'\n", v->value); - return -1; - } - } else if (!strcasecmp(v->name, "nsf")) { - if (!strcasecmp(v->value, "sdn")) - confp->pri.nsf = PRI_NSF_SDN; - else if (!strcasecmp(v->value, "megacom")) - confp->pri.nsf = PRI_NSF_MEGACOM; - else if (!strcasecmp(v->value, "tollfreemegacom")) - confp->pri.nsf = PRI_NSF_TOLL_FREE_MEGACOM; - else if (!strcasecmp(v->value, "accunet")) - confp->pri.nsf = PRI_NSF_ACCUNET; - else if (!strcasecmp(v->value, "none")) - confp->pri.nsf = PRI_NSF_NONE; - else { - ast_log(LOG_WARNING, "Unknown network-specific facility '%s'\n", v->value); - confp->pri.nsf = PRI_NSF_NONE; - } - } else if (!strcasecmp(v->name, "priindication")) { - if (!strcasecmp(v->value, "outofband")) - confp->chan.priindication_oob = 1; - else if (!strcasecmp(v->value, "inband")) - confp->chan.priindication_oob = 0; - else - ast_log(LOG_WARNING, "'%s' is not a valid pri indication value, should be 'inband' or 'outofband' at line %d\n", - v->value, v->lineno); - } else if (!strcasecmp(v->name, "priexclusive")) { - confp->chan.priexclusive = ast_true(v->value); - } else if (!strcasecmp(v->name, "internationalprefix")) { - ast_copy_string(confp->pri.internationalprefix, v->value, sizeof(confp->pri.internationalprefix)); - } else if (!strcasecmp(v->name, "nationalprefix")) { - ast_copy_string(confp->pri.nationalprefix, v->value, sizeof(confp->pri.nationalprefix)); - } else if (!strcasecmp(v->name, "localprefix")) { - ast_copy_string(confp->pri.localprefix, v->value, sizeof(confp->pri.localprefix)); - } else if (!strcasecmp(v->name, "privateprefix")) { - ast_copy_string(confp->pri.privateprefix, v->value, sizeof(confp->pri.privateprefix)); - } else if (!strcasecmp(v->name, "unknownprefix")) { - ast_copy_string(confp->pri.unknownprefix, v->value, sizeof(confp->pri.unknownprefix)); - } else if (!strcasecmp(v->name, "resetinterval")) { - if (!strcasecmp(v->value, "never")) - confp->pri.resetinterval = -1; - else if (atoi(v->value) >= 60) - confp->pri.resetinterval = atoi(v->value); - else - ast_log(LOG_WARNING, "'%s' is not a valid reset interval, should be >= 60 seconds or 'never' at line %d\n", - v->value, v->lineno); - } else if (!strcasecmp(v->name, "minunused")) { - confp->pri.minunused = atoi(v->value); - } else if (!strcasecmp(v->name, "minidle")) { - confp->pri.minidle = atoi(v->value); - } else if (!strcasecmp(v->name, "idleext")) { - ast_copy_string(confp->pri.idleext, v->value, sizeof(confp->pri.idleext)); - } else if (!strcasecmp(v->name, "idledial")) { - ast_copy_string(confp->pri.idledial, v->value, sizeof(confp->pri.idledial)); - } else if (!strcasecmp(v->name, "overlapdial")) { - confp->pri.overlapdial = ast_true(v->value); -#ifdef HAVE_PRI_INBANDDISCONNECT - } else if (!strcasecmp(v->name, "inbanddisconnect")) { - confp->pri.inbanddisconnect = ast_true(v->value); -#endif - } else if (!strcasecmp(v->name, "pritimer")) { -#ifdef PRI_GETSET_TIMERS - char *timerc, *c; - int timer, timeridx; - c = v->value; - timerc = strsep(&c, ","); - if (timerc) { - timer = atoi(c); - if (!timer) - ast_log(LOG_WARNING, "'%s' is not a valid value for an ISDN timer\n", timerc); - else { - if ((timeridx = pri_timer2idx(timerc)) >= 0) - pritimers[timeridx] = timer; - else - ast_log(LOG_WARNING, "'%s' is not a valid ISDN timer\n", timerc); - } - } else - ast_log(LOG_WARNING, "'%s' is not a valid ISDN timer configuration string\n", v->value); - - } else if (!strcasecmp(v->name, "facilityenable")) { - confp->pri.facilityenable = ast_true(v->value); -#endif /* PRI_GETSET_TIMERS */ -#endif /* HAVE_PRI */ - } else if (!strcasecmp(v->name, "cadence")) { - /* setup to scan our argument */ - int element_count, c[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - int i; - struct dahdi_ring_cadence new_cadence; - int cid_location = -1; - int firstcadencepos = 0; - char original_args[80]; - int cadence_is_ok = 1; - - ast_copy_string(original_args, v->value, sizeof(original_args)); - /* 16 cadences allowed (8 pairs) */ - element_count = sscanf(v->value, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", &c[0], &c[1], &c[2], &c[3], &c[4], &c[5], &c[6], &c[7], &c[8], &c[9], &c[10], &c[11], &c[12], &c[13], &c[14], &c[15]); - - /* Cadence must be even (on/off) */ - if (element_count % 2 == 1) { - ast_log(LOG_ERROR, "Must be a silence duration for each ring duration: %s\n",original_args); - cadence_is_ok = 0; - } - - /* Ring cadences cannot be negative */ - for (i = 0; i < element_count; i++) { - if (c[i] == 0) { - ast_log(LOG_ERROR, "Ring or silence duration cannot be zero: %s\n", original_args); - cadence_is_ok = 0; - break; - } else if (c[i] < 0) { - if (i % 2 == 1) { - /* Silence duration, negative possibly okay */ - if (cid_location == -1) { - cid_location = i; - c[i] *= -1; - } else { - ast_log(LOG_ERROR, "CID location specified twice: %s\n",original_args); - cadence_is_ok = 0; - break; - } - } else { - if (firstcadencepos == 0) { - firstcadencepos = i; /* only recorded to avoid duplicate specification */ - /* duration will be passed negative to the DAHDI driver */ - } else { - ast_log(LOG_ERROR, "First cadence position specified twice: %s\n",original_args); - cadence_is_ok = 0; - break; - } - } - } - } - - /* Substitute our scanned cadence */ - for (i = 0; i < 16; i++) { - new_cadence.ringcadence[i] = c[i]; - } - - if (cadence_is_ok) { - /* ---we scanned it without getting annoyed; now some sanity checks--- */ - if (element_count < 2) { - ast_log(LOG_ERROR, "Minimum cadence is ring,pause: %s\n", original_args); - } else { - if (cid_location == -1) { - /* user didn't say; default to first pause */ - cid_location = 1; - } else { - /* convert element_index to cidrings value */ - cid_location = (cid_location + 1) / 2; - } - /* ---we like their cadence; try to install it--- */ - if (!user_has_defined_cadences++) - /* this is the first user-defined cadence; clear the default user cadences */ - num_cadence = 0; - if ((num_cadence+1) >= NUM_CADENCE_MAX) - ast_log(LOG_ERROR, "Already %d cadences; can't add another: %s\n", NUM_CADENCE_MAX, original_args); - else { - cadences[num_cadence] = new_cadence; - cidrings[num_cadence++] = cid_location; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "cadence 'r%d' added: %s\n",num_cadence,original_args); - } - } - } - } else if (!strcasecmp(v->name, "ringtimeout")) { - ringt_base = (atoi(v->value) * 8) / READ_SIZE; - } else if (!strcasecmp(v->name, "prewink")) { - confp->timing.prewinktime = atoi(v->value); - } else if (!strcasecmp(v->name, "preflash")) { - confp->timing.preflashtime = atoi(v->value); - } else if (!strcasecmp(v->name, "wink")) { - confp->timing.winktime = atoi(v->value); - } else if (!strcasecmp(v->name, "flash")) { - confp->timing.flashtime = atoi(v->value); - } else if (!strcasecmp(v->name, "start")) { - confp->timing.starttime = atoi(v->value); - } else if (!strcasecmp(v->name, "rxwink")) { - confp->timing.rxwinktime = atoi(v->value); - } else if (!strcasecmp(v->name, "rxflash")) { - confp->timing.rxflashtime = atoi(v->value); - } else if (!strcasecmp(v->name, "debounce")) { - confp->timing.debouncetime = atoi(v->value); - } else if (!strcasecmp(v->name, "toneduration")) { - int toneduration; - int ctlfd; - int res; - struct dahdi_dialparams dps; - - ctlfd = open(DAHDI_FILE_CTL, O_RDWR); - - if (ctlfd == -1) { - ast_log(LOG_ERROR, "Unable to open " DAHDI_FILE_CTL " to set toneduration\n"); - return -1; - } - - toneduration = atoi(v->value); - if (toneduration > -1) { - memset(&dps, 0, sizeof(dps)); - - dps.dtmf_tonelen = dps.mfv1_tonelen = toneduration; - res = ioctl(ctlfd, DAHDI_SET_DIALPARAMS, &dps); - if (res < 0) { - ast_log(LOG_ERROR, "Invalid tone duration: %d ms: %s\n", toneduration, strerror(errno)); - return -1; - } - } - close(ctlfd); - } else if (!strcasecmp(v->name, "defaultcic")) { - ast_copy_string(defaultcic, v->value, sizeof(defaultcic)); - } else if (!strcasecmp(v->name, "defaultozz")) { - ast_copy_string(defaultozz, v->value, sizeof(defaultozz)); - } - } else if (!skipchannels) - ast_log(LOG_WARNING, "Ignoring %s\n", v->name); - } - if (dahdichan[0]) { - /* The user has set 'dahdichan' */ - /*< \todo pass proper line number instead of 0 */ - if (build_channels(confp, 0, dahdichan, reload, 0, &found_pseudo)) { - return -1; - } - } - /*< \todo why check for the pseudo in the per-channel section. - * Any actual use for manual setup of the pseudo channel? */ - if (!found_pseudo && reload == 0) { - /* use the default configuration for a channel, so - that any settings from real configured channels - don't "leak" into the pseudo channel config - */ - struct dahdi_chan_conf conf = dahdi_chan_conf_default(); - - tmp = mkintf(CHAN_PSEUDO, &conf, NULL, reload); - - if (tmp) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Automatically generated pseudo channel\n"); - } else { - ast_log(LOG_WARNING, "Unable to register pseudo channel!\n"); - } - } - return 0; -} - -static int setup_dahdi(int reload) -{ - struct ast_config *cfg; - struct ast_variable *v; - struct dahdi_chan_conf conf = dahdi_chan_conf_default(); - int res; - -#ifdef HAVE_PRI - char *c; - int spanno; - int i, x; - int logicalspan; - int trunkgroup; - int dchannels[NUM_DCHANS]; -#endif - -#ifdef HAVE_ZAPTEL - int load_from_zapata_conf = 1; -#else - int load_from_zapata_conf = (dahdi_chan_mode == CHAN_ZAP_MODE); -#endif - - if (load_from_zapata_conf) { - if (!(cfg = ast_config_load("zapata.conf"))) { - ast_log(LOG_ERROR, "Unable to load zapata.conf\n"); - return 0; - } - } else { - if (!(cfg = ast_config_load("chan_dahdi.conf"))) { - ast_log(LOG_ERROR, "Unable to load chan_dahdi.conf\n"); - return 0; - } - } - - /* It's a little silly to lock it, but we mind as well just to be sure */ - ast_mutex_lock(&iflock); -#ifdef HAVE_PRI - if (reload != 1) { - /* Process trunkgroups first */ - v = ast_variable_browse(cfg, "trunkgroups"); - while (v) { - if (!strcasecmp(v->name, "trunkgroup")) { - trunkgroup = atoi(v->value); - if (trunkgroup > 0) { - if ((c = strchr(v->value, ','))) { - i = 0; - memset(dchannels, 0, sizeof(dchannels)); - while (c && (i < NUM_DCHANS)) { - dchannels[i] = atoi(c + 1); - if (dchannels[i] < 0) { - ast_log(LOG_WARNING, "D-channel for trunk group %d must be a postiive number at line %d of chan_dahdi.conf\n", trunkgroup, v->lineno); - } else - i++; - c = strchr(c + 1, ','); - } - if (i) { - if (pri_create_trunkgroup(trunkgroup, dchannels)) { - ast_log(LOG_WARNING, "Unable to create trunk group %d with Primary D-channel %d at line %d of chan_dahdi.conf\n", trunkgroup, dchannels[0], v->lineno); - } else if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Created trunk group %d with Primary D-channel %d and %d backup%s\n", trunkgroup, dchannels[0], i - 1, (i == 1) ? "" : "s"); - } else - ast_log(LOG_WARNING, "Trunk group %d lacks any valid D-channels at line %d of chan_dahdi.conf\n", trunkgroup, v->lineno); - } else - ast_log(LOG_WARNING, "Trunk group %d lacks a primary D-channel at line %d of chan_dahdi.conf\n", trunkgroup, v->lineno); - } else - ast_log(LOG_WARNING, "Trunk group identifier must be a positive integer at line %d of chan_dahdi.conf\n", v->lineno); - } else if (!strcasecmp(v->name, "spanmap")) { - spanno = atoi(v->value); - if (spanno > 0) { - if ((c = strchr(v->value, ','))) { - trunkgroup = atoi(c + 1); - if (trunkgroup > 0) { - if ((c = strchr(c + 1, ','))) - logicalspan = atoi(c + 1); - else - logicalspan = 0; - if (logicalspan >= 0) { - if (pri_create_spanmap(spanno - 1, trunkgroup, logicalspan)) { - ast_log(LOG_WARNING, "Failed to map span %d to trunk group %d (logical span %d)\n", spanno, trunkgroup, logicalspan); - } else if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Mapped span %d to trunk group %d (logical span %d)\n", spanno, trunkgroup, logicalspan); - } else - ast_log(LOG_WARNING, "Logical span must be a postive number, or '0' (for unspecified) at line %d of chan_dahdi.conf\n", v->lineno); - } else - ast_log(LOG_WARNING, "Trunk group must be a postive number at line %d of chan_dahdi.conf\n", v->lineno); - } else - ast_log(LOG_WARNING, "Missing trunk group for span map at line %d of chan_dahdi.conf\n", v->lineno); - } else - ast_log(LOG_WARNING, "Span number must be a postive integer at line %d of chan_dahdi.conf\n", v->lineno); - } else { - ast_log(LOG_NOTICE, "Ignoring unknown keyword '%s' in trunkgroups\n", v->name); - } - v = v->next; - } - } -#endif - - /* Copy the default jb config over global_jbconf */ - memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); - - v = ast_variable_browse(cfg, "channels"); - res = process_dahdi(&conf, "", v, reload, 0); - ast_mutex_unlock(&iflock); - ast_config_destroy(cfg); - if (res) - return res; - cfg = ast_config_load("users.conf"); - if (cfg) { - char *cat; - const char *chans; - process_dahdi(&conf, "", ast_variable_browse(cfg, "general"), 1, 1); - for (cat = ast_category_browse(cfg, NULL); cat ; cat = ast_category_browse(cfg, cat)) { - if (!strcasecmp(cat, "general")) - continue; - chans = ast_variable_retrieve(cfg, cat, "dahdichan"); - if (!ast_strlen_zero(chans)) { - struct dahdi_chan_conf sect_conf; - memcpy(§_conf, &conf, sizeof(sect_conf)); - - process_dahdi(§_conf, cat, ast_variable_browse(cfg, cat), reload, 0); - } - } - ast_config_destroy(cfg); - } -#ifdef HAVE_PRI - if (reload != 1) { - for (x = 0; x < NUM_SPANS; x++) { - if (pris[x].pvts[0]) { - if (start_pri(pris + x)) { - ast_log(LOG_ERROR, "Unable to start D-channel on span %d\n", x + 1); - return -1; - } else if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Starting D-Channel on span %d\n", x + 1); - } - } - } -#endif - /* And start the monitor for the first time */ - restart_monitor(); - return 0; -} - -#define local_astman_register(a, b, c, d) do { \ - if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) { \ - ast_manager_register("DAHDI" a, b, dahdi_ ## c, d); \ - } \ - ast_manager_register("Zap" a, b, zap_ ## c, d); \ - } while (0) - -static int load_module(void) -{ - int res; - -#ifdef HAVE_PRI - int y,i; - memset(pris, 0, sizeof(pris)); - for (y = 0; y < NUM_SPANS; y++) { - ast_mutex_init(&pris[y].lock); - pris[y].offset = -1; - pris[y].master = AST_PTHREADT_NULL; - for (i = 0; i < NUM_DCHANS; i++) - pris[y].fds[i] = -1; - } - pri_set_error(dahdi_pri_error); - pri_set_message(dahdi_pri_message); - if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) { - ast_register_application(dahdi_send_keypad_facility_app, dahdi_send_keypad_facility_exec, - dahdi_send_keypad_facility_synopsis, dahdi_send_keypad_facility_descrip); - } - ast_register_application(zap_send_keypad_facility_app, zap_send_keypad_facility_exec, - zap_send_keypad_facility_synopsis, zap_send_keypad_facility_descrip); -#endif - if ((res = setup_dahdi(0))) { - return AST_MODULE_LOAD_DECLINE; - } - if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) { - chan_tech = &dahdi_tech; - } else { - chan_tech = &zap_tech; - } - if (ast_channel_register(chan_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class '%s'\n", chan_tech->type); - __unload_module(); - return -1; - } -#ifdef HAVE_PRI - ast_string_field_init(&inuse, 16); - ast_string_field_set(&inuse, name, "GR-303InUse"); - ast_cli_register_multiple(dahdi_pri_cli, sizeof(dahdi_pri_cli) / sizeof(struct ast_cli_entry)); -#endif - ast_cli_register_multiple(dahdi_cli, sizeof(dahdi_cli) / sizeof(struct ast_cli_entry)); - - memset(round_robin, 0, sizeof(round_robin)); - local_astman_register("Transfer", 0, action_transfer, "Transfer Channel"); - local_astman_register("Hangup", 0, action_transferhangup, "Hangup Channel"); - local_astman_register("DialOffHook", 0, action_dialoffhook, "Dial over channel while offhook"); - local_astman_register("DNDon", 0, action_dndon, "Toggle channel Do Not Disturb status ON"); - local_astman_register("DNDoff", 0, action_dndoff, "Toggle channel Do Not Disturb status OFF"); - local_astman_register("ShowChannels", 0, action_showchannels, "Show status channels"); - local_astman_register("Restart", 0, action_restart, "Fully Restart channels (terminates calls)"); - - ast_cond_init(&ss_thread_complete, NULL); - - return res; -} - -static int dahdi_sendtext(struct ast_channel *c, const char *text) -{ -#define END_SILENCE_LEN 400 -#define HEADER_MS 50 -#define TRAILER_MS 5 -#define HEADER_LEN ((HEADER_MS + TRAILER_MS) * 8) -#define ASCII_BYTES_PER_CHAR 80 - - unsigned char *buf,*mybuf; - struct dahdi_pvt *p = c->tech_pvt; - struct pollfd fds[1]; - int size,res,fd,len,x; - int bytes=0; - /* Initial carrier (imaginary) */ - float cr = 1.0; - float ci = 0.0; - float scont = 0.0; - int index; - - index = dahdi_get_index(c, p, 0); - if (index < 0) { - ast_log(LOG_WARNING, "Huh? I don't exist?\n"); - return -1; - } - if (!text[0]) return(0); /* if nothing to send, dont */ - if ((!p->tdd) && (!p->mate)) return(0); /* if not in TDD mode, just return */ - if (p->mate) - buf = ast_malloc(((strlen(text) + 1) * ASCII_BYTES_PER_CHAR) + END_SILENCE_LEN + HEADER_LEN); - else - buf = ast_malloc(((strlen(text) + 1) * TDD_BYTES_PER_CHAR) + END_SILENCE_LEN); - if (!buf) - return -1; - mybuf = buf; - if (p->mate) { - int codec = AST_LAW(p); - for (x = 0; x < HEADER_MS; x++) { /* 50 ms of Mark */ - PUT_CLID_MARKMS; - } - /* Put actual message */ - for (x = 0; text[x]; x++) { - PUT_CLID(text[x]); - } - for (x = 0; x < TRAILER_MS; x++) { /* 5 ms of Mark */ - PUT_CLID_MARKMS; - } - len = bytes; - buf = mybuf; - } else { - len = tdd_generate(p->tdd, buf, text); - if (len < 1) { - ast_log(LOG_ERROR, "TDD generate (len %d) failed!!\n", (int)strlen(text)); - free(mybuf); - return -1; - } - } - memset(buf + len, 0x7f, END_SILENCE_LEN); - len += END_SILENCE_LEN; - fd = p->subs[index].dfd; - while (len) { - if (ast_check_hangup(c)) { - free(mybuf); - return -1; - } - size = len; - if (size > READ_SIZE) - size = READ_SIZE; - fds[0].fd = fd; - fds[0].events = POLLOUT | POLLPRI; - fds[0].revents = 0; - res = poll(fds, 1, -1); - if (!res) { - ast_log(LOG_DEBUG, "poll (for write) ret. 0 on channel %d\n", p->channel); - continue; - } - /* if got exception */ - if (fds[0].revents & POLLPRI) { - ast_free(mybuf); - return -1; - } - if (!(fds[0].revents & POLLOUT)) { - ast_log(LOG_DEBUG, "write fd not ready on channel %d\n", p->channel); - continue; - } - res = write(fd, buf, size); - if (res != size) { - if (res == -1) { - free(mybuf); - return -1; - } - if (option_debug) - ast_log(LOG_DEBUG, "Write returned %d (%s) on channel %d\n", res, strerror(errno), p->channel); - break; - } - len -= size; - buf += size; - } - free(mybuf); - return(0); -} - - -static int reload(void) -{ - int res = 0; - - res = setup_dahdi(1); - if (res) { - ast_log(LOG_WARNING, "Reload of chan_dahdi.so is unsuccessful!\n"); - return -1; - } - return 0; -} - -/* This is a workaround so that menuselect displays a proper description - * AST_MODULE_INFO(, , "DAHDI Telephony" - */ - -#ifdef HAVE_PRI -#define tdesc "DAHDI Telephony w/PRI" -#else -#define tdesc "DAHDI Telephony" -#endif - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc, - .load = load_module, - .unload = unload_module, - .reload = reload, - ); - - diff --git a/1.4.23-rc4/channels/chan_features.c b/1.4.23-rc4/channels/chan_features.c deleted file mode 100644 index 043576623..000000000 --- a/1.4.23-rc4/channels/chan_features.c +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief feature Proxy Channel - * - * \author Mark Spencer <markster@digium.com> - * - * \note *** Experimental code **** - * - * \ingroup channel_drivers - */ -/*** MODULEINFO - <defaultenabled>no</defaultenabled> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <sys/socket.h> -#include <errno.h> -#include <stdlib.h> -#include <fcntl.h> -#include <netdb.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/signal.h> - -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/lock.h" -#include "asterisk/sched.h" -#include "asterisk/io.h" -#include "asterisk/rtp.h" -#include "asterisk/acl.h" -#include "asterisk/callerid.h" -#include "asterisk/file.h" -#include "asterisk/cli.h" -#include "asterisk/app.h" -#include "asterisk/musiconhold.h" -#include "asterisk/manager.h" -#include "asterisk/stringfields.h" - -static const char tdesc[] = "Feature Proxy Channel Driver"; - -#define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0) - -struct feature_sub { - struct ast_channel *owner; - int inthreeway; - int pfd; - int timingfdbackup; - int alertpipebackup[2]; -}; - -struct feature_pvt { - ast_mutex_t lock; /* Channel private lock */ - char tech[AST_MAX_EXTENSION]; /* Technology to abstract */ - char dest[AST_MAX_EXTENSION]; /* Destination to abstract */ - struct ast_channel *subchan; - struct feature_sub subs[3]; /* Subs */ - struct ast_channel *owner; /* Current Master Channel */ - AST_LIST_ENTRY(feature_pvt) list; /* Next entity */ -}; - -static AST_LIST_HEAD_STATIC(features, feature_pvt); - -#define SUB_REAL 0 /* Active call */ -#define SUB_CALLWAIT 1 /* Call-Waiting call on hold */ -#define SUB_THREEWAY 2 /* Three-way call */ - -static struct ast_channel *features_request(const char *type, int format, void *data, int *cause); -static int features_digit_begin(struct ast_channel *ast, char digit); -static int features_digit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int features_call(struct ast_channel *ast, char *dest, int timeout); -static int features_hangup(struct ast_channel *ast); -static int features_answer(struct ast_channel *ast); -static struct ast_frame *features_read(struct ast_channel *ast); -static int features_write(struct ast_channel *ast, struct ast_frame *f); -static int features_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); -static int features_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); - -static const struct ast_channel_tech features_tech = { - .type = "Feature", - .description = tdesc, - .capabilities = -1, - .requester = features_request, - .send_digit_begin = features_digit_begin, - .send_digit_end = features_digit_end, - .call = features_call, - .hangup = features_hangup, - .answer = features_answer, - .read = features_read, - .write = features_write, - .exception = features_read, - .indicate = features_indicate, - .fixup = features_fixup, -}; - -static inline void init_sub(struct feature_sub *sub) -{ - sub->inthreeway = 0; - sub->pfd = -1; - sub->timingfdbackup = -1; - sub->alertpipebackup[0] = sub->alertpipebackup[1] = -1; -} - -static inline int indexof(struct feature_pvt *p, struct ast_channel *owner, int nullok) -{ - int x; - if (!owner) { - ast_log(LOG_WARNING, "indexof called on NULL owner??\n"); - return -1; - } - for (x=0; x<3; x++) { - if (owner == p->subs[x].owner) - return x; - } - return -1; -} - -#if 0 -static void wakeup_sub(struct feature_pvt *p, int a) -{ - struct ast_frame null = { AST_FRAME_NULL, }; - for (;;) { - if (p->subs[a].owner) { - if (ast_mutex_trylock(&p->subs[a].owner->lock)) { - ast_mutex_unlock(&p->lock); - usleep(1); - ast_mutex_lock(&p->lock); - } else { - ast_queue_frame(p->subs[a].owner, &null); - ast_mutex_unlock(&p->subs[a].owner->lock); - break; - } - } else - break; - } -} -#endif - -static void restore_channel(struct feature_pvt *p, int index) -{ - /* Restore timing/alertpipe */ - p->subs[index].owner->timingfd = p->subs[index].timingfdbackup; - p->subs[index].owner->alertpipe[0] = p->subs[index].alertpipebackup[0]; - p->subs[index].owner->alertpipe[1] = p->subs[index].alertpipebackup[1]; - p->subs[index].owner->fds[AST_ALERT_FD] = p->subs[index].alertpipebackup[0]; - p->subs[index].owner->fds[AST_TIMING_FD] = p->subs[index].timingfdbackup; -} - -static void update_features(struct feature_pvt *p, int index) -{ - int x; - if (p->subs[index].owner) { - for (x=0; x<AST_MAX_FDS; x++) { - if (index) - p->subs[index].owner->fds[x] = -1; - else - p->subs[index].owner->fds[x] = p->subchan->fds[x]; - } - if (!index) { - /* Copy timings from master channel */ - p->subs[index].owner->timingfd = p->subchan->timingfd; - p->subs[index].owner->alertpipe[0] = p->subchan->alertpipe[0]; - p->subs[index].owner->alertpipe[1] = p->subchan->alertpipe[1]; - if (p->subs[index].owner->nativeformats != p->subchan->readformat) { - p->subs[index].owner->nativeformats = p->subchan->readformat; - if (p->subs[index].owner->readformat) - ast_set_read_format(p->subs[index].owner, p->subs[index].owner->readformat); - if (p->subs[index].owner->writeformat) - ast_set_write_format(p->subs[index].owner, p->subs[index].owner->writeformat); - } - } else{ - restore_channel(p, index); - } - } -} - -#if 0 -static void swap_subs(struct feature_pvt *p, int a, int b) -{ - int tinthreeway; - struct ast_channel *towner; - - ast_log(LOG_DEBUG, "Swapping %d and %d\n", a, b); - - towner = p->subs[a].owner; - tinthreeway = p->subs[a].inthreeway; - - p->subs[a].owner = p->subs[b].owner; - p->subs[a].inthreeway = p->subs[b].inthreeway; - - p->subs[b].owner = towner; - p->subs[b].inthreeway = tinthreeway; - update_features(p,a); - update_features(p,b); - wakeup_sub(p, a); - wakeup_sub(p, b); -} -#endif - -static int features_answer(struct ast_channel *ast) -{ - struct feature_pvt *p = ast->tech_pvt; - int res = -1; - int x; - - ast_mutex_lock(&p->lock); - x = indexof(p, ast, 0); - if (!x && p->subchan) - res = ast_answer(p->subchan); - ast_mutex_unlock(&p->lock); - return res; -} - -static struct ast_frame *features_read(struct ast_channel *ast) -{ - struct feature_pvt *p = ast->tech_pvt; - struct ast_frame *f; - int x; - - f = &ast_null_frame; - ast_mutex_lock(&p->lock); - x = indexof(p, ast, 0); - if (!x && p->subchan) { - update_features(p, x); - f = ast_read(p->subchan); - } - ast_mutex_unlock(&p->lock); - return f; -} - -static int features_write(struct ast_channel *ast, struct ast_frame *f) -{ - struct feature_pvt *p = ast->tech_pvt; - int res = -1; - int x; - - ast_mutex_lock(&p->lock); - x = indexof(p, ast, 0); - if (!x && p->subchan) - res = ast_write(p->subchan, f); - ast_mutex_unlock(&p->lock); - return res; -} - -static int features_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct feature_pvt *p = newchan->tech_pvt; - int x; - - ast_mutex_lock(&p->lock); - if (p->owner == oldchan) - p->owner = newchan; - for (x = 0; x < 3; x++) { - if (p->subs[x].owner == oldchan) - p->subs[x].owner = newchan; - } - ast_mutex_unlock(&p->lock); - return 0; -} - -static int features_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen) -{ - struct feature_pvt *p = ast->tech_pvt; - int res = -1; - int x; - - /* Queue up a frame representing the indication as a control frame */ - ast_mutex_lock(&p->lock); - x = indexof(p, ast, 0); - if (!x && p->subchan) - res = ast_indicate(p->subchan, condition); - ast_mutex_unlock(&p->lock); - return res; -} - -static int features_digit_begin(struct ast_channel *ast, char digit) -{ - struct feature_pvt *p = ast->tech_pvt; - int res = -1; - int x; - - /* Queue up a frame representing the indication as a control frame */ - ast_mutex_lock(&p->lock); - x = indexof(p, ast, 0); - if (!x && p->subchan) - res = ast_senddigit_begin(p->subchan, digit); - ast_mutex_unlock(&p->lock); - - return res; -} - -static int features_digit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ - struct feature_pvt *p = ast->tech_pvt; - int res = -1; - int x; - - /* Queue up a frame representing the indication as a control frame */ - ast_mutex_lock(&p->lock); - x = indexof(p, ast, 0); - if (!x && p->subchan) - res = ast_senddigit_end(p->subchan, digit, duration); - ast_mutex_unlock(&p->lock); - return res; -} - -static int features_call(struct ast_channel *ast, char *dest, int timeout) -{ - struct feature_pvt *p = ast->tech_pvt; - int res = -1; - int x; - char *dest2; - - dest2 = strchr(dest, '/'); - if (dest2) { - ast_mutex_lock(&p->lock); - x = indexof(p, ast, 0); - if (!x && p->subchan) { - p->subchan->cid.cid_num = ast_strdup(p->owner->cid.cid_num); - p->subchan->cid.cid_name = ast_strdup(p->owner->cid.cid_name); - p->subchan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis); - p->subchan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani); - - p->subchan->cid.cid_pres = p->owner->cid.cid_pres; - ast_string_field_set(p->subchan, language, p->owner->language); - ast_string_field_set(p->subchan, accountcode, p->owner->accountcode); - p->subchan->cdrflags = p->owner->cdrflags; - res = ast_call(p->subchan, dest2, timeout); - update_features(p, x); - } else - ast_log(LOG_NOTICE, "Uhm yah, not quite there with the call waiting...\n"); - ast_mutex_unlock(&p->lock); - } - return res; -} - -static int features_hangup(struct ast_channel *ast) -{ - struct feature_pvt *p = ast->tech_pvt; - int x; - - ast_mutex_lock(&p->lock); - x = indexof(p, ast, 0); - if (x > -1) { - restore_channel(p, x); - p->subs[x].owner = NULL; - /* XXX Re-arrange, unconference, etc XXX */ - } - ast->tech_pvt = NULL; - - if (!p->subs[SUB_REAL].owner && !p->subs[SUB_CALLWAIT].owner && !p->subs[SUB_THREEWAY].owner) { - ast_mutex_unlock(&p->lock); - /* Remove from list */ - AST_LIST_LOCK(&features); - AST_LIST_REMOVE(&features, p, list); - AST_LIST_UNLOCK(&features); - ast_mutex_lock(&p->lock); - /* And destroy */ - if (p->subchan) - ast_hangup(p->subchan); - ast_mutex_unlock(&p->lock); - ast_mutex_destroy(&p->lock); - free(p); - return 0; - } - ast_mutex_unlock(&p->lock); - return 0; -} - -static struct feature_pvt *features_alloc(char *data, int format) -{ - struct feature_pvt *tmp; - char *dest=NULL; - char *tech; - int x; - int status; - struct ast_channel *chan; - - tech = ast_strdupa(data); - if (tech) { - dest = strchr(tech, '/'); - if (dest) { - *dest = '\0'; - dest++; - } - } - if (!tech || !dest) { - ast_log(LOG_NOTICE, "Format for feature channel is Feature/Tech/Dest ('%s' not valid)!\n", - data); - return NULL; - } - AST_LIST_LOCK(&features); - AST_LIST_TRAVERSE(&features, tmp, list) { - if (!strcasecmp(tmp->tech, tech) && !strcmp(tmp->dest, dest)) - break; - } - AST_LIST_UNLOCK(&features); - if (!tmp) { - chan = ast_request(tech, format, dest, &status); - if (!chan) { - ast_log(LOG_NOTICE, "Unable to allocate subchannel '%s/%s'\n", tech, dest); - return NULL; - } - tmp = malloc(sizeof(struct feature_pvt)); - if (tmp) { - memset(tmp, 0, sizeof(struct feature_pvt)); - for (x=0;x<3;x++) - init_sub(tmp->subs + x); - ast_mutex_init(&tmp->lock); - ast_copy_string(tmp->tech, tech, sizeof(tmp->tech)); - ast_copy_string(tmp->dest, dest, sizeof(tmp->dest)); - tmp->subchan = chan; - AST_LIST_LOCK(&features); - AST_LIST_INSERT_HEAD(&features, tmp, list); - AST_LIST_UNLOCK(&features); - } - } - return tmp; -} - -static struct ast_channel *features_new(struct feature_pvt *p, int state, int index) -{ - struct ast_channel *tmp; - int x,y; - char *b2 = 0; - if (!p->subchan) { - ast_log(LOG_WARNING, "Called upon channel with no subchan:(\n"); - return NULL; - } - if (p->subs[index].owner) { - ast_log(LOG_WARNING, "Called to put index %d already there!\n", index); - return NULL; - } - /* figure out what you want the name to be */ - for (x=1;x<4;x++) { - if (b2) - free(b2); - b2 = ast_safe_string_alloc("%s/%s-%d", p->tech, p->dest, x); - for (y=0;y<3;y++) { - if (y == index) - continue; - if (p->subs[y].owner && !strcasecmp(p->subs[y].owner->name, b2)) - break; - } - if (y >= 3) - break; - } - tmp = ast_channel_alloc(0, state, 0,0, "", "", "", 0, "Feature/%s", b2); - /* free up the name, it was copied into the channel name */ - if (b2) - free(b2); - if (!tmp) { - ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); - return NULL; - } - tmp->tech = &features_tech; - tmp->writeformat = p->subchan->writeformat; - tmp->rawwriteformat = p->subchan->rawwriteformat; - tmp->readformat = p->subchan->readformat; - tmp->rawreadformat = p->subchan->rawreadformat; - tmp->nativeformats = p->subchan->readformat; - tmp->tech_pvt = p; - p->subs[index].owner = tmp; - if (!p->owner) - p->owner = tmp; - ast_module_ref(ast_module_info->self); - return tmp; -} - - -static struct ast_channel *features_request(const char *type, int format, void *data, int *cause) -{ - struct feature_pvt *p; - struct ast_channel *chan = NULL; - - p = features_alloc(data, format); - if (p && !p->subs[SUB_REAL].owner) - chan = features_new(p, AST_STATE_DOWN, SUB_REAL); - if (chan) - update_features(p,SUB_REAL); - return chan; -} - -static int features_show(int fd, int argc, char **argv) -{ - struct feature_pvt *p; - - if (argc != 3) - return RESULT_SHOWUSAGE; - - if (AST_LIST_EMPTY(&features)) { - ast_cli(fd, "No feature channels in use\n"); - return RESULT_SUCCESS; - } - - AST_LIST_LOCK(&features); - AST_LIST_TRAVERSE(&features, p, list) { - ast_mutex_lock(&p->lock); - ast_cli(fd, "%s -- %s/%s\n", p->owner ? p->owner->name : "<unowned>", p->tech, p->dest); - ast_mutex_unlock(&p->lock); - } - AST_LIST_UNLOCK(&features); - return RESULT_SUCCESS; -} - -static char show_features_usage[] = -"Usage: feature show channels\n" -" Provides summary information on feature channels.\n"; - -static struct ast_cli_entry cli_features[] = { - { { "feature", "show", "channels", NULL }, - features_show, "List status of feature channels", - show_features_usage }, -}; - -static int load_module(void) -{ - /* Make sure we can register our sip channel type */ - if (ast_channel_register(&features_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class 'Feature'\n"); - return -1; - } - ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry)); - return 0; -} - -static int unload_module(void) -{ - struct feature_pvt *p; - - /* First, take us out of the channel loop */ - ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry)); - ast_channel_unregister(&features_tech); - - if (!AST_LIST_LOCK(&features)) - return -1; - /* Hangup all interfaces if they have an owner */ - AST_LIST_TRAVERSE_SAFE_BEGIN(&features, p, list) { - if (p->owner) - ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD); - AST_LIST_REMOVE_CURRENT(&features, list); - free(p); - } - AST_LIST_TRAVERSE_SAFE_END - AST_LIST_UNLOCK(&features); - - return 0; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Feature Proxy Channel"); - diff --git a/1.4.23-rc4/channels/chan_gtalk.c b/1.4.23-rc4/channels/chan_gtalk.c deleted file mode 100644 index abe0d63b6..000000000 --- a/1.4.23-rc4/channels/chan_gtalk.c +++ /dev/null @@ -1,2067 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Matt O'Gorman <mogorman@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \author Matt O'Gorman <mogorman@digium.com> - * - * \brief Gtalk Channel Driver, until google/libjingle works with jingle spec - * - * \ingroup channel_drivers - */ - -/*** MODULEINFO - <depend>iksemel</depend> - <depend>res_jabber</depend> - <use>gnutls</use> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <sys/socket.h> -#include <errno.h> -#include <stdlib.h> -#include <fcntl.h> -#include <netdb.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/signal.h> -#include <iksemel.h> -#include <pthread.h> - -#ifdef HAVE_GNUTLS -#include <gcrypt.h> -GCRY_THREAD_OPTION_PTHREAD_IMPL; -#endif /* HAVE_GNUTLS */ - -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/lock.h" -#include "asterisk/sched.h" -#include "asterisk/io.h" -#include "asterisk/rtp.h" -#include "asterisk/acl.h" -#include "asterisk/callerid.h" -#include "asterisk/file.h" -#include "asterisk/cli.h" -#include "asterisk/app.h" -#include "asterisk/musiconhold.h" -#include "asterisk/manager.h" -#include "asterisk/stringfields.h" -#include "asterisk/utils.h" -#include "asterisk/causes.h" -#include "asterisk/astobj.h" -#include "asterisk/abstract_jb.h" -#include "asterisk/jabber.h" - -#define GOOGLE_CONFIG "gtalk.conf" - -#define GOOGLE_NS "http://www.google.com/session" - - -/*! Global jitterbuffer configuration - by default, jb is disabled */ -static struct ast_jb_conf default_jbconf = -{ - .flags = 0, - .max_size = -1, - .resync_threshold = -1, - .impl = "" -}; -static struct ast_jb_conf global_jbconf; - -enum gtalk_protocol { - AJI_PROTOCOL_UDP = 1, - AJI_PROTOCOL_SSLTCP = 2, -}; - -enum gtalk_connect_type { - AJI_CONNECT_STUN = 1, - AJI_CONNECT_LOCAL = 2, - AJI_CONNECT_RELAY = 3, -}; - -struct gtalk_pvt { - ast_mutex_t lock; /*!< Channel private lock */ - time_t laststun; - struct gtalk *parent; /*!< Parent client */ - char sid[100]; - char us[AJI_MAX_JIDLEN]; - char them[AJI_MAX_JIDLEN]; - char ring[10]; /*!< Message ID of ring */ - iksrule *ringrule; /*!< Rule for matching RING request */ - int initiator; /*!< If we're the initiator */ - int alreadygone; - int capability; - struct ast_codec_pref prefs; - struct gtalk_candidate *theircandidates; - struct gtalk_candidate *ourcandidates; - char cid_num[80]; /*!< Caller ID num */ - char cid_name[80]; /*!< Caller ID name */ - char exten[80]; /*!< Called extension */ - struct ast_channel *owner; /*!< Master Channel */ - struct ast_rtp *rtp; /*!< RTP audio session */ - struct ast_rtp *vrtp; /*!< RTP video session */ - int jointcapability; /*!< Supported capability at both ends (codecs ) */ - int peercapability; - struct gtalk_pvt *next; /* Next entity */ -}; - -struct gtalk_candidate { - char name[100]; - enum gtalk_protocol protocol; - double preference; - char username[100]; - char password[100]; - enum gtalk_connect_type type; - char network[6]; - int generation; - char ip[16]; - int port; - int receipt; - struct gtalk_candidate *next; -}; - -struct gtalk { - ASTOBJ_COMPONENTS(struct gtalk); - struct aji_client *connection; - struct aji_buddy *buddy; - struct gtalk_pvt *p; - struct ast_codec_pref prefs; - int amaflags; /*!< AMA Flags */ - char user[AJI_MAX_JIDLEN]; - char context[AST_MAX_CONTEXT]; - char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */ - int capability; - ast_group_t callgroup; /*!< Call group */ - ast_group_t pickupgroup; /*!< Pickup group */ - int callingpres; /*!< Calling presentation */ - int allowguest; - char language[MAX_LANGUAGE]; /*!< Default language for prompts */ - char musicclass[MAX_MUSICCLASS]; /*!< Music on Hold class */ -}; - -struct gtalk_container { - ASTOBJ_CONTAINER_COMPONENTS(struct gtalk); -}; - -static const char desc[] = "Gtalk Channel"; - -static int global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263; - -AST_MUTEX_DEFINE_STATIC(gtalklock); /*!< Protect the interface list (of gtalk_pvt's) */ - -/* Forward declarations */ -static struct ast_channel *gtalk_request(const char *type, int format, void *data, int *cause); -static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration); -static int gtalk_digit_begin(struct ast_channel *ast, char digit); -static int gtalk_digit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int gtalk_call(struct ast_channel *ast, char *dest, int timeout); -static int gtalk_hangup(struct ast_channel *ast); -static int gtalk_answer(struct ast_channel *ast); -static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action); -static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p); -static int gtalk_newcall(struct gtalk *client, ikspak *pak); -static struct ast_frame *gtalk_read(struct ast_channel *ast); -static int gtalk_write(struct ast_channel *ast, struct ast_frame *f); -static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); -static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen); -static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid); -static int gtalk_do_reload(int fd, int argc, char **argv); -static int gtalk_show_channels(int fd, int argc, char **argv); -/*----- RTP interface functions */ -static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, - struct ast_rtp *vrtp, int codecs, int nat_active); -static enum ast_rtp_get_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static int gtalk_get_codec(struct ast_channel *chan); - -/*! \brief PBX interface structure for channel registration */ -static const struct ast_channel_tech gtalk_tech = { - .type = "Gtalk", - .description = "Gtalk Channel Driver", - .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1), - .requester = gtalk_request, - .send_digit_begin = gtalk_digit_begin, - .send_digit_end = gtalk_digit_end, - .bridge = ast_rtp_bridge, - .call = gtalk_call, - .hangup = gtalk_hangup, - .answer = gtalk_answer, - .read = gtalk_read, - .write = gtalk_write, - .exception = gtalk_read, - .indicate = gtalk_indicate, - .fixup = gtalk_fixup, - .send_html = gtalk_sendhtml, - .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER -}; - -static struct sockaddr_in bindaddr = { 0, }; /*!< The address we bind to */ - -static struct sched_context *sched; /*!< The scheduling context */ -static struct io_context *io; /*!< The IO context */ -static struct in_addr __ourip; - - -/*! \brief RTP driver interface */ -static struct ast_rtp_protocol gtalk_rtp = { - type: "Gtalk", - get_rtp_info: gtalk_get_rtp_peer, - set_rtp_peer: gtalk_set_rtp_peer, - get_codec: gtalk_get_codec, -}; - -static char show_channels_usage[] = -"Usage: gtalk show channels\n" -" Shows current state of the Gtalk channels.\n"; - -static char reload_usage[] = -"Usage: gtalk reload\n" -" Reload gtalk channel driver.\n"; - - -static struct ast_cli_entry gtalk_cli[] = { - {{ "gtalk", "reload", NULL}, gtalk_do_reload, "Reload GoogleTalk configuration", reload_usage }, - {{ "gtalk", "show", "channels", NULL}, gtalk_show_channels, "Show GoogleTalk channels", show_channels_usage }, - }; - - - -static char externip[16]; - -static struct gtalk_container gtalk_list; - -static void gtalk_member_destroy(struct gtalk *obj) -{ - free(obj); -} - -static struct gtalk *find_gtalk(char *name, char *connection) -{ - struct gtalk *gtalk = NULL; - char *domain = NULL , *s = NULL; - - if(strchr(connection, '@')) { - s = ast_strdupa(connection); - domain = strsep(&s, "@"); - ast_verbose("OOOOH domain = %s\n", domain); - } - gtalk = ASTOBJ_CONTAINER_FIND(>alk_list, name); - if (!gtalk && strchr(name, '@')) - gtalk = ASTOBJ_CONTAINER_FIND_FULL(>alk_list, name, user,,, strcasecmp); - - if (!gtalk) { - /* guest call */ - ASTOBJ_CONTAINER_TRAVERSE(>alk_list, 1, { - ASTOBJ_RDLOCK(iterator); - if (!strcasecmp(iterator->name, "guest")) { - gtalk = iterator; - } - ASTOBJ_UNLOCK(iterator); - - if (gtalk) - break; - }); - - } - return gtalk; -} - - -static int add_codec_to_answer(const struct gtalk_pvt *p, int codec, iks *dcodecs) -{ - int res = 0; - char *format = ast_getformatname(codec); - - if (!strcasecmp("ulaw", format)) { - iks *payload_eg711u, *payload_pcmu; - payload_pcmu = iks_new("payload-type"); - payload_eg711u = iks_new("payload-type"); - - if(!payload_eg711u || !payload_pcmu) { - if(payload_pcmu) - iks_delete(payload_pcmu); - if(payload_eg711u) - iks_delete(payload_eg711u); - ast_log(LOG_WARNING,"Failed to allocate iks node"); - return -1; - } - iks_insert_attrib(payload_pcmu, "id", "0"); - iks_insert_attrib(payload_pcmu, "name", "PCMU"); - iks_insert_attrib(payload_pcmu, "clockrate","8000"); - iks_insert_attrib(payload_pcmu, "bitrate","64000"); - iks_insert_attrib(payload_eg711u, "id", "100"); - iks_insert_attrib(payload_eg711u, "name", "EG711U"); - iks_insert_attrib(payload_eg711u, "clockrate","8000"); - iks_insert_attrib(payload_eg711u, "bitrate","64000"); - iks_insert_node(dcodecs, payload_pcmu); - iks_insert_node(dcodecs, payload_eg711u); - res ++; - } - if (!strcasecmp("alaw", format)) { - iks *payload_eg711a, *payload_pcma; - payload_pcma = iks_new("payload-type"); - payload_eg711a = iks_new("payload-type"); - if(!payload_eg711a || !payload_pcma) { - if(payload_eg711a) - iks_delete(payload_eg711a); - if(payload_pcma) - iks_delete(payload_pcma); - ast_log(LOG_WARNING,"Failed to allocate iks node"); - return -1; - } - iks_insert_attrib(payload_pcma, "id", "8"); - iks_insert_attrib(payload_pcma, "name", "PCMA"); - iks_insert_attrib(payload_pcma, "clockrate","8000"); - iks_insert_attrib(payload_pcma, "bitrate","64000"); - payload_eg711a = iks_new("payload-type"); - iks_insert_attrib(payload_eg711a, "id", "101"); - iks_insert_attrib(payload_eg711a, "name", "EG711A"); - iks_insert_attrib(payload_eg711a, "clockrate","8000"); - iks_insert_attrib(payload_eg711a, "bitrate","64000"); - iks_insert_node(dcodecs, payload_pcma); - iks_insert_node(dcodecs, payload_eg711a); - res ++; - } - if (!strcasecmp("ilbc", format)) { - iks *payload_ilbc = iks_new("payload-type"); - if(!payload_ilbc) { - ast_log(LOG_WARNING,"Failed to allocate iks node"); - return -1; - } - iks_insert_attrib(payload_ilbc, "id", "97"); - iks_insert_attrib(payload_ilbc, "name", "iLBC"); - iks_insert_attrib(payload_ilbc, "clockrate","8000"); - iks_insert_attrib(payload_ilbc, "bitrate","13300"); - iks_insert_node(dcodecs, payload_ilbc); - res ++; - } - if (!strcasecmp("g723", format)) { - iks *payload_g723 = iks_new("payload-type"); - if(!payload_g723) { - ast_log(LOG_WARNING,"Failed to allocate iks node"); - return -1; - } - iks_insert_attrib(payload_g723, "id", "4"); - iks_insert_attrib(payload_g723, "name", "G723"); - iks_insert_attrib(payload_g723, "clockrate","8000"); - iks_insert_attrib(payload_g723, "bitrate","6300"); - iks_insert_node(dcodecs, payload_g723); - res ++; - } - if (!strcasecmp("speex", format)) { - iks *payload_speex = iks_new("payload-type"); - if(!payload_speex) { - ast_log(LOG_WARNING,"Failed to allocate iks node"); - return -1; - } - iks_insert_attrib(payload_speex, "id", "110"); - iks_insert_attrib(payload_speex, "name", "speex"); - iks_insert_attrib(payload_speex, "clockrate","8000"); - iks_insert_attrib(payload_speex, "bitrate","11000"); - iks_insert_node(dcodecs, payload_speex); - res++; - } - if (!strcasecmp("gsm", format)) { - iks *payload_gsm = iks_new("payload-type"); - if(!payload_gsm) { - ast_log(LOG_WARNING,"Failed to allocate iks node"); - return -1; - } - iks_insert_attrib(payload_gsm, "id", "103"); - iks_insert_attrib(payload_gsm, "name", "gsm"); - iks_insert_node(dcodecs, payload_gsm); - res++; - } - ast_rtp_lookup_code(p->rtp, 1, codec); - return res; -} - -static int gtalk_invite(struct gtalk_pvt *p, char *to, char *from, char *sid, int initiator) -{ - struct gtalk *client = p->parent; - iks *iq, *gtalk, *dcodecs, *payload_telephone, *transport; - int x; - int pref_codec = 0; - int alreadysent = 0; - int codecs_num = 0; - - iq = iks_new("iq"); - gtalk = iks_new("session"); - dcodecs = iks_new("description"); - transport = iks_new("transport"); - payload_telephone = iks_new("payload-type"); - if (!(iq && gtalk && dcodecs && transport && payload_telephone)){ - if(iq) - iks_delete(iq); - if(gtalk) - iks_delete(gtalk); - if(dcodecs) - iks_delete(dcodecs); - if(transport) - iks_delete(transport); - if(payload_telephone) - iks_delete(payload_telephone); - - ast_log(LOG_ERROR, "Could not allocate iksemel nodes\n"); - return 0; - } - iks_insert_attrib(dcodecs, "xmlns", "http://www.google.com/session/phone"); - iks_insert_attrib(dcodecs, "xml:lang", "en"); - - for (x = 0; x < 32; x++) { - if (!(pref_codec = ast_codec_pref_index(&client->prefs, x))) - break; - if (!(client->capability & pref_codec)) - continue; - if (alreadysent & pref_codec) - continue; - codecs_num = add_codec_to_answer(p, pref_codec, dcodecs); - alreadysent |= pref_codec; - } - - if (codecs_num) { - /* only propose DTMF within an audio session */ - iks_insert_attrib(payload_telephone, "id", "106"); - iks_insert_attrib(payload_telephone, "name", "telephone-event"); - iks_insert_attrib(payload_telephone, "clockrate", "8000"); - } - iks_insert_attrib(transport,"xmlns","http://www.google.com/transport/p2p"); - - iks_insert_attrib(iq, "type", "set"); - iks_insert_attrib(iq, "to", to); - iks_insert_attrib(iq, "from", from); - iks_insert_attrib(iq, "id", client->connection->mid); - ast_aji_increment_mid(client->connection->mid); - - iks_insert_attrib(gtalk, "xmlns", "http://www.google.com/session"); - iks_insert_attrib(gtalk, "type",initiator ? "initiate": "accept"); - iks_insert_attrib(gtalk, "initiator", initiator ? from : to); - iks_insert_attrib(gtalk, "id", sid); - iks_insert_node(iq, gtalk); - iks_insert_node(gtalk, dcodecs); - iks_insert_node(gtalk, transport); - iks_insert_node(dcodecs, payload_telephone); - - iks_send(client->connection->p, iq); - iks_delete(payload_telephone); - iks_delete(transport); - iks_delete(dcodecs); - iks_delete(gtalk); - iks_delete(iq); - return 1; -} - -static int gtalk_invite_response(struct gtalk_pvt *p, char *to , char *from, char *sid, int initiator) -{ - iks *iq, *session, *transport; - iq = iks_new("iq"); - session = iks_new("session"); - transport = iks_new("transport"); - if(!(iq && session && transport)) { - if(iq) - iks_delete(iq); - if(session) - iks_delete(session); - if(transport) - iks_delete(transport); - ast_log(LOG_ERROR, " Unable to allocate IKS node\n"); - return -1; - } - iks_insert_attrib(iq, "from", from); - iks_insert_attrib(iq, "to", to); - iks_insert_attrib(iq, "type", "set"); - iks_insert_attrib(iq, "id",p->parent->connection->mid); - ast_aji_increment_mid(p->parent->connection->mid); - iks_insert_attrib(session, "type", "transport-accept"); - iks_insert_attrib(session, "id", sid); - iks_insert_attrib(session, "initiator", initiator ? from : to); - iks_insert_attrib(session, "xmlns", "http://www.google.com/session"); - iks_insert_attrib(transport, "xmlns", "http://www.google.com/transport/p2p"); - iks_insert_node(iq,session); - iks_insert_node(session,transport); - iks_send(p->parent->connection->p, iq); - iks_delete(transport); - iks_delete(session); - iks_delete(iq); - return 1; - -} - -static int gtalk_ringing_ack(void *data, ikspak *pak) -{ - struct gtalk_pvt *p = data; - - if (p->ringrule) - iks_filter_remove_rule(p->parent->connection->f, p->ringrule); - p->ringrule = NULL; - if (p->owner) - ast_queue_control(p->owner, AST_CONTROL_RINGING); - return IKS_FILTER_EAT; -} - -static int gtalk_answer(struct ast_channel *ast) -{ - struct gtalk_pvt *p = ast->tech_pvt; - int res = 0; - - if (option_debug) - ast_log(LOG_DEBUG, "Answer!\n"); - ast_mutex_lock(&p->lock); - gtalk_invite(p, p->them, p->us,p->sid, 0); - ast_mutex_unlock(&p->lock); - return res; -} - -static enum ast_rtp_get_result gtalk_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) -{ - struct gtalk_pvt *p = chan->tech_pvt; - enum ast_rtp_get_result res = AST_RTP_GET_FAILED; - - if (!p) - return res; - - ast_mutex_lock(&p->lock); - if (p->rtp){ - *rtp = p->rtp; - res = AST_RTP_TRY_PARTIAL; - } - ast_mutex_unlock(&p->lock); - - return res; -} - -static int gtalk_get_codec(struct ast_channel *chan) -{ - struct gtalk_pvt *p = chan->tech_pvt; - return p->peercapability; -} - -static int gtalk_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active) -{ - struct gtalk_pvt *p; - - p = chan->tech_pvt; - if (!p) - return -1; - ast_mutex_lock(&p->lock); - -/* if (rtp) - ast_rtp_get_peer(rtp, &p->redirip); - else - memset(&p->redirip, 0, sizeof(p->redirip)); - p->redircodecs = codecs; */ - - /* Reset lastrtprx timer */ - ast_mutex_unlock(&p->lock); - return 0; -} - -static int gtalk_response(struct gtalk *client, char *from, ikspak *pak, const char *reasonstr, const char *reasonstr2) -{ - iks *response = NULL, *error = NULL, *reason = NULL; - int res = -1; - - response = iks_new("iq"); - if (response) { - iks_insert_attrib(response, "type", "result"); - iks_insert_attrib(response, "from", from); - iks_insert_attrib(response, "to", iks_find_attrib(pak->x, "from")); - iks_insert_attrib(response, "id", iks_find_attrib(pak->x, "id")); - if (reasonstr) { - error = iks_new("error"); - if (error) { - iks_insert_attrib(error, "type", "cancel"); - reason = iks_new(reasonstr); - if (reason) - iks_insert_node(error, reason); - iks_insert_node(response, error); - } - } - iks_send(client->connection->p, response); - if (reason) - iks_delete(reason); - if (error) - iks_delete(error); - iks_delete(response); - res = 0; - } - return res; -} - -static int gtalk_is_answered(struct gtalk *client, ikspak *pak) -{ - struct gtalk_pvt *tmp; - char *from; - iks *codec; - char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ]; - int peernoncodeccapability; - - ast_log(LOG_DEBUG, "The client is %s\n", client->name); - /* Make sure our new call doesn't exist yet */ - for (tmp = client->p; tmp; tmp = tmp->next) { - if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) - break; - } - - /* codec points to the first <payload-type/> tag */ - codec = iks_child(iks_child(iks_child(pak->x))); - while (codec) { - ast_rtp_set_m_type(tmp->rtp, atoi(iks_find_attrib(codec, "id"))); - ast_rtp_set_rtpmap_type(tmp->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); - codec = iks_next(codec); - } - - /* Now gather all of the codecs that we are asked for */ - ast_rtp_get_current_formats(tmp->rtp, &tmp->peercapability, &peernoncodeccapability); - - /* at this point, we received an awser from the remote Gtalk client, - which allows us to compare capabilities */ - tmp->jointcapability = tmp->capability & tmp->peercapability; - if (!tmp->jointcapability) { - ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, tmp->capability), - ast_getformatname_multiple(s2, BUFSIZ, tmp->peercapability), - ast_getformatname_multiple(s3, BUFSIZ, tmp->jointcapability)); - /* close session if capabilities don't match */ - ast_queue_hangup(tmp->owner); - - return -1; - - } - - from = iks_find_attrib(pak->x, "to"); - if(!from) - from = client->connection->jid->full; - - if (tmp) { - if (tmp->owner) - ast_queue_control(tmp->owner, AST_CONTROL_ANSWER); - } else - ast_log(LOG_NOTICE, "Whoa, didn't find call!\n"); - gtalk_response(client, from, pak, NULL, NULL); - return 1; -} - -static int gtalk_is_accepted(struct gtalk *client, ikspak *pak) -{ - struct gtalk_pvt *tmp; - char *from; - - ast_log(LOG_DEBUG, "The client is %s\n", client->name); - /* find corresponding call */ - for (tmp = client->p; tmp; tmp = tmp->next) { - if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) - break; - } - - from = iks_find_attrib(pak->x, "to"); - if(!from) - from = client->connection->jid->full; - - if (!tmp) - ast_log(LOG_NOTICE, "Whoa, didn't find call!\n"); - - /* answer 'iq' packet to let the remote peer know that we're alive */ - gtalk_response(client, from, pak, NULL, NULL); - return 1; -} - -static int gtalk_handle_dtmf(struct gtalk *client, ikspak *pak) -{ - struct gtalk_pvt *tmp; - iks *dtmfnode = NULL, *dtmfchild = NULL; - char *dtmf; - char *from; - /* Make sure our new call doesn't exist yet */ - for (tmp = client->p; tmp; tmp = tmp->next) { - if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid) || iks_find_with_attrib(pak->x, "gtalk", "sid", tmp->sid)) - break; - } - from = iks_find_attrib(pak->x, "to"); - if(!from) - from = client->connection->jid->full; - - - if (tmp) { - if(iks_find_with_attrib(pak->x, "dtmf-method", "method", "rtp")) { - gtalk_response(client, from, pak, - "feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", - "unsupported-dtmf-method xmlns='http://jabber.org/protocol/gtalk/info/dtmf#errors'"); - return -1; - } - if ((dtmfnode = iks_find(pak->x, "dtmf"))) { - if((dtmf = iks_find_attrib(dtmfnode, "code"))) { - if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-up")) { - struct ast_frame f = {AST_FRAME_DTMF_BEGIN, }; - f.subclass = dtmf[0]; - ast_queue_frame(tmp->owner, &f); - ast_verbose("GOOGLE! DTMF-relay event received: %c\n", f.subclass); - } else if(iks_find_with_attrib(pak->x, "dtmf", "action", "button-down")) { - struct ast_frame f = {AST_FRAME_DTMF_END, }; - f.subclass = dtmf[0]; - ast_queue_frame(tmp->owner, &f); - ast_verbose("GOOGLE! DTMF-relay event received: %c\n", f.subclass); - } else if(iks_find_attrib(pak->x, "dtmf")) { /* 250 millasecond default */ - struct ast_frame f = {AST_FRAME_DTMF, }; - f.subclass = dtmf[0]; - ast_queue_frame(tmp->owner, &f); - ast_verbose("GOOGLE! DTMF-relay event received: %c\n", f.subclass); - } - } - } else if ((dtmfnode = iks_find_with_attrib(pak->x, "gtalk", "action", "session-info"))) { - if((dtmfchild = iks_find(dtmfnode, "dtmf"))) { - if((dtmf = iks_find_attrib(dtmfchild, "code"))) { - if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-up")) { - struct ast_frame f = {AST_FRAME_DTMF_END, }; - f.subclass = dtmf[0]; - ast_queue_frame(tmp->owner, &f); - ast_verbose("GOOGLE! DTMF-relay event received: %c\n", f.subclass); - } else if(iks_find_with_attrib(dtmfnode, "dtmf", "action", "button-down")) { - struct ast_frame f = {AST_FRAME_DTMF_BEGIN, }; - f.subclass = dtmf[0]; - ast_queue_frame(tmp->owner, &f); - ast_verbose("GOOGLE! DTMF-relay event received: %c\n", f.subclass); - } - } - } - } - gtalk_response(client, from, pak, NULL, NULL); - return 1; - } else - ast_log(LOG_NOTICE, "Whoa, didn't find call!\n"); - - gtalk_response(client, from, pak, NULL, NULL); - return 1; -} - -static int gtalk_hangup_farend(struct gtalk *client, ikspak *pak) -{ - struct gtalk_pvt *tmp; - char *from; - - ast_log(LOG_DEBUG, "The client is %s\n", client->name); - /* Make sure our new call doesn't exist yet */ - for (tmp = client->p; tmp; tmp = tmp->next) { - if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) - break; - } - from = iks_find_attrib(pak->x, "to"); - if(!from) - from = client->connection->jid->full; - - if (tmp) { - tmp->alreadygone = 1; - if (tmp->owner) - ast_queue_hangup(tmp->owner); - } else - ast_log(LOG_NOTICE, "Whoa, didn't find call!\n"); - gtalk_response(client, from, pak, NULL, NULL); - return 1; -} - -static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to) -{ - struct gtalk_candidate *tmp; - struct aji_client *c = client->connection; - struct gtalk_candidate *ours1 = NULL, *ours2 = NULL; - struct sockaddr_in sin; - struct sockaddr_in dest; - struct in_addr us; - iks *iq, *gtalk, *candidate, *transport; - char user[17], pass[17], preference[5], port[7]; - - - iq = iks_new("iq"); - gtalk = iks_new("session"); - candidate = iks_new("candidate"); - transport = iks_new("transport"); - if (!iq || !gtalk || !candidate || !transport) { - ast_log(LOG_ERROR, "Memory allocation error\n"); - goto safeout; - } - ours1 = ast_calloc(1, sizeof(*ours1)); - ours2 = ast_calloc(1, sizeof(*ours2)); - if (!ours1 || !ours2) - goto safeout; - - iks_insert_attrib(transport, "xmlns","http://www.google.com/transport/p2p"); - iks_insert_node(iq, gtalk); - iks_insert_node(gtalk,transport); - iks_insert_node(transport, candidate); - - for (; p; p = p->next) { - if (!strcasecmp(p->sid, sid)) - break; - } - - if (!p) { - ast_log(LOG_NOTICE, "No matching gtalk session - SID %s!\n", sid); - goto safeout; - } - - ast_rtp_get_us(p->rtp, &sin); - ast_find_ourip(&us, bindaddr); - - /* Setup our gtalk candidates */ - ast_copy_string(ours1->name, "rtp", sizeof(ours1->name)); - ours1->port = ntohs(sin.sin_port); - ours1->preference = 1; - snprintf(user, sizeof(user), "%08lx%08lx", ast_random(), ast_random()); - snprintf(pass, sizeof(pass), "%08lx%08lx", ast_random(), ast_random()); - ast_copy_string(ours1->username, user, sizeof(ours1->username)); - ast_copy_string(ours1->password, pass, sizeof(ours1->password)); - ast_copy_string(ours1->ip, ast_inet_ntoa(us), sizeof(ours1->ip)); - ours1->protocol = AJI_PROTOCOL_UDP; - ours1->type = AJI_CONNECT_LOCAL; - ours1->generation = 0; - p->ourcandidates = ours1; - - if (!ast_strlen_zero(externip)) { - /* XXX We should really stun for this one not just go with externip XXX */ - snprintf(user, sizeof(user), "%08lx%08lx", ast_random(), ast_random()); - snprintf(pass, sizeof(pass), "%08lx%08lx", ast_random(), ast_random()); - ast_copy_string(ours2->username, user, sizeof(ours2->username)); - ast_copy_string(ours2->password, pass, sizeof(ours2->password)); - ast_copy_string(ours2->ip, externip, sizeof(ours2->ip)); - ast_copy_string(ours2->name, "rtp", sizeof(ours1->name)); - ours2->port = ntohs(sin.sin_port); - ours2->preference = 0.9; - ours2->protocol = AJI_PROTOCOL_UDP; - ours2->type = AJI_CONNECT_STUN; - ours2->generation = 0; - ours1->next = ours2; - ours2 = NULL; - } - ours1 = NULL; - dest.sin_addr = __ourip; - dest.sin_port = sin.sin_port; - - - for (tmp = p->ourcandidates; tmp; tmp = tmp->next) { - snprintf(port, sizeof(port), "%d", tmp->port); - snprintf(preference, sizeof(preference), "%.2f", tmp->preference); - iks_insert_attrib(iq, "from", to); - iks_insert_attrib(iq, "to", from); - iks_insert_attrib(iq, "type", "set"); - iks_insert_attrib(iq, "id", c->mid); - ast_aji_increment_mid(c->mid); - iks_insert_attrib(gtalk, "type", "transport-info"); - iks_insert_attrib(gtalk, "id", sid); - iks_insert_attrib(gtalk, "initiator", (p->initiator) ? to : from); - iks_insert_attrib(gtalk, "xmlns", GOOGLE_NS); - iks_insert_attrib(candidate, "name", tmp->name); - iks_insert_attrib(candidate, "address", tmp->ip); - iks_insert_attrib(candidate, "port", port); - iks_insert_attrib(candidate, "username", tmp->username); - iks_insert_attrib(candidate, "password", tmp->password); - iks_insert_attrib(candidate, "preference", preference); - if (tmp->protocol == AJI_PROTOCOL_UDP) - iks_insert_attrib(candidate, "protocol", "udp"); - if (tmp->protocol == AJI_PROTOCOL_SSLTCP) - iks_insert_attrib(candidate, "protocol", "ssltcp"); - if (tmp->type == AJI_CONNECT_STUN) - iks_insert_attrib(candidate, "type", "stun"); - if (tmp->type == AJI_CONNECT_LOCAL) - iks_insert_attrib(candidate, "type", "local"); - if (tmp->type == AJI_CONNECT_RELAY) - iks_insert_attrib(candidate, "type", "relay"); - iks_insert_attrib(candidate, "network", "0"); - iks_insert_attrib(candidate, "generation", "0"); - iks_send(c->p, iq); - } - p->laststun = 0; - -safeout: - if (ours1) - free(ours1); - if (ours2) - free(ours2); - if (iq) - iks_delete(iq); - if (gtalk) - iks_delete(gtalk); - if (candidate) - iks_delete(candidate); - if(transport) - iks_delete(transport); - return 1; -} - -static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid) -{ - struct gtalk_pvt *tmp = NULL; - struct aji_resource *resources = NULL; - struct aji_buddy *buddy; - char idroster[200]; - char *data, *exten = NULL; - - if (option_debug) - ast_log(LOG_DEBUG, "The client is %s for alloc\n", client->name); - if (!sid && !strchr(them, '/')) { /* I started call! */ - if (!strcasecmp(client->name, "guest")) { - buddy = ASTOBJ_CONTAINER_FIND(&client->connection->buddies, them); - if (buddy) - resources = buddy->resources; - } else if (client->buddy) - resources = client->buddy->resources; - while (resources) { - if (resources->cap->jingle) { - break; - } - resources = resources->next; - } - if (resources) - snprintf(idroster, sizeof(idroster), "%s/%s", them, resources->resource); - else { - ast_log(LOG_ERROR, "no gtalk capable clients to talk to.\n"); - return NULL; - } - } - if (!(tmp = ast_calloc(1, sizeof(*tmp)))) { - return NULL; - } - if (sid) { - ast_copy_string(tmp->sid, sid, sizeof(tmp->sid)); - ast_copy_string(tmp->them, them, sizeof(tmp->them)); - ast_copy_string(tmp->us, us, sizeof(tmp->us)); - } else { - snprintf(tmp->sid, sizeof(tmp->sid), "%08lx%08lx", ast_random(), ast_random()); - ast_copy_string(tmp->them, idroster, sizeof(tmp->them)); - ast_copy_string(tmp->us, us, sizeof(tmp->us)); - tmp->initiator = 1; - } - /* clear codecs */ - tmp->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - ast_rtp_pt_clear(tmp->rtp); - - /* add user configured codec capabilites */ - if (client->capability) - tmp->capability = client->capability; - else if (global_capability) - tmp->capability = global_capability; - - tmp->parent = client; - if (!tmp->rtp) { - ast_log(LOG_WARNING, "Out of RTP sessions?\n"); - free(tmp); - return NULL; - } - - /* Set CALLERID(name) to the full JID of the remote peer */ - ast_copy_string(tmp->cid_name, tmp->them, sizeof(tmp->cid_name)); - - if(strchr(tmp->us, '/')) { - data = ast_strdupa(tmp->us); - exten = strsep(&data, "/"); - } else - exten = tmp->us; - ast_copy_string(tmp->exten, exten, sizeof(tmp->exten)); - ast_mutex_init(&tmp->lock); - ast_mutex_lock(>alklock); - tmp->next = client->p; - client->p = tmp; - ast_mutex_unlock(>alklock); - return tmp; -} - -/*! \brief Start new gtalk channel */ -static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i, int state, const char *title) -{ - struct ast_channel *tmp; - int fmt; - int what; - const char *n2; - - if (title) - n2 = title; - else - n2 = i->us; - tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, client->accountcode, i->exten, client->context, client->amaflags, "Gtalk/%s-%04lx", n2, ast_random() & 0xffff); - if (!tmp) { - ast_log(LOG_WARNING, "Unable to allocate Gtalk channel structure!\n"); - return NULL; - } - tmp->tech = >alk_tech; - - /* Select our native format based on codec preference until we receive - something from another device to the contrary. */ - if (i->jointcapability) - what = i->jointcapability; - else if (i->capability) - what = i->capability; - else - what = global_capability; - tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK); - fmt = ast_best_codec(tmp->nativeformats); - - if (i->rtp) { - ast_rtp_setstun(i->rtp, 1); - tmp->fds[0] = ast_rtp_fd(i->rtp); - tmp->fds[1] = ast_rtcp_fd(i->rtp); - } - if (i->vrtp) { - ast_rtp_setstun(i->rtp, 1); - tmp->fds[2] = ast_rtp_fd(i->vrtp); - tmp->fds[3] = ast_rtcp_fd(i->vrtp); - } - if (state == AST_STATE_RING) - tmp->rings = 1; - tmp->adsicpe = AST_ADSI_UNAVAILABLE; - tmp->writeformat = fmt; - tmp->rawwriteformat = fmt; - tmp->readformat = fmt; - tmp->rawreadformat = fmt; - tmp->tech_pvt = i; - - tmp->callgroup = client->callgroup; - tmp->pickupgroup = client->pickupgroup; - tmp->cid.cid_pres = client->callingpres; - if (!ast_strlen_zero(client->accountcode)) - ast_string_field_set(tmp, accountcode, client->accountcode); - if (client->amaflags) - tmp->amaflags = client->amaflags; - if (!ast_strlen_zero(client->language)) - ast_string_field_set(tmp, language, client->language); - if (!ast_strlen_zero(client->musicclass)) - ast_string_field_set(tmp, musicclass, client->musicclass); - i->owner = tmp; - ast_module_ref(ast_module_info->self); - ast_copy_string(tmp->context, client->context, sizeof(tmp->context)); - ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten)); - - if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) - tmp->cid.cid_dnid = ast_strdup(i->exten); - tmp->priority = 1; - if (i->rtp) - ast_jb_configure(tmp, &global_jbconf); - if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); - tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION; - ast_hangup(tmp); - tmp = NULL; - } - - return tmp; -} - -static int gtalk_action(struct gtalk *client, struct gtalk_pvt *p, const char *action) -{ - iks *request, *session = NULL; - int res = -1; - - request = iks_new("iq"); - if (request) { - iks_insert_attrib(request, "type", "set"); - iks_insert_attrib(request, "from", p->us); - iks_insert_attrib(request, "to", p->them); - iks_insert_attrib(request, "id", client->connection->mid); - ast_aji_increment_mid(client->connection->mid); - session = iks_new("session"); - if (session) { - iks_insert_attrib(session, "type", action); - iks_insert_attrib(session, "id", p->sid); - iks_insert_attrib(session, "initiator", p->initiator ? p->us : p->them); - iks_insert_attrib(session, "xmlns", "http://www.google.com/session"); - iks_insert_node(request, session); - iks_send(client->connection->p, request); - iks_delete(session); - res = 0; - } - iks_delete(request); - } - return res; -} - -static void gtalk_free_candidates(struct gtalk_candidate *candidate) -{ - struct gtalk_candidate *last; - while (candidate) { - last = candidate; - candidate = candidate->next; - free(last); - } -} - -static void gtalk_free_pvt(struct gtalk *client, struct gtalk_pvt *p) -{ - struct gtalk_pvt *cur, *prev = NULL; - cur = client->p; - while (cur) { - if (cur == p) { - if (prev) - prev->next = p->next; - else - client->p = p->next; - break; - } - prev = cur; - cur = cur->next; - } - if (p->ringrule) - iks_filter_remove_rule(p->parent->connection->f, p->ringrule); - if (p->owner) - ast_log(LOG_WARNING, "Uh oh, there's an owner, this is going to be messy.\n"); - if (p->rtp) - ast_rtp_destroy(p->rtp); - if (p->vrtp) - ast_rtp_destroy(p->vrtp); - gtalk_free_candidates(p->theircandidates); - free(p); -} - - -static int gtalk_newcall(struct gtalk *client, ikspak *pak) -{ - struct gtalk_pvt *p, *tmp = client->p; - struct ast_channel *chan; - int res; - iks *codec; - char *from = NULL; - char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ]; - int peernoncodeccapability; - - /* Make sure our new call doesn't exist yet */ - from = iks_find_attrib(pak->x,"to"); - if(!from) - from = client->connection->jid->full; - - while (tmp) { - if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) { - ast_log(LOG_NOTICE, "Ignoring duplicate call setup on SID %s\n", tmp->sid); - gtalk_response(client, from, pak, "out-of-order", NULL); - return -1; - } - tmp = tmp->next; - } - - if (!strcasecmp(client->name, "guest")){ - /* the guest account is not tied to any configured XMPP client, - let's set it now */ - client->connection = ast_aji_get_client(from); - if (!client->connection) { - ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", from); - return -1; - } - } - - p = gtalk_alloc(client, from, pak->from->full, iks_find_attrib(pak->query, "id")); - if (!p) { - ast_log(LOG_WARNING, "Unable to allocate gtalk structure!\n"); - return -1; - } - - chan = gtalk_new(client, p, AST_STATE_DOWN, pak->from->user); - if (!chan) { - gtalk_free_pvt(client, p); - return -1; - } - - ast_mutex_lock(&p->lock); - ast_copy_string(p->them, pak->from->full, sizeof(p->them)); - if (iks_find_attrib(pak->query, "id")) { - ast_copy_string(p->sid, iks_find_attrib(pak->query, "id"), - sizeof(p->sid)); - } - - /* codec points to the first <payload-type/> tag */ - codec = iks_child(iks_child(iks_child(pak->x))); - - while (codec) { - ast_rtp_set_m_type(p->rtp, atoi(iks_find_attrib(codec, "id"))); - ast_rtp_set_rtpmap_type(p->rtp, atoi(iks_find_attrib(codec, "id")), "audio", iks_find_attrib(codec, "name"), 0); - codec = iks_next(codec); - } - - /* Now gather all of the codecs that we are asked for */ - ast_rtp_get_current_formats(p->rtp, &p->peercapability, &peernoncodeccapability); - p->jointcapability = p->capability & p->peercapability; - ast_mutex_unlock(&p->lock); - - ast_setstate(chan, AST_STATE_RING); - if (!p->jointcapability) { - ast_log(LOG_WARNING, "Capabilities don't match : us - %s, peer - %s, combined - %s \n", ast_getformatname_multiple(s1, BUFSIZ, p->capability), - ast_getformatname_multiple(s2, BUFSIZ, p->peercapability), - ast_getformatname_multiple(s3, BUFSIZ, p->jointcapability)); - /* close session if capabilities don't match */ - gtalk_action(client, p, "reject"); - p->alreadygone = 1; - gtalk_hangup(chan); - ast_channel_free(chan); - return -1; - } - - res = ast_pbx_start(chan); - - switch (res) { - case AST_PBX_FAILED: - ast_log(LOG_WARNING, "Failed to start PBX :(\n"); - gtalk_response(client, from, pak, "service-unavailable", NULL); - break; - case AST_PBX_CALL_LIMIT: - ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n"); - gtalk_response(client, from, pak, "service-unavailable", NULL); - break; - case AST_PBX_SUCCESS: - gtalk_response(client, from, pak, NULL, NULL); - gtalk_invite_response(p, p->them, p->us,p->sid, 0); - gtalk_create_candidates(client, p, p->sid, p->them, p->us); - /* nothing to do */ - break; - } - - return 1; -} - -static int gtalk_update_stun(struct gtalk *client, struct gtalk_pvt *p) -{ - struct gtalk_candidate *tmp; - struct hostent *hp; - struct ast_hostent ahp; - struct sockaddr_in sin; - struct sockaddr_in aux; - - if (time(NULL) == p->laststun) - return 0; - - tmp = p->theircandidates; - p->laststun = time(NULL); - while (tmp) { - char username[256]; - - /* Find the IP address of the host */ - hp = ast_gethostbyname(tmp->ip, &ahp); - sin.sin_family = AF_INET; - memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); - sin.sin_port = htons(tmp->port); - snprintf(username, sizeof(username), "%s%s", tmp->username, - p->ourcandidates->username); - - /* Find out the result of the STUN */ - ast_rtp_get_peer(p->rtp, &aux); - - /* If the STUN result is different from the IP of the hostname, - lock on the stun IP of the hostname advertised by the - remote client */ - if (aux.sin_addr.s_addr && - aux.sin_addr.s_addr != sin.sin_addr.s_addr) - ast_rtp_stun_request(p->rtp, &aux, username); - else - ast_rtp_stun_request(p->rtp, &sin, username); - - if (aux.sin_addr.s_addr && option_debug > 3) { - ast_log(LOG_DEBUG, "Receiving RTP traffic from IP %s, matches with remote candidate's IP %s\n", ast_inet_ntoa(aux.sin_addr), tmp->ip); - ast_log(LOG_DEBUG, "Sending STUN request to %s\n", tmp->ip); - } - - tmp = tmp->next; - } - return 1; -} - -static int gtalk_add_candidate(struct gtalk *client, ikspak *pak) -{ - struct gtalk_pvt *p = NULL, *tmp = NULL; - struct aji_client *c = client->connection; - struct gtalk_candidate *newcandidate = NULL; - iks *traversenodes = NULL, *receipt = NULL; - char *from; - - from = iks_find_attrib(pak->x,"to"); - if(!from) - from = c->jid->full; - - for (tmp = client->p; tmp; tmp = tmp->next) { - if (iks_find_with_attrib(pak->x, "session", "id", tmp->sid)) { - p = tmp; - break; - } - } - - if (!p) - return -1; - - traversenodes = pak->query; - while(traversenodes) { - if(!strcasecmp(iks_name(traversenodes), "session")) { - traversenodes = iks_child(traversenodes); - continue; - } - if(!strcasecmp(iks_name(traversenodes), "transport")) { - traversenodes = iks_child(traversenodes); - continue; - } - if(!strcasecmp(iks_name(traversenodes), "candidate")) { - newcandidate = ast_calloc(1, sizeof(*newcandidate)); - if (!newcandidate) - return 0; - ast_copy_string(newcandidate->name, iks_find_attrib(traversenodes, "name"), - sizeof(newcandidate->name)); - ast_copy_string(newcandidate->ip, iks_find_attrib(traversenodes, "address"), - sizeof(newcandidate->ip)); - newcandidate->port = atoi(iks_find_attrib(traversenodes, "port")); - ast_copy_string(newcandidate->username, iks_find_attrib(traversenodes, "username"), - sizeof(newcandidate->username)); - ast_copy_string(newcandidate->password, iks_find_attrib(traversenodes, "password"), - sizeof(newcandidate->password)); - newcandidate->preference = atof(iks_find_attrib(traversenodes, "preference")); - if (!strcasecmp(iks_find_attrib(traversenodes, "protocol"), "udp")) - newcandidate->protocol = AJI_PROTOCOL_UDP; - if (!strcasecmp(iks_find_attrib(traversenodes, "protocol"), "ssltcp")) - newcandidate->protocol = AJI_PROTOCOL_SSLTCP; - - if (!strcasecmp(iks_find_attrib(traversenodes, "type"), "stun")) - newcandidate->type = AJI_CONNECT_STUN; - if (!strcasecmp(iks_find_attrib(traversenodes, "type"), "local")) - newcandidate->type = AJI_CONNECT_LOCAL; - if (!strcasecmp(iks_find_attrib(traversenodes, "type"), "relay")) - newcandidate->type = AJI_CONNECT_RELAY; - ast_copy_string(newcandidate->network, iks_find_attrib(traversenodes, "network"), - sizeof(newcandidate->network)); - newcandidate->generation = atoi(iks_find_attrib(traversenodes, "generation")); - newcandidate->next = NULL; - - newcandidate->next = p->theircandidates; - p->theircandidates = newcandidate; - p->laststun = 0; - gtalk_update_stun(p->parent, p); - newcandidate = NULL; - } - traversenodes = iks_next(traversenodes); - } - - receipt = iks_new("iq"); - iks_insert_attrib(receipt, "type", "result"); - iks_insert_attrib(receipt, "from", from); - iks_insert_attrib(receipt, "to", iks_find_attrib(pak->x, "from")); - iks_insert_attrib(receipt, "id", iks_find_attrib(pak->x, "id")); - iks_send(c->p, receipt); - iks_delete(receipt); - - return 1; -} - -static struct ast_frame *gtalk_rtp_read(struct ast_channel *ast, struct gtalk_pvt *p) -{ - struct ast_frame *f; - - if (!p->rtp) - return &ast_null_frame; - f = ast_rtp_read(p->rtp); - gtalk_update_stun(p->parent, p); - if (p->owner) { - /* We already hold the channel lock */ - if (f->frametype == AST_FRAME_VOICE) { - if (f->subclass != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) { - if (option_debug) - ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass); - p->owner->nativeformats = - (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass; - ast_set_read_format(p->owner, p->owner->readformat); - ast_set_write_format(p->owner, p->owner->writeformat); - } -/* if ((ast_test_flag(p, SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) { - f = ast_dsp_process(p->owner, p->vad, f); - if (option_debug && f && (f->frametype == AST_FRAME_DTMF)) - ast_log(LOG_DEBUG, "* Detected inband DTMF '%c'\n", f->subclass); - } */ - } - } - return f; -} - -static struct ast_frame *gtalk_read(struct ast_channel *ast) -{ - struct ast_frame *fr; - struct gtalk_pvt *p = ast->tech_pvt; - - ast_mutex_lock(&p->lock); - fr = gtalk_rtp_read(ast, p); - ast_mutex_unlock(&p->lock); - return fr; -} - -/*! \brief Send frame to media channel (rtp) */ -static int gtalk_write(struct ast_channel *ast, struct ast_frame *frame) -{ - struct gtalk_pvt *p = ast->tech_pvt; - int res = 0; - - switch (frame->frametype) { - case AST_FRAME_VOICE: - if (!(frame->subclass & ast->nativeformats)) { - ast_log(LOG_WARNING, - "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n", - frame->subclass, ast->nativeformats, ast->readformat, - ast->writeformat); - return 0; - } - if (p) { - ast_mutex_lock(&p->lock); - if (p->rtp) { - res = ast_rtp_write(p->rtp, frame); - } - ast_mutex_unlock(&p->lock); - } - break; - case AST_FRAME_VIDEO: - if (p) { - ast_mutex_lock(&p->lock); - if (p->vrtp) { - res = ast_rtp_write(p->vrtp, frame); - } - ast_mutex_unlock(&p->lock); - } - break; - case AST_FRAME_IMAGE: - return 0; - break; - default: - ast_log(LOG_WARNING, "Can't send %d type frames with Gtalk write\n", - frame->frametype); - return 0; - } - - return res; -} - -static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct gtalk_pvt *p = newchan->tech_pvt; - ast_mutex_lock(&p->lock); - - if ((p->owner != oldchan)) { - ast_mutex_unlock(&p->lock); - return -1; - } - if (p->owner == oldchan) - p->owner = newchan; - ast_mutex_unlock(&p->lock); - return 0; -} - -static int gtalk_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen) -{ - int res = 0; - - switch (condition) { - case AST_CONTROL_HOLD: - ast_moh_start(ast, data, NULL); - break; - case AST_CONTROL_UNHOLD: - ast_moh_stop(ast); - break; - default: - ast_log(LOG_NOTICE, "Don't know how to indicate condition '%d'\n", condition); - res = -1; - } - - return res; -} - -static int gtalk_digit_begin(struct ast_channel *chan, char digit) -{ - return gtalk_digit(chan, digit, 0); -} - -static int gtalk_digit_end(struct ast_channel *chan, char digit, unsigned int duration) -{ - return gtalk_digit(chan, digit, duration); -} - -static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration) -{ - struct gtalk_pvt *p = ast->tech_pvt; - struct gtalk *client = p->parent; - iks *iq, *gtalk, *dtmf; - char buffer[2] = {digit, '\0'}; - iq = iks_new("iq"); - gtalk = iks_new("gtalk"); - dtmf = iks_new("dtmf"); - if(!iq || !gtalk || !dtmf) { - if(iq) - iks_delete(iq); - if(gtalk) - iks_delete(gtalk); - if(dtmf) - iks_delete(dtmf); - ast_log(LOG_ERROR, "Did not send dtmf do to memory issue\n"); - return -1; - } - - iks_insert_attrib(iq, "type", "set"); - iks_insert_attrib(iq, "to", p->them); - iks_insert_attrib(iq, "from", p->us); - iks_insert_attrib(iq, "id", client->connection->mid); - ast_aji_increment_mid(client->connection->mid); - iks_insert_attrib(gtalk, "xmlns", "http://jabber.org/protocol/gtalk"); - iks_insert_attrib(gtalk, "action", "session-info"); - iks_insert_attrib(gtalk, "initiator", p->initiator ? p->us: p->them); - iks_insert_attrib(gtalk, "sid", p->sid); - iks_insert_attrib(dtmf, "xmlns", "http://jabber.org/protocol/gtalk/info/dtmf"); - iks_insert_attrib(dtmf, "code", buffer); - iks_insert_node(iq, gtalk); - iks_insert_node(gtalk, dtmf); - - ast_mutex_lock(&p->lock); - if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN || duration == 0) { - iks_insert_attrib(dtmf, "action", "button-down"); - } else if (ast->dtmff.frametype == AST_FRAME_DTMF_END || duration != 0) { - iks_insert_attrib(dtmf, "action", "button-up"); - } - iks_send(client->connection->p, iq); - iks_delete(iq); - iks_delete(gtalk); - iks_delete(dtmf); - ast_mutex_unlock(&p->lock); - return 0; -} - -static int gtalk_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen) -{ - ast_log(LOG_NOTICE, "XXX Implement gtalk sendhtml XXX\n"); - - return -1; -} - -/* Not in use right now. -static int gtalk_auto_congest(void *nothing) -{ - struct gtalk_pvt *p = nothing; - - ast_mutex_lock(&p->lock); - if (p->owner) { - if (!ast_channel_trylock(p->owner)) { - ast_log(LOG_NOTICE, "Auto-congesting %s\n", p->owner->name); - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - ast_channel_unlock(p->owner); - } - } - ast_mutex_unlock(&p->lock); - return 0; -} -*/ - -/*! \brief Initiate new call, part of PBX interface - * dest is the dial string */ -static int gtalk_call(struct ast_channel *ast, char *dest, int timeout) -{ - struct gtalk_pvt *p = ast->tech_pvt; - - if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { - ast_log(LOG_WARNING, "gtalk_call called on %s, neither down nor reserved\n", ast->name); - return -1; - } - - ast_setstate(ast, AST_STATE_RING); - if (!p->ringrule) { - ast_copy_string(p->ring, p->parent->connection->mid, sizeof(p->ring)); - p->ringrule = iks_filter_add_rule(p->parent->connection->f, gtalk_ringing_ack, p, - IKS_RULE_ID, p->ring, IKS_RULE_DONE); - } else - ast_log(LOG_WARNING, "Whoa, already have a ring rule!\n"); - - gtalk_invite(p, p->them, p->us, p->sid, 1); - gtalk_create_candidates(p->parent, p, p->sid, p->them, p->us); - - return 0; -} - -/*! \brief Hangup a call through the gtalk proxy channel */ -static int gtalk_hangup(struct ast_channel *ast) -{ - struct gtalk_pvt *p = ast->tech_pvt; - struct gtalk *client; - - ast_mutex_lock(&p->lock); - client = p->parent; - p->owner = NULL; - ast->tech_pvt = NULL; - if (!p->alreadygone) - gtalk_action(client, p, "terminate"); - ast_mutex_unlock(&p->lock); - - gtalk_free_pvt(client, p); - ast_module_unref(ast_module_info->self); - - return 0; -} - -/*! \brief Part of PBX interface */ -static struct ast_channel *gtalk_request(const char *type, int format, void *data, int *cause) -{ - struct gtalk_pvt *p = NULL; - struct gtalk *client = NULL; - char *sender = NULL, *to = NULL, *s = NULL; - struct ast_channel *chan = NULL; - - if (data) { - s = ast_strdupa(data); - if (s) { - sender = strsep(&s, "/"); - if (sender && (sender[0] != '\0')) - to = strsep(&s, "/"); - if (!to) { - ast_log(LOG_ERROR, "Bad arguments in Gtalk Dialstring: %s\n", (char*) data); - return NULL; - } - } - } - - client = find_gtalk(to, sender); - if (!client) { - ast_log(LOG_WARNING, "Could not find recipient.\n"); - return NULL; - } - if (!strcasecmp(client->name, "guest")){ - /* the guest account is not tied to any configured XMPP client, - let's set it now */ - client->connection = ast_aji_get_client(sender); - if (!client->connection) { - ast_log(LOG_ERROR, "No XMPP client to talk to, us (partial JID) : %s\n", sender); - ASTOBJ_UNREF(client, gtalk_member_destroy); - return NULL; - } - } - - ASTOBJ_WRLOCK(client); - p = gtalk_alloc(client, strchr(sender, '@') ? sender : client->connection->jid->full, strchr(to, '@') ? to : client->user, NULL); - if (p) - chan = gtalk_new(client, p, AST_STATE_DOWN, to); - - ASTOBJ_UNLOCK(client); - return chan; -} - -/*! \brief CLI command "gtalk show channels" */ -static int gtalk_show_channels(int fd, int argc, char **argv) -{ -#define FORMAT "%-30.30s %-30.30s %-15.15s %-5.5s %-5.5s \n" - struct gtalk_pvt *p; - struct ast_channel *chan; - int numchans = 0; - char them[AJI_MAX_JIDLEN]; - char *jid = NULL; - char *resource = NULL; - - if (argc != 3) - return RESULT_SHOWUSAGE; - - ast_mutex_lock(>alklock); - ast_cli(fd, FORMAT, "Channel", "Jabber ID", "Resource", "Read", "Write"); - ASTOBJ_CONTAINER_TRAVERSE(>alk_list, 1, { - ASTOBJ_WRLOCK(iterator); - p = iterator->p; - while(p) { - chan = p->owner; - ast_copy_string(them, p->them, sizeof(them)); - jid = them; - resource = strchr(them, '/'); - if (!resource) - resource = "None"; - else { - *resource = '\0'; - resource ++; - } - if (chan) - ast_cli(fd, FORMAT, - chan->name, - jid, - resource, - ast_getformatname(chan->readformat), - ast_getformatname(chan->writeformat) - ); - else - ast_log(LOG_WARNING, "No available channel\n"); - numchans ++; - p = p->next; - } - ASTOBJ_UNLOCK(iterator); - }); - - ast_mutex_unlock(>alklock); - - ast_cli(fd, "%d active gtalk channel%s\n", numchans, (numchans != 1) ? "s" : ""); - return RESULT_SUCCESS; -#undef FORMAT -} - -/*! \brief CLI command "gtalk show channels" */ -static int gtalk_do_reload(int fd, int argc, char **argv) -{ - ast_verbose("IT DOES WORK!\n"); - return RESULT_SUCCESS; -} - -static int gtalk_parser(void *data, ikspak *pak) -{ - struct gtalk *client = ASTOBJ_REF((struct gtalk *) data); - - if (iks_find_with_attrib(pak->x, "session", "type", "initiate")) { - /* New call */ - gtalk_newcall(client, pak); - } else if (iks_find_with_attrib(pak->x, "session", "type", "candidates") || iks_find_with_attrib(pak->x, "session", "type", "transport-info")) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "About to add candidate!\n"); - gtalk_add_candidate(client, pak); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Candidate Added!\n"); - } else if (iks_find_with_attrib(pak->x, "session", "type", "accept")) { - gtalk_is_answered(client, pak); - } else if (iks_find_with_attrib(pak->x, "session", "type", "transport-accept")) { - gtalk_is_accepted(client, pak); - } else if (iks_find_with_attrib(pak->x, "session", "type", "content-info") || iks_find_with_attrib(pak->x, "gtalk", "action", "session-info")) { - gtalk_handle_dtmf(client, pak); - } else if (iks_find_with_attrib(pak->x, "session", "type", "terminate")) { - gtalk_hangup_farend(client, pak); - } else if (iks_find_with_attrib(pak->x, "session", "type", "reject")) { - gtalk_hangup_farend(client, pak); - } - ASTOBJ_UNREF(client, gtalk_member_destroy); - return IKS_FILTER_EAT; -} - -/* Not using this anymore probably take out soon -static struct gtalk_candidate *gtalk_create_candidate(char *args) -{ - char *name, *type, *preference, *protocol; - struct gtalk_candidate *res; - res = malloc(sizeof(struct gtalk_candidate)); - memset(res, 0, sizeof(struct gtalk_candidate)); - if (args) - name = args; - if ((args = strchr(args, ','))) { - *args = '\0'; - args++; - preference = args; - } - if ((args = strchr(args, ','))) { - *args = '\0'; - args++; - protocol = args; - } - if ((args = strchr(args, ','))) { - *args = '\0'; - args++; - type = args; - } - if (name) - ast_copy_string(res->name, name, sizeof(res->name)); - if (preference) { - res->preference = atof(preference); - } - if (protocol) { - if (!strcasecmp("udp", protocol)) - res->protocol = AJI_PROTOCOL_UDP; - if (!strcasecmp("ssltcp", protocol)) - res->protocol = AJI_PROTOCOL_SSLTCP; - } - if (type) { - if (!strcasecmp("stun", type)) - res->type = AJI_CONNECT_STUN; - if (!strcasecmp("local", type)) - res->type = AJI_CONNECT_LOCAL; - if (!strcasecmp("relay", type)) - res->type = AJI_CONNECT_RELAY; - } - - return res; -} -*/ - -static int gtalk_create_member(char *label, struct ast_variable *var, int allowguest, - struct ast_codec_pref prefs, char *context, - struct gtalk *member) -{ - struct aji_client *client; - - if (!member) - ast_log(LOG_WARNING, "Out of memory.\n"); - - ast_copy_string(member->name, label, sizeof(member->name)); - ast_copy_string(member->user, label, sizeof(member->user)); - ast_copy_string(member->context, context, sizeof(member->context)); - member->allowguest = allowguest; - member->prefs = prefs; - while (var) { -#if 0 - struct gtalk_candidate *candidate = NULL; -#endif - if (!strcasecmp(var->name, "username")) - ast_copy_string(member->user, var->value, sizeof(member->user)); - else if (!strcasecmp(var->name, "disallow")) - ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 0); - else if (!strcasecmp(var->name, "allow")) - ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 1); - else if (!strcasecmp(var->name, "context")) - ast_copy_string(member->context, var->value, sizeof(member->context)); -#if 0 - else if (!strcasecmp(var->name, "candidate")) { - candidate = gtalk_create_candidate(var->value); - if (candidate) { - candidate->next = member->ourcandidates; - member->ourcandidates = candidate; - } - } -#endif - else if (!strcasecmp(var->name, "connection")) { - if ((client = ast_aji_get_client(var->value))) { - member->connection = client; - iks_filter_add_rule(client->f, gtalk_parser, member, - IKS_RULE_TYPE, IKS_PAK_IQ, - IKS_RULE_FROM_PARTIAL, member->user, - IKS_RULE_NS, "http://www.google.com/session", - IKS_RULE_DONE); - - } else { - ast_log(LOG_ERROR, "connection referenced not found!\n"); - return 0; - } - } - var = var->next; - } - if (member->connection && member->user) - member->buddy = ASTOBJ_CONTAINER_FIND(&member->connection->buddies, member->user); - else { - ast_log(LOG_ERROR, "No Connection or Username!\n"); - } - return 1; -} - -static int gtalk_load_config(void) -{ - char *cat = NULL; - struct ast_config *cfg = NULL; - char context[AST_MAX_CONTEXT]; - int allowguest = 1; - struct ast_variable *var; - struct gtalk *member; - struct ast_codec_pref prefs; - struct aji_client_container *clients; - struct gtalk_candidate *global_candidates = NULL; - struct hostent *hp; - struct ast_hostent ahp; - - cfg = ast_config_load(GOOGLE_CONFIG); - if (!cfg) - return 0; - - /* Copy the default jb config over global_jbconf */ - memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); - - cat = ast_category_browse(cfg, NULL); - for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { - /* handle jb conf */ - if (!ast_jb_read_conf(&global_jbconf, var->name, var->value)) - continue; - - if (!strcasecmp(var->name, "allowguest")) - allowguest = - (ast_true(ast_variable_retrieve(cfg, "general", "allowguest"))) ? 1 : 0; - else if (!strcasecmp(var->name, "disallow")) - ast_parse_allow_disallow(&prefs, &global_capability, var->value, 0); - else if (!strcasecmp(var->name, "allow")) - ast_parse_allow_disallow(&prefs, &global_capability, var->value, 1); - else if (!strcasecmp(var->name, "context")) - ast_copy_string(context, var->value, sizeof(context)); - else if (!strcasecmp(var->name, "bindaddr")) { - if (!(hp = ast_gethostbyname(var->value, &ahp))) { - ast_log(LOG_WARNING, "Invalid address: %s\n", var->value); - } else { - memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr)); - } - } -/* Idea to allow for custom candidates */ -/* - else if (!strcasecmp(var->name, "candidate")) { - candidate = gtalk_create_candidate(var->value); - if (candidate) { - candidate->next = global_candidates; - global_candidates = candidate; - } - } -*/ - } - while (cat) { - if (strcasecmp(cat, "general")) { - var = ast_variable_browse(cfg, cat); - member = (struct gtalk *) malloc(sizeof(struct gtalk)); - memset(member, 0, sizeof(struct gtalk)); - ASTOBJ_INIT(member); - ASTOBJ_WRLOCK(member); - if (!strcasecmp(cat, "guest")) { - ast_copy_string(member->name, "guest", sizeof(member->name)); - ast_copy_string(member->user, "guest", sizeof(member->user)); - ast_copy_string(member->context, context, sizeof(member->context)); - member->allowguest = allowguest; - member->prefs = prefs; - while (var) { - if (!strcasecmp(var->name, "disallow")) - ast_parse_allow_disallow(&member->prefs, &member->capability, - var->value, 0); - else if (!strcasecmp(var->name, "allow")) - ast_parse_allow_disallow(&member->prefs, &member->capability, - var->value, 1); - else if (!strcasecmp(var->name, "context")) - ast_copy_string(member->context, var->value, - sizeof(member->context)); -/* Idea to allow for custom candidates */ -/* - else if (!strcasecmp(var->name, "candidate")) { - candidate = gtalk_create_candidate(var->value); - if (candidate) { - candidate->next = member->ourcandidates; - member->ourcandidates = candidate; - } - } -*/ - var = var->next; - } - ASTOBJ_UNLOCK(member); - clients = ast_aji_get_clients(); - if (clients) { - ASTOBJ_CONTAINER_TRAVERSE(clients, 1, { - ASTOBJ_WRLOCK(iterator); - ASTOBJ_WRLOCK(member); - member->connection = NULL; - iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, "http://www.google.com/session", IKS_RULE_DONE); - iks_filter_add_rule(iterator->f, gtalk_parser, member, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_NS, "http://jabber.org/protocol/gtalk", IKS_RULE_DONE); - ASTOBJ_UNLOCK(member); - ASTOBJ_UNLOCK(iterator); - }); - ASTOBJ_CONTAINER_LINK(>alk_list, member); - ASTOBJ_UNREF(member, gtalk_member_destroy); - } else { - ASTOBJ_UNLOCK(member); - ASTOBJ_UNREF(member, gtalk_member_destroy); - } - } else { - ASTOBJ_UNLOCK(member); - if (gtalk_create_member(cat, var, allowguest, prefs, context, member)) - ASTOBJ_CONTAINER_LINK(>alk_list, member); - ASTOBJ_UNREF(member, gtalk_member_destroy); - } - } - cat = ast_category_browse(cfg, cat); - } - gtalk_free_candidates(global_candidates); - return 1; -} - -/*! \brief Load module into PBX, register channel */ -static int load_module(void) -{ - char *jabber_loaded = ast_module_helper("", "res_jabber.so", 0, 0, 0, 0); - free(jabber_loaded); - if (!jabber_loaded) { - /* If embedded, check for a different module name */ - jabber_loaded = ast_module_helper("", "res_jabber", 0, 0, 0, 0); - free(jabber_loaded); - if (!jabber_loaded) { - ast_log(LOG_ERROR, "chan_gtalk.so depends upon res_jabber.so\n"); - return AST_MODULE_LOAD_DECLINE; - } - } - -#ifdef HAVE_GNUTLS - gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); -#endif /* HAVE_GNUTLS */ - - ASTOBJ_CONTAINER_INIT(>alk_list); - if (!gtalk_load_config()) { - ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", GOOGLE_CONFIG); - return 0; - } - - sched = sched_context_create(); - if (!sched) - ast_log(LOG_WARNING, "Unable to create schedule context\n"); - - io = io_context_create(); - if (!io) - ast_log(LOG_WARNING, "Unable to create I/O context\n"); - - if (ast_find_ourip(&__ourip, bindaddr)) { - ast_log(LOG_WARNING, "Unable to get own IP address, Gtalk disabled\n"); - return 0; - } - - ast_rtp_proto_register(>alk_rtp); - ast_cli_register_multiple(gtalk_cli, sizeof(gtalk_cli) / sizeof(gtalk_cli[0])); - - /* Make sure we can register our channel type */ - if (ast_channel_register(>alk_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class %s\n", gtalk_tech.type); - return -1; - } - return 0; -} - -/*! \brief Reload module */ -static int reload(void) -{ - return 0; -} - -/*! \brief Unload the gtalk channel from Asterisk */ -static int unload_module(void) -{ - struct gtalk_pvt *privates = NULL; - ast_cli_unregister_multiple(gtalk_cli, sizeof(gtalk_cli) / sizeof(gtalk_cli[0])); - /* First, take us out of the channel loop */ - ast_channel_unregister(>alk_tech); - ast_rtp_proto_unregister(>alk_rtp); - - if (!ast_mutex_lock(>alklock)) { - /* Hangup all interfaces if they have an owner */ - ASTOBJ_CONTAINER_TRAVERSE(>alk_list, 1, { - ASTOBJ_WRLOCK(iterator); - privates = iterator->p; - while(privates) { - if (privates->owner) - ast_softhangup(privates->owner, AST_SOFTHANGUP_APPUNLOAD); - privates = privates->next; - } - iterator->p = NULL; - ASTOBJ_UNLOCK(iterator); - }); - ast_mutex_unlock(>alklock); - } else { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - return -1; - } - ASTOBJ_CONTAINER_DESTROYALL(>alk_list, gtalk_member_destroy); - ASTOBJ_CONTAINER_DESTROY(>alk_list); - return 0; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Gtalk Channel Driver", - .load = load_module, - .unload = unload_module, - .reload = reload, - ); diff --git a/1.4.23-rc4/channels/chan_h323.c b/1.4.23-rc4/channels/chan_h323.c deleted file mode 100644 index 1199ddda9..000000000 --- a/1.4.23-rc4/channels/chan_h323.c +++ /dev/null @@ -1,3274 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005 - * - * OpenH323 Channel Driver for ASTERISK PBX. - * By Jeremy McNamara - * For The NuFone Network - * - * chan_h323 has been derived from code created by - * Michael Manousos and Mark Spencer - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief This file is part of the chan_h323 driver for Asterisk - * - * \author Jeremy McNamara - * - * \par See also - * \arg Config_h323 - * - * \ingroup channel_drivers - */ - -/*** MODULEINFO - <depend>openh323</depend> - <defaultenabled>yes</defaultenabled> - ***/ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#ifdef __cplusplus -} -#endif - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/signal.h> -#include <sys/param.h> -#if defined(BSD) || defined(SOLARIS) -#ifndef IPTOS_MINCOST -#define IPTOS_MINCOST 0x02 -#endif -#endif -#include <arpa/inet.h> -#include <net/if.h> -#include <netinet/in.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <unistd.h> -#include <stdlib.h> -#include <netdb.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <fcntl.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#include "asterisk/lock.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/module.h" -#include "asterisk/musiconhold.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/utils.h" -#include "asterisk/lock.h" -#include "asterisk/sched.h" -#include "asterisk/io.h" -#include "asterisk/rtp.h" -#include "asterisk/acl.h" -#include "asterisk/callerid.h" -#include "asterisk/cli.h" -#include "asterisk/dsp.h" -#include "asterisk/causes.h" -#include "asterisk/stringfields.h" -#include "asterisk/abstract_jb.h" -#include "asterisk/astobj.h" - -#ifdef __cplusplus -} -#endif - -#include "h323/chan_h323.h" - -receive_digit_cb on_receive_digit; -on_rtp_cb on_external_rtp_create; -start_rtp_cb on_start_rtp_channel; -setup_incoming_cb on_incoming_call; -setup_outbound_cb on_outgoing_call; -chan_ringing_cb on_chan_ringing; -con_established_cb on_connection_established; -clear_con_cb on_connection_cleared; -answer_call_cb on_answer_call; -progress_cb on_progress; -rfc2833_cb on_set_rfc2833_payload; -hangup_cb on_hangup; -setcapabilities_cb on_setcapabilities; -setpeercapabilities_cb on_setpeercapabilities; - -/* global debug flag */ -int h323debug; - -/*! Global jitterbuffer configuration - by default, jb is disabled */ -static struct ast_jb_conf default_jbconf = -{ - .flags = 0, - .max_size = -1, - .resync_threshold = -1, - .impl = "" -}; -static struct ast_jb_conf global_jbconf; - -/** Variables required by Asterisk */ -static const char tdesc[] = "The NuFone Network's Open H.323 Channel Driver"; -static const char config[] = "h323.conf"; -static char default_context[AST_MAX_CONTEXT] = "default"; -static struct sockaddr_in bindaddr; - -#define GLOBAL_CAPABILITY (AST_FORMAT_G723_1 | AST_FORMAT_GSM | AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_G729A | AST_FORMAT_H261) - -/** H.323 configuration values */ -static int h323_signalling_port = 1720; -static char gatekeeper[100]; -static int gatekeeper_disable = 1; -static int gatekeeper_discover = 0; -static int gkroute = 0; -/* Find user by alias (h.323 id) is default, alternative is the incomming call's source IP address*/ -static int userbyalias = 1; -static int acceptAnonymous = 1; -static int tos = 0; -static char secret[50]; -static unsigned int unique = 0; - -static call_options_t global_options; - -/** Private structure of a OpenH323 channel */ -struct oh323_pvt { - ast_mutex_t lock; /* Channel private lock */ - call_options_t options; /* Options to be used during call setup */ - int alreadygone; /* Whether or not we've already been destroyed by our peer */ - int needdestroy; /* if we need to be destroyed */ - call_details_t cd; /* Call details */ - struct ast_channel *owner; /* Who owns us */ - struct sockaddr_in sa; /* Our peer */ - struct sockaddr_in redirip; /* Where our RTP should be going if not to us */ - int nonCodecCapability; /* non-audio capability */ - int outgoing; /* Outgoing or incoming call? */ - char exten[AST_MAX_EXTENSION]; /* Requested extension */ - char context[AST_MAX_CONTEXT]; /* Context where to start */ - char accountcode[256]; /* Account code */ - char rdnis[80]; /* Referring DNIS, if available */ - int amaflags; /* AMA Flags */ - struct ast_rtp *rtp; /* RTP Session */ - struct ast_dsp *vad; /* Used for in-band DTMF detection */ - int nativeformats; /* Codec formats supported by a channel */ - int needhangup; /* Send hangup when Asterisk is ready */ - int hangupcause; /* Hangup cause from OpenH323 layer */ - int newstate; /* Pending state change */ - int newcontrol; /* Pending control to send */ - int newdigit; /* Pending DTMF digit to send */ - int newduration; /* Pending DTMF digit duration to send */ - int pref_codec; /* Preferred codec */ - int peercapability; /* Capabilities learned from peer */ - int jointcapability; /* Common capabilities for local and remote side */ - struct ast_codec_pref peer_prefs; /* Preferenced list of codecs which remote side supports */ - int dtmf_pt; /* Payload code used for RFC2833 messages */ - int curDTMF; /* DTMF tone being generated to Asterisk side */ - int DTMFsched; /* Scheduler descriptor for DTMF */ - int update_rtp_info; /* Configuration of fd's array is pending */ - int recvonly; /* Peer isn't wish to receive our voice stream */ - int txDtmfDigit; /* DTMF digit being to send to H.323 side */ - int noInbandDtmf; /* Inband DTMF processing by DSP isn't available */ - int connection_established; /* Call got CONNECT message */ - int got_progress; /* Call got PROGRESS message, pass inband audio */ - struct oh323_pvt *next; /* Next channel in list */ -} *iflist = NULL; - -static struct ast_user_list { - ASTOBJ_CONTAINER_COMPONENTS(struct oh323_user); -} userl; - -static struct ast_peer_list { - ASTOBJ_CONTAINER_COMPONENTS(struct oh323_peer); -} peerl; - -static struct ast_alias_list { - ASTOBJ_CONTAINER_COMPONENTS(struct oh323_alias); -} aliasl; - -/** Asterisk RTP stuff */ -static struct sched_context *sched; -static struct io_context *io; - -/** Protect the interface list (oh323_pvt) */ -AST_MUTEX_DEFINE_STATIC(iflock); - -/* Protect the monitoring thread, so only one process can kill or start it, and not - when it's doing something critical. */ -AST_MUTEX_DEFINE_STATIC(monlock); - -/* Protect the H.323 capabilities list, to avoid more than one channel to set the capabilities simultaneaously in the h323 stack. */ -AST_MUTEX_DEFINE_STATIC(caplock); - -/* Protect the reload process */ -AST_MUTEX_DEFINE_STATIC(h323_reload_lock); -static int h323_reloading = 0; - -/* This is the thread for the monitor which checks for input on the channels - which are not currently in use. */ -static pthread_t monitor_thread = AST_PTHREADT_NULL; -static int restart_monitor(void); -static int h323_do_reload(void); - -static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause); -static int oh323_digit_begin(struct ast_channel *c, char digit); -static int oh323_digit_end(struct ast_channel *c, char digit, unsigned int duration); -static int oh323_call(struct ast_channel *c, char *dest, int timeout); -static int oh323_hangup(struct ast_channel *c); -static int oh323_answer(struct ast_channel *c); -static struct ast_frame *oh323_read(struct ast_channel *c); -static int oh323_write(struct ast_channel *c, struct ast_frame *frame); -static int oh323_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen); -static int oh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); - -static const struct ast_channel_tech oh323_tech = { - .type = "H323", - .description = tdesc, - .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1), - .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER, - .requester = oh323_request, - .send_digit_begin = oh323_digit_begin, - .send_digit_end = oh323_digit_end, - .call = oh323_call, - .hangup = oh323_hangup, - .answer = oh323_answer, - .read = oh323_read, - .write = oh323_write, - .indicate = oh323_indicate, - .fixup = oh323_fixup, - /* disable, for now */ -#if 0 - .bridge = ast_rtp_bridge, -#endif -}; - -static const char* redirectingreason2str(int redirectingreason) -{ - switch (redirectingreason) { - case 0: - return "UNKNOWN"; - case 1: - return "BUSY"; - case 2: - return "NO_REPLY"; - case 0xF: - return "UNCONDITIONAL"; - default: - return "NOREDIRECT"; - } -} - -static void oh323_destroy_alias(struct oh323_alias *alias) -{ - if (h323debug) - ast_log(LOG_DEBUG, "Destroying alias '%s'\n", alias->name); - free(alias); -} - -static void oh323_destroy_user(struct oh323_user *user) -{ - if (h323debug) - ast_log(LOG_DEBUG, "Destroying user '%s'\n", user->name); - ast_free_ha(user->ha); - free(user); -} - -static void oh323_destroy_peer(struct oh323_peer *peer) -{ - if (h323debug) - ast_log(LOG_DEBUG, "Destroying peer '%s'\n", peer->name); - ast_free_ha(peer->ha); - free(peer); -} - -static int oh323_simulate_dtmf_end(const void *data) -{ - struct oh323_pvt *pvt = (struct oh323_pvt *)data; - - if (pvt) { - ast_mutex_lock(&pvt->lock); - /* Don't hold pvt lock while trying to lock the channel */ - while(pvt->owner && ast_channel_trylock(pvt->owner)) { - ast_mutex_unlock(&pvt->lock); - usleep(1); - ast_mutex_lock(&pvt->lock); - } - - if (pvt->owner) { - struct ast_frame f = { - .frametype = AST_FRAME_DTMF_END, - .subclass = pvt->curDTMF, - .samples = 0, - .src = "SIMULATE_DTMF_END", - }; - ast_queue_frame(pvt->owner, &f); - ast_channel_unlock(pvt->owner); - } - - pvt->DTMFsched = -1; - ast_mutex_unlock(&pvt->lock); - } - - return 0; -} - -/* Channel and private structures should be already locked */ -static void __oh323_update_info(struct ast_channel *c, struct oh323_pvt *pvt) -{ - if (c->nativeformats != pvt->nativeformats) { - if (h323debug) - ast_log(LOG_DEBUG, "Preparing %s for new native format\n", c->name); - c->nativeformats = pvt->nativeformats; - ast_set_read_format(c, c->readformat); - ast_set_write_format(c, c->writeformat); - } - if (pvt->needhangup) { - if (h323debug) - ast_log(LOG_DEBUG, "Process pending hangup for %s\n", c->name); - c->_softhangup |= AST_SOFTHANGUP_DEV; - c->hangupcause = pvt->hangupcause; - ast_queue_hangup(c); - pvt->needhangup = 0; - pvt->newstate = pvt->newcontrol = pvt->newdigit = pvt->DTMFsched = -1; - } - if (pvt->newstate >= 0) { - ast_setstate(c, pvt->newstate); - pvt->newstate = -1; - } - if (pvt->newcontrol >= 0) { - ast_queue_control(c, pvt->newcontrol); - pvt->newcontrol = -1; - } - if (pvt->newdigit >= 0) { - struct ast_frame f = { - .frametype = AST_FRAME_DTMF_END, - .subclass = pvt->newdigit, - .samples = pvt->newduration * 8, - .len = pvt->newduration, - .src = "UPDATE_INFO", - }; - if (pvt->newdigit == ' ') { /* signalUpdate message */ - f.subclass = pvt->curDTMF; - if (pvt->DTMFsched >= 0) { - AST_SCHED_DEL(sched, pvt->DTMFsched); - } - } else { /* Regular input or signal message */ - if (pvt->newduration) { /* This is a signal, signalUpdate follows */ - f.frametype = AST_FRAME_DTMF_BEGIN; - AST_SCHED_DEL(sched, pvt->DTMFsched); - pvt->DTMFsched = ast_sched_add(sched, pvt->newduration, oh323_simulate_dtmf_end, pvt); - if (h323debug) - ast_log(LOG_DTMF, "Scheduled DTMF END simulation for %d ms, id=%d\n", pvt->newduration, pvt->DTMFsched); - } - pvt->curDTMF = pvt->newdigit; - } - ast_queue_frame(c, &f); - pvt->newdigit = -1; - } - if (pvt->update_rtp_info > 0) { - if (pvt->rtp) { - ast_jb_configure(c, &global_jbconf); - c->fds[0] = ast_rtp_fd(pvt->rtp); - c->fds[1] = ast_rtcp_fd(pvt->rtp); - ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ - } - pvt->update_rtp_info = -1; - } -} - -/* Only channel structure should be locked */ -static void oh323_update_info(struct ast_channel *c) -{ - struct oh323_pvt *pvt = c->tech_pvt; - - if (pvt) { - ast_mutex_lock(&pvt->lock); - __oh323_update_info(c, pvt); - ast_mutex_unlock(&pvt->lock); - } -} - -static void cleanup_call_details(call_details_t *cd) -{ - if (cd->call_token) { - free(cd->call_token); - cd->call_token = NULL; - } - if (cd->call_source_aliases) { - free(cd->call_source_aliases); - cd->call_source_aliases = NULL; - } - if (cd->call_dest_alias) { - free(cd->call_dest_alias); - cd->call_dest_alias = NULL; - } - if (cd->call_source_name) { - free(cd->call_source_name); - cd->call_source_name = NULL; - } - if (cd->call_source_e164) { - free(cd->call_source_e164); - cd->call_source_e164 = NULL; - } - if (cd->call_dest_e164) { - free(cd->call_dest_e164); - cd->call_dest_e164 = NULL; - } - if (cd->sourceIp) { - free(cd->sourceIp); - cd->sourceIp = NULL; - } - if (cd->redirect_number) { - free(cd->redirect_number); - cd->redirect_number = NULL; - } -} - -static void __oh323_destroy(struct oh323_pvt *pvt) -{ - struct oh323_pvt *cur, *prev = NULL; - - AST_SCHED_DEL(sched, pvt->DTMFsched); - - if (pvt->rtp) { - ast_rtp_destroy(pvt->rtp); - } - - /* Free dsp used for in-band DTMF detection */ - if (pvt->vad) { - ast_dsp_free(pvt->vad); - } - cleanup_call_details(&pvt->cd); - - /* Unlink us from the owner if we have one */ - if (pvt->owner) { - ast_channel_lock(pvt->owner); - if (h323debug) - ast_log(LOG_DEBUG, "Detaching from %s\n", pvt->owner->name); - pvt->owner->tech_pvt = NULL; - ast_channel_unlock(pvt->owner); - } - cur = iflist; - while(cur) { - if (cur == pvt) { - if (prev) - prev->next = cur->next; - else - iflist = cur->next; - break; - } - prev = cur; - cur = cur->next; - } - if (!cur) { - ast_log(LOG_WARNING, "%p is not in list?!?! \n", cur); - } else { - ast_mutex_unlock(&pvt->lock); - ast_mutex_destroy(&pvt->lock); - free(pvt); - } -} - -static void oh323_destroy(struct oh323_pvt *pvt) -{ - if (h323debug) { - ast_log(LOG_DEBUG, "Destroying channel %s\n", (pvt->owner ? pvt->owner->name : "<unknown>")); - } - ast_mutex_lock(&iflock); - ast_mutex_lock(&pvt->lock); - __oh323_destroy(pvt); - ast_mutex_unlock(&iflock); -} - -static int oh323_digit_begin(struct ast_channel *c, char digit) -{ - struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt; - char *token; - - if (!pvt) { - ast_log(LOG_ERROR, "No private structure?! This is bad\n"); - return -1; - } - ast_mutex_lock(&pvt->lock); - if (pvt->rtp && (pvt->options.dtmfmode & H323_DTMF_RFC2833) && (pvt->dtmf_pt > 0)) { - /* out-of-band DTMF */ - if (h323debug) { - ast_log(LOG_DTMF, "Begin sending out-of-band digit %c on %s\n", digit, c->name); - } - ast_rtp_senddigit_begin(pvt->rtp, digit); - ast_mutex_unlock(&pvt->lock); - } else if (pvt->txDtmfDigit != digit) { - /* in-band DTMF */ - if (h323debug) { - ast_log(LOG_DTMF, "Begin sending inband digit %c on %s\n", digit, c->name); - } - pvt->txDtmfDigit = digit; - token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL; - ast_mutex_unlock(&pvt->lock); - h323_send_tone(token, digit); - if (token) { - free(token); - } - } else - ast_mutex_unlock(&pvt->lock); - oh323_update_info(c); - return 0; -} - -/** - * Send (play) the specified digit to the channel. - * - */ -static int oh323_digit_end(struct ast_channel *c, char digit, unsigned int duration) -{ - struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt; - char *token; - - if (!pvt) { - ast_log(LOG_ERROR, "No private structure?! This is bad\n"); - return -1; - } - ast_mutex_lock(&pvt->lock); - if (pvt->rtp && (pvt->options.dtmfmode & H323_DTMF_RFC2833) && (pvt->dtmf_pt > 0)) { - /* out-of-band DTMF */ - if (h323debug) { - ast_log(LOG_DTMF, "End sending out-of-band digit %c on %s, duration %d\n", digit, c->name, duration); - } - ast_rtp_senddigit_end(pvt->rtp, digit); - ast_mutex_unlock(&pvt->lock); - } else { - /* in-band DTMF */ - if (h323debug) { - ast_log(LOG_DTMF, "End sending inband digit %c on %s, duration %d\n", digit, c->name, duration); - } - pvt->txDtmfDigit = ' '; - token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL; - ast_mutex_unlock(&pvt->lock); - h323_send_tone(token, ' '); - if (token) { - free(token); - } - } - oh323_update_info(c); - return 0; -} - -/** - * Make a call over the specified channel to the specified - * destination. - * Returns -1 on error, 0 on success. - */ -static int oh323_call(struct ast_channel *c, char *dest, int timeout) -{ - int res = 0; - struct oh323_pvt *pvt = (struct oh323_pvt *)c->tech_pvt; - const char *addr; - char called_addr[1024]; - - if (h323debug) { - ast_log(LOG_DEBUG, "Calling to %s on %s\n", dest, c->name); - } - if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) { - ast_log(LOG_WARNING, "Line is already in use (%s)\n", c->name); - return -1; - } - ast_mutex_lock(&pvt->lock); - if (!gatekeeper_disable) { - if (ast_strlen_zero(pvt->exten)) { - ast_copy_string(called_addr, dest, sizeof(called_addr)); - } else { - snprintf(called_addr, sizeof(called_addr), "%s@%s", pvt->exten, dest); - } - } else { - res = htons(pvt->sa.sin_port); - addr = ast_inet_ntoa(pvt->sa.sin_addr); - if (ast_strlen_zero(pvt->exten)) { - snprintf(called_addr, sizeof(called_addr), "%s:%d", addr, res); - } else { - snprintf(called_addr, sizeof(called_addr), "%s@%s:%d", pvt->exten, addr, res); - } - } - /* make sure null terminated */ - called_addr[sizeof(called_addr) - 1] = '\0'; - - if (c->cid.cid_num) - ast_copy_string(pvt->options.cid_num, c->cid.cid_num, sizeof(pvt->options.cid_num)); - - if (c->cid.cid_name) - ast_copy_string(pvt->options.cid_name, c->cid.cid_name, sizeof(pvt->options.cid_name)); - - if (c->cid.cid_rdnis) { - ast_copy_string(pvt->options.cid_rdnis, c->cid.cid_rdnis, sizeof(pvt->options.cid_rdnis)); - } - - pvt->options.presentation = c->cid.cid_pres; - pvt->options.type_of_number = c->cid.cid_ton; - - if ((addr = pbx_builtin_getvar_helper(c, "PRIREDIRECTREASON"))) { - if (!strcasecmp(addr, "UNKNOWN")) - pvt->options.redirect_reason = 0; - else if (!strcasecmp(addr, "BUSY")) - pvt->options.redirect_reason = 1; - else if (!strcasecmp(addr, "NO_REPLY")) - pvt->options.redirect_reason = 2; - else if (!strcasecmp(addr, "UNCONDITIONAL")) - pvt->options.redirect_reason = 15; - else - pvt->options.redirect_reason = -1; - } else - pvt->options.redirect_reason = -1; - - pvt->options.transfer_capability = c->transfercapability; - - /* indicate that this is an outgoing call */ - pvt->outgoing = 1; - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Requested transfer capability: 0x%.2x - %s\n", c->transfercapability, ast_transfercapability2str(c->transfercapability)); - if (h323debug) - ast_log(LOG_DEBUG, "Placing outgoing call to %s, %d\n", called_addr, pvt->options.dtmfcodec); - ast_mutex_unlock(&pvt->lock); - res = h323_make_call(called_addr, &(pvt->cd), &pvt->options); - if (res) { - ast_log(LOG_NOTICE, "h323_make_call failed(%s)\n", c->name); - return -1; - } - oh323_update_info(c); - return 0; -} - -static int oh323_answer(struct ast_channel *c) -{ - int res; - struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt; - char *token; - - if (h323debug) - ast_log(LOG_DEBUG, "Answering on %s\n", c->name); - - ast_mutex_lock(&pvt->lock); - token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL; - ast_mutex_unlock(&pvt->lock); - res = h323_answering_call(token, 0); - if (token) - free(token); - - oh323_update_info(c); - if (c->_state != AST_STATE_UP) { - ast_setstate(c, AST_STATE_UP); - } - return res; -} - -static int oh323_hangup(struct ast_channel *c) -{ - struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt; - int q931cause = AST_CAUSE_NORMAL_CLEARING; - char *call_token; - - - if (h323debug) - ast_log(LOG_DEBUG, "Hanging up and scheduling destroy of call %s\n", c->name); - - if (!c->tech_pvt) { - ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); - return 0; - } - ast_mutex_lock(&pvt->lock); - /* Determine how to disconnect */ - if (pvt->owner != c) { - ast_log(LOG_WARNING, "Huh? We aren't the owner?\n"); - ast_mutex_unlock(&pvt->lock); - return 0; - } - - pvt->owner = NULL; - c->tech_pvt = NULL; - - if (c->hangupcause) { - q931cause = c->hangupcause; - } else { - const char *cause = pbx_builtin_getvar_helper(c, "DIALSTATUS"); - if (cause) { - if (!strcmp(cause, "CONGESTION")) { - q931cause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION; - } else if (!strcmp(cause, "BUSY")) { - q931cause = AST_CAUSE_USER_BUSY; - } else if (!strcmp(cause, "CHANISUNVAIL")) { - q931cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL; - } else if (!strcmp(cause, "NOANSWER")) { - q931cause = AST_CAUSE_NO_ANSWER; - } else if (!strcmp(cause, "CANCEL")) { - q931cause = AST_CAUSE_CALL_REJECTED; - } - } - } - - /* Start the process if it's not already started */ - if (!pvt->alreadygone && !pvt->hangupcause) { - call_token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL; - if (call_token) { - /* Release lock to eliminate deadlock */ - ast_mutex_unlock(&pvt->lock); - if (h323_clear_call(call_token, q931cause)) { - ast_log(LOG_WARNING, "ClearCall failed.\n"); - } - free(call_token); - ast_mutex_lock(&pvt->lock); - } - } - pvt->needdestroy = 1; - ast_mutex_unlock(&pvt->lock); - - /* Update usage counter */ - ast_module_unref(ast_module_info->self); - - return 0; -} - -static struct ast_frame *oh323_rtp_read(struct oh323_pvt *pvt) -{ - /* Retrieve audio/etc from channel. Assumes pvt->lock is already held. */ - struct ast_frame *f; - - /* Only apply it for the first packet, we just need the correct ip/port */ - if (pvt->options.nat) { - ast_rtp_setnat(pvt->rtp, pvt->options.nat); - pvt->options.nat = 0; - } - - f = ast_rtp_read(pvt->rtp); - /* Don't send RFC2833 if we're not supposed to */ - if (f && (f->frametype == AST_FRAME_DTMF) && !(pvt->options.dtmfmode & H323_DTMF_RFC2833)) { - return &ast_null_frame; - } - if (pvt->owner) { - /* We already hold the channel lock */ - if (f->frametype == AST_FRAME_VOICE) { - if (f->subclass != pvt->owner->nativeformats) { - /* Try to avoid deadlock */ - if (ast_channel_trylock(pvt->owner)) { - ast_log(LOG_NOTICE, "Format changed but channel is locked. Ignoring frame...\n"); - return &ast_null_frame; - } - if (h323debug) - ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass); - pvt->owner->nativeformats = f->subclass; - pvt->nativeformats = f->subclass; - ast_set_read_format(pvt->owner, pvt->owner->readformat); - ast_set_write_format(pvt->owner, pvt->owner->writeformat); - ast_channel_unlock(pvt->owner); - } - /* Do in-band DTMF detection */ - if ((pvt->options.dtmfmode & H323_DTMF_INBAND) && pvt->vad) { - if ((pvt->nativeformats & (AST_FORMAT_SLINEAR | AST_FORMAT_ALAW | AST_FORMAT_ULAW))) { - if (!ast_channel_trylock(pvt->owner)) { - f = ast_dsp_process(pvt->owner, pvt->vad, f); - ast_channel_unlock(pvt->owner); - } - else - ast_log(LOG_NOTICE, "Unable to process inband DTMF while channel is locked\n"); - } else if (pvt->nativeformats && !pvt->noInbandDtmf) { - ast_log(LOG_NOTICE, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_getformatname(f->subclass)); - pvt->noInbandDtmf = 1; - } - if (f &&(f->frametype == AST_FRAME_DTMF)) { - if (h323debug) - ast_log(LOG_DTMF, "Received in-band digit %c.\n", f->subclass); - } - } - } - } - return f; -} - -static struct ast_frame *oh323_read(struct ast_channel *c) -{ - struct ast_frame *fr; - struct oh323_pvt *pvt = (struct oh323_pvt *)c->tech_pvt; - ast_mutex_lock(&pvt->lock); - __oh323_update_info(c, pvt); - switch(c->fdno) { - case 0: - fr = oh323_rtp_read(pvt); - break; - case 1: - if (pvt->rtp) - fr = ast_rtcp_read(pvt->rtp); - else - fr = &ast_null_frame; - break; - default: - ast_log(LOG_ERROR, "Unable to handle fd %d on channel %s\n", c->fdno, c->name); - fr = &ast_null_frame; - break; - } - ast_mutex_unlock(&pvt->lock); - return fr; -} - -static int oh323_write(struct ast_channel *c, struct ast_frame *frame) -{ - struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt; - int res = 0; - if (frame->frametype != AST_FRAME_VOICE) { - if (frame->frametype == AST_FRAME_IMAGE) { - return 0; - } else { - ast_log(LOG_WARNING, "Can't send %d type frames with H323 write\n", frame->frametype); - return 0; - } - } else { - if (!(frame->subclass & c->nativeformats)) { - ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n", - frame->subclass, c->nativeformats, c->readformat, c->writeformat); - return 0; - } - } - if (pvt) { - ast_mutex_lock(&pvt->lock); - if (pvt->rtp && !pvt->recvonly) - res = ast_rtp_write(pvt->rtp, frame); - __oh323_update_info(c, pvt); - ast_mutex_unlock(&pvt->lock); - } - return res; -} - -static int oh323_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen) -{ - - struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt; - char *token = (char *)NULL; - int res = -1; - int got_progress; - - ast_mutex_lock(&pvt->lock); - token = (pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL); - got_progress = pvt->got_progress; - if (condition == AST_CONTROL_PROGRESS) - pvt->got_progress = 1; - else if ((condition == AST_CONTROL_BUSY) || (condition == AST_CONTROL_CONGESTION)) - pvt->alreadygone = 1; - ast_mutex_unlock(&pvt->lock); - - if (h323debug) - ast_log(LOG_DEBUG, "OH323: Indicating %d on %s\n", condition, token); - - switch(condition) { - case AST_CONTROL_RINGING: - if (c->_state == AST_STATE_RING || c->_state == AST_STATE_RINGING) { - h323_send_alerting(token); - res = (got_progress ? 0 : -1); /* Do not simulate any audio tones if we got PROGRESS message */ - } - break; - case AST_CONTROL_PROGRESS: - if (c->_state != AST_STATE_UP) { - /* Do not send PROGRESS message more than once */ - if (!got_progress) - h323_send_progress(token); - res = 0; - } - break; - case AST_CONTROL_BUSY: - if (c->_state != AST_STATE_UP) { - h323_answering_call(token, 1); - ast_softhangup_nolock(c, AST_SOFTHANGUP_DEV); - res = 0; - } - break; - case AST_CONTROL_CONGESTION: - if (c->_state != AST_STATE_UP) { - h323_answering_call(token, 1); - ast_softhangup_nolock(c, AST_SOFTHANGUP_DEV); - res = 0; - } - break; - case AST_CONTROL_HOLD: - ast_moh_start(c, data, NULL); - res = 0; - break; - case AST_CONTROL_UNHOLD: - ast_moh_stop(c); - res = 0; - break; - case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(pvt->rtp); - res = 0; - break; - case AST_CONTROL_PROCEEDING: - case -1: - break; - default: - ast_log(LOG_WARNING, "OH323: Don't know how to indicate condition %d on %s\n", condition, token); - break; - } - - if (h323debug) - ast_log(LOG_DEBUG, "OH323: Indicated %d on %s, res=%d\n", condition, token, res); - if (token) - free(token); - oh323_update_info(c); - - return res; -} - -static int oh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct oh323_pvt *pvt = (struct oh323_pvt *) newchan->tech_pvt; - - ast_mutex_lock(&pvt->lock); - if (pvt->owner != oldchan) { - ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, pvt->owner); - return -1; - } - pvt->owner = newchan; - ast_mutex_unlock(&pvt->lock); - return 0; -} - -static int __oh323_rtp_create(struct oh323_pvt *pvt) -{ - struct in_addr our_addr; - - if (pvt->rtp) - return 0; - - if (ast_find_ourip(&our_addr, bindaddr)) { - ast_mutex_unlock(&pvt->lock); - ast_log(LOG_ERROR, "Unable to locate local IP address for RTP stream\n"); - return -1; - } - pvt->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, our_addr); - if (!pvt->rtp) { - ast_mutex_unlock(&pvt->lock); - ast_log(LOG_WARNING, "Unable to create RTP session: %s\n", strerror(errno)); - return -1; - } - if (h323debug) - ast_log(LOG_DEBUG, "Created RTP channel\n"); - - ast_rtp_settos(pvt->rtp, tos); - - if (h323debug) - ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", pvt->options.nat); - ast_rtp_setnat(pvt->rtp, pvt->options.nat); - - if (pvt->dtmf_pt > 0) - ast_rtp_set_rtpmap_type(pvt->rtp, pvt->dtmf_pt, "audio", "telephone-event", 0); - - if (pvt->peercapability) - ast_rtp_codec_setpref(pvt->rtp, &pvt->peer_prefs); - - if (pvt->owner && !ast_channel_trylock(pvt->owner)) { - ast_jb_configure(pvt->owner, &global_jbconf); - pvt->owner->fds[0] = ast_rtp_fd(pvt->rtp); - pvt->owner->fds[1] = ast_rtcp_fd(pvt->rtp); - ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ - ast_channel_unlock(pvt->owner); - } else - pvt->update_rtp_info = 1; - - return 0; -} - -/* Private structure should be locked on a call */ -static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const char *host) -{ - struct ast_channel *ch; - char *cid_num, *cid_name; - int fmt; - - if (!ast_strlen_zero(pvt->options.cid_num)) - cid_num = pvt->options.cid_num; - else - cid_num = pvt->cd.call_source_e164; - - if (!ast_strlen_zero(pvt->options.cid_name)) - cid_name = pvt->options.cid_name; - else - cid_name = pvt->cd.call_source_name; - - /* Don't hold a oh323_pvt lock while we allocate a chanel */ - ast_mutex_unlock(&pvt->lock); - ch = ast_channel_alloc(1, state, cid_num, cid_name, pvt->accountcode, pvt->exten, pvt->context, pvt->amaflags, "H323/%s", host); - /* Update usage counter */ - ast_module_ref(ast_module_info->self); - ast_mutex_lock(&pvt->lock); - if (ch) { - ch->tech = &oh323_tech; - if (!(fmt = pvt->jointcapability) && !(fmt = pvt->options.capability)) - fmt = global_options.capability; - ch->nativeformats = ast_codec_choose(&pvt->options.prefs, fmt, 1)/* | (pvt->jointcapability & AST_FORMAT_VIDEO_MASK)*/; - pvt->nativeformats = ch->nativeformats; - fmt = ast_best_codec(ch->nativeformats); - ch->writeformat = fmt; - ch->rawwriteformat = fmt; - ch->readformat = fmt; - ch->rawreadformat = fmt; -#if 0 - ch->fds[0] = ast_rtp_fd(pvt->rtp); - ch->fds[1] = ast_rtcp_fd(pvt->rtp); -#endif -#ifdef VIDEO_SUPPORT - if (pvt->vrtp) { - ch->fds[2] = ast_rtp_fd(pvt->vrtp); - ch->fds[3] = ast_rtcp_fd(pvt->vrtp); - } -#endif -#ifdef T38_SUPPORT - if (pvt->udptl) { - ch->fds[4] = ast_udptl_fd(pvt->udptl); - } -#endif - if (state == AST_STATE_RING) { - ch->rings = 1; - } - /* Allocate dsp for in-band DTMF support */ - if (pvt->options.dtmfmode & H323_DTMF_INBAND) { - pvt->vad = ast_dsp_new(); - ast_dsp_set_features(pvt->vad, DSP_FEATURE_DTMF_DETECT); - } - /* Register channel functions. */ - ch->tech_pvt = pvt; - /* Set the owner of this channel */ - pvt->owner = ch; - - ast_copy_string(ch->context, pvt->context, sizeof(ch->context)); - ast_copy_string(ch->exten, pvt->exten, sizeof(ch->exten)); - ch->priority = 1; - if (!ast_strlen_zero(pvt->accountcode)) { - ast_string_field_set(ch, accountcode, pvt->accountcode); - } - if (pvt->amaflags) { - ch->amaflags = pvt->amaflags; - } - - /* Don't use ast_set_callerid() here because it will - * generate a needless NewCallerID event */ - ch->cid.cid_ani = ast_strdup(cid_num); - - if (pvt->cd.redirect_reason >= 0) { - ch->cid.cid_rdnis = ast_strdup(pvt->cd.redirect_number); - pbx_builtin_setvar_helper(ch, "PRIREDIRECTREASON", redirectingreason2str(pvt->cd.redirect_reason)); - } - ch->cid.cid_pres = pvt->cd.presentation; - ch->cid.cid_ton = pvt->cd.type_of_number; - - if (!ast_strlen_zero(pvt->exten) && strcmp(pvt->exten, "s")) { - ch->cid.cid_dnid = strdup(pvt->exten); - } - if (pvt->cd.transfer_capability >= 0) - ch->transfercapability = pvt->cd.transfer_capability; - if (state != AST_STATE_DOWN) { - if (ast_pbx_start(ch)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ch->name); - ast_hangup(ch); - ch = NULL; - } - } - } else { - ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); - } - return ch; -} - -static struct oh323_pvt *oh323_alloc(int callid) -{ - struct oh323_pvt *pvt; - - pvt = (struct oh323_pvt *) malloc(sizeof(struct oh323_pvt)); - if (!pvt) { - ast_log(LOG_ERROR, "Couldn't allocate private structure. This is bad\n"); - return NULL; - } - memset(pvt, 0, sizeof(struct oh323_pvt)); - pvt->cd.redirect_reason = -1; - pvt->cd.transfer_capability = -1; - /* Ensure the call token is allocated for outgoing call */ - if (!callid) { - if ((pvt->cd).call_token == NULL) { - (pvt->cd).call_token = (char *)malloc(128); - } - if (!pvt->cd.call_token) { - ast_log(LOG_ERROR, "Not enough memory to alocate call token\n"); - ast_rtp_destroy(pvt->rtp); - free(pvt); - return NULL; - } - memset((char *)(pvt->cd).call_token, 0, 128); - pvt->cd.call_reference = callid; - } - memcpy(&pvt->options, &global_options, sizeof(pvt->options)); - pvt->jointcapability = pvt->options.capability; - if (pvt->options.dtmfmode & H323_DTMF_RFC2833) { - pvt->nonCodecCapability |= AST_RTP_DTMF; - } else { - pvt->nonCodecCapability &= ~AST_RTP_DTMF; - } - ast_copy_string(pvt->context, default_context, sizeof(pvt->context)); - pvt->newstate = pvt->newcontrol = pvt->newdigit = pvt->update_rtp_info = pvt->DTMFsched = -1; - ast_mutex_init(&pvt->lock); - /* Add to interface list */ - ast_mutex_lock(&iflock); - pvt->next = iflist; - iflist = pvt; - ast_mutex_unlock(&iflock); - return pvt; -} - -static struct oh323_pvt *find_call_locked(int call_reference, const char *token) -{ - struct oh323_pvt *pvt; - - ast_mutex_lock(&iflock); - pvt = iflist; - while(pvt) { - if (!pvt->needdestroy && ((signed int)pvt->cd.call_reference == call_reference)) { - /* Found the call */ - if ((token != NULL) && (pvt->cd.call_token != NULL) && (!strcmp(pvt->cd.call_token, token))) { - ast_mutex_lock(&pvt->lock); - ast_mutex_unlock(&iflock); - return pvt; - } else if (token == NULL) { - ast_log(LOG_WARNING, "Call Token is NULL\n"); - ast_mutex_lock(&pvt->lock); - ast_mutex_unlock(&iflock); - return pvt; - } - } - pvt = pvt->next; - } - ast_mutex_unlock(&iflock); - return NULL; -} - -static int update_state(struct oh323_pvt *pvt, int state, int signal) -{ - if (!pvt) - return 0; - if (pvt->owner && !ast_channel_trylock(pvt->owner)) { - if (state >= 0) - ast_setstate(pvt->owner, state); - if (signal >= 0) - ast_queue_control(pvt->owner, signal); - ast_channel_unlock(pvt->owner); - return 1; - } - else { - if (state >= 0) - pvt->newstate = state; - if (signal >= 0) - pvt->newcontrol = signal; - return 0; - } -} - -static struct oh323_alias *build_alias(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime) -{ - struct oh323_alias *alias; - int found = 0; - - alias = ASTOBJ_CONTAINER_FIND_UNLINK_FULL(&aliasl, name, name, 0, 0, strcasecmp); - - if (alias) - found++; - else { - if (!(alias = (struct oh323_alias *)calloc(1, sizeof(*alias)))) - return NULL; - ASTOBJ_INIT(alias); - } - if (!found && name) - ast_copy_string(alias->name, name, sizeof(alias->name)); - for (; v || ((v = alt) && !(alt = NULL)); v = v->next) { - if (!strcasecmp(v->name, "e164")) { - ast_copy_string(alias->e164, v->value, sizeof(alias->e164)); - } else if (!strcasecmp(v->name, "prefix")) { - ast_copy_string(alias->prefix, v->value, sizeof(alias->prefix)); - } else if (!strcasecmp(v->name, "context")) { - ast_copy_string(alias->context, v->value, sizeof(alias->context)); - } else if (!strcasecmp(v->name, "secret")) { - ast_copy_string(alias->secret, v->value, sizeof(alias->secret)); - } else { - if (strcasecmp(v->value, "h323")) { - ast_log(LOG_WARNING, "Keyword %s does not make sense in type=h323\n", v->name); - } - } - } - ASTOBJ_UNMARK(alias); - return alias; -} - -static struct oh323_alias *realtime_alias(const char *alias) -{ - struct ast_variable *var, *tmp; - struct oh323_alias *a; - - var = ast_load_realtime("h323", "name", alias, NULL); - - if (!var) - return NULL; - - for (tmp = var; tmp; tmp = tmp->next) { - if (!strcasecmp(tmp->name, "type") && - !(!strcasecmp(tmp->value, "alias") || !strcasecmp(tmp->value, "h323"))) { - ast_variables_destroy(var); - return NULL; - } - } - - a = build_alias(alias, var, NULL, 1); - - ast_variables_destroy(var); - - return a; -} - -#define DEPRECATED(_v, _new_opt) \ - ast_log(LOG_WARNING, "Option %s found at line %d has beed deprecated. Use %s instead.\n", (_v)->name, (_v)->lineno, (_new_opt)) - -static int update_common_options(struct ast_variable *v, struct call_options *options) -{ - int tmp; - - if (!strcasecmp(v->name, "allow")) { - ast_parse_allow_disallow(&options->prefs, &options->capability, v->value, 1); - } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&options->prefs, &options->capability, v->value, 0); - } else if (!strcasecmp(v->name, "dtmfmode")) { - if (!strcasecmp(v->value, "inband")) { - options->dtmfmode = H323_DTMF_INBAND; - } else if (!strcasecmp(v->value, "rfc2833")) { - options->dtmfmode = H323_DTMF_RFC2833; - } else { - ast_log(LOG_WARNING, "Unknown dtmf mode '%s', using rfc2833\n", v->value); - options->dtmfmode = H323_DTMF_RFC2833; - } - } else if (!strcasecmp(v->name, "dtmfcodec")) { - tmp = atoi(v->value); - if (tmp < 96) - ast_log(LOG_WARNING, "Invalid %s value %s at line %d\n", v->name, v->value, v->lineno); - else - options->dtmfcodec = tmp; - } else if (!strcasecmp(v->name, "bridge")) { - options->bridge = ast_true(v->value); - } else if (!strcasecmp(v->name, "nat")) { - options->nat = ast_true(v->value); - } else if (!strcasecmp(v->name, "noFastStart")) { - DEPRECATED(v, "fastStart"); - options->fastStart = !ast_true(v->value); - } else if (!strcasecmp(v->name, "fastStart")) { - options->fastStart = ast_true(v->value); - } else if (!strcasecmp(v->name, "noH245Tunneling")) { - DEPRECATED(v, "h245Tunneling"); - options->h245Tunneling = !ast_true(v->value); - } else if (!strcasecmp(v->name, "h245Tunneling")) { - options->h245Tunneling = ast_true(v->value); - } else if (!strcasecmp(v->name, "noSilenceSuppression")) { - DEPRECATED(v, "silenceSuppression"); - options->silenceSuppression = !ast_true(v->value); - } else if (!strcasecmp(v->name, "silenceSuppression")) { - options->silenceSuppression = ast_true(v->value); - } else if (!strcasecmp(v->name, "progress_setup")) { - tmp = atoi(v->value); - if ((tmp != 0) && (tmp != 1) && (tmp != 3) && (tmp != 8)) { - ast_log(LOG_WARNING, "Invalid value %s for %s at line %d, assuming 0\n", v->value, v->name, v->lineno); - tmp = 0; - } - options->progress_setup = tmp; - } else if (!strcasecmp(v->name, "progress_alert")) { - tmp = atoi(v->value); - if ((tmp != 0) && (tmp != 1) && (tmp != 8)) { - ast_log(LOG_WARNING, "Invalid value %s for %s at line %d, assuming 0\n", v->value, v->name, v->lineno); - tmp = 0; - } - options->progress_alert = tmp; - } else if (!strcasecmp(v->name, "progress_audio")) { - options->progress_audio = ast_true(v->value); - } else if (!strcasecmp(v->name, "callerid")) { - ast_callerid_split(v->value, options->cid_name, sizeof(options->cid_name), options->cid_num, sizeof(options->cid_num)); - } else if (!strcasecmp(v->name, "fullname")) { - ast_copy_string(options->cid_name, v->value, sizeof(options->cid_name)); - } else if (!strcasecmp(v->name, "cid_number")) { - ast_copy_string(options->cid_num, v->value, sizeof(options->cid_num)); - } else if (!strcasecmp(v->name, "tunneling")) { - if (!strcasecmp(v->value, "none")) - options->tunnelOptions = 0; - else if (!strcasecmp(v->value, "cisco")) - options->tunnelOptions |= H323_TUNNEL_CISCO; - else if (!strcasecmp(v->value, "qsig")) - options->tunnelOptions |= H323_TUNNEL_QSIG; - else - ast_log(LOG_WARNING, "Invalid value %s for %s at line %d\n", v->value, v->name, v->lineno); - } else - return 1; - - return 0; -} -#undef DEPRECATED - -static struct oh323_user *build_user(char *name, struct ast_variable *v, struct ast_variable *alt, int realtime) -{ - struct oh323_user *user; - struct ast_ha *oldha; - int found = 0; - int format; - - user = ASTOBJ_CONTAINER_FIND_UNLINK_FULL(&userl, name, name, 0, 0, strcmp); - - if (user) - found++; - else { - if (!(user = (struct oh323_user *)calloc(1, sizeof(*user)))) - return NULL; - ASTOBJ_INIT(user); - } - oldha = user->ha; - user->ha = (struct ast_ha *)NULL; - memcpy(&user->options, &global_options, sizeof(user->options)); - /* Set default context */ - ast_copy_string(user->context, default_context, sizeof(user->context)); - if (user && !found) - ast_copy_string(user->name, name, sizeof(user->name)); - -#if 0 /* XXX Port channel variables functionality from chan_sip XXX */ - if (user->chanvars) { - ast_variables_destroy(user->chanvars); - user->chanvars = NULL; - } -#endif - - for (; v || ((v = alt) && !(alt = NULL)); v = v->next) { - if (!update_common_options(v, &user->options)) - continue; - if (!strcasecmp(v->name, "context")) { - ast_copy_string(user->context, v->value, sizeof(user->context)); - } else if (!strcasecmp(v->name, "secret")) { - ast_copy_string(user->secret, v->value, sizeof(user->secret)); - } else if (!strcasecmp(v->name, "accountcode")) { - ast_copy_string(user->accountcode, v->value, sizeof(user->accountcode)); - } else if (!strcasecmp(v->name, "host")) { - if (!strcasecmp(v->value, "dynamic")) { - ast_log(LOG_ERROR, "A dynamic host on a type=user does not make any sense\n"); - ASTOBJ_UNREF(user, oh323_destroy_user); - return NULL; - } else if (ast_get_ip(&user->addr, v->value)) { - ASTOBJ_UNREF(user, oh323_destroy_user); - return NULL; - } - /* Let us know we need to use ip authentication */ - user->host = 1; - } else if (!strcasecmp(v->name, "amaflags")) { - format = ast_cdr_amaflags2int(v->value); - if (format < 0) { - ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno); - } else { - user->amaflags = format; - } - } else if (!strcasecmp(v->name, "permit") || - !strcasecmp(v->name, "deny")) { - user->ha = ast_append_ha(v->name, v->value, user->ha); - } - } - ASTOBJ_UNMARK(user); - ast_free_ha(oldha); - return user; -} - -static struct oh323_user *realtime_user(const call_details_t *cd) -{ - struct ast_variable *var, *tmp; - struct oh323_user *user; - char *username; - - if (userbyalias) - var = ast_load_realtime("h323", "name", username = cd->call_source_aliases, NULL); - else { - username = (char *)NULL; - var = ast_load_realtime("h323", "host", cd->sourceIp, NULL); - } - - if (!var) - return NULL; - - for (tmp = var; tmp; tmp = tmp->next) { - if (!strcasecmp(tmp->name, "type") && - !(!strcasecmp(tmp->value, "user") || !strcasecmp(tmp->value, "friend"))) { - ast_variables_destroy(var); - return NULL; - } else if (!username && !strcasecmp(tmp->name, "name")) - username = tmp->value; - } - - if (!username) { - ast_log(LOG_WARNING, "Cannot determine user name for IP address %s\n", cd->sourceIp); - ast_variables_destroy(var); - return NULL; - } - - user = build_user(username, var, NULL, 1); - - ast_variables_destroy(var); - - return user; -} - -static struct oh323_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime) -{ - struct oh323_peer *peer; - struct ast_ha *oldha; - int found = 0; - - peer = ASTOBJ_CONTAINER_FIND_UNLINK_FULL(&peerl, name, name, 0, 0, strcmp); - - if (peer) - found++; - else { - if (!(peer = (struct oh323_peer*)calloc(1, sizeof(*peer)))) - return NULL; - ASTOBJ_INIT(peer); - } - oldha = peer->ha; - peer->ha = NULL; - memcpy(&peer->options, &global_options, sizeof(peer->options)); - peer->addr.sin_port = htons(h323_signalling_port); - peer->addr.sin_family = AF_INET; - if (!found && name) - ast_copy_string(peer->name, name, sizeof(peer->name)); - -#if 0 /* XXX Port channel variables functionality from chan_sip XXX */ - if (peer->chanvars) { - ast_variables_destroy(peer->chanvars); - peer->chanvars = NULL; - } -#endif - /* Default settings for mailbox */ - peer->mailbox[0] = '\0'; - - for (; v || ((v = alt) && !(alt = NULL)); v = v->next) { - if (!update_common_options(v, &peer->options)) - continue; - if (!strcasecmp(v->name, "host")) { - if (!strcasecmp(v->value, "dynamic")) { - ast_log(LOG_ERROR, "Dynamic host configuration not implemented.\n"); - ASTOBJ_UNREF(peer, oh323_destroy_peer); - return NULL; - } - if (ast_get_ip(&peer->addr, v->value)) { - ast_log(LOG_ERROR, "Could not determine IP for %s\n", v->value); - ASTOBJ_UNREF(peer, oh323_destroy_peer); - return NULL; - } - } else if (!strcasecmp(v->name, "port")) { - peer->addr.sin_port = htons(atoi(v->value)); - } else if (!strcasecmp(v->name, "permit") || - !strcasecmp(v->name, "deny")) { - peer->ha = ast_append_ha(v->name, v->value, peer->ha); - } else if (!strcasecmp(v->name, "mailbox")) { - ast_copy_string(peer->mailbox, v->value, sizeof(peer->mailbox)); - } else if (!strcasecmp(v->name, "hasvoicemail")) { - if (ast_true(v->value) && ast_strlen_zero(peer->mailbox)) { - ast_copy_string(peer->mailbox, name, sizeof(peer->mailbox)); - } - } - } - ASTOBJ_UNMARK(peer); - ast_free_ha(oldha); - return peer; -} - -static struct oh323_peer *realtime_peer(const char *peername, struct sockaddr_in *sin) -{ - struct oh323_peer *peer; - struct ast_variable *var; - struct ast_variable *tmp; - const char *addr; - - /* First check on peer name */ - if (peername) - var = ast_load_realtime("h323", "name", peername, addr = NULL); - else if (sin) /* Then check on IP address for dynamic peers */ - var = ast_load_realtime("h323", "host", addr = ast_inet_ntoa(sin->sin_addr), NULL); - else - return NULL; - - if (!var) - return NULL; - - for (tmp = var; tmp; tmp = tmp->next) { - /* If this is type=user, then skip this object. */ - if (!strcasecmp(tmp->name, "type") && - !(!strcasecmp(tmp->value, "peer") || !strcasecmp(tmp->value, "friend"))) { - ast_variables_destroy(var); - return NULL; - } else if (!peername && !strcasecmp(tmp->name, "name")) { - peername = tmp->value; - } - } - - if (!peername) { /* Did not find peer in realtime */ - ast_log(LOG_WARNING, "Cannot determine peer name for IP address %s\n", addr); - ast_variables_destroy(var); - return NULL; - } - - /* Peer found in realtime, now build it in memory */ - peer = build_peer(peername, var, NULL, 1); - - ast_variables_destroy(var); - - return peer; -} - -static int oh323_addrcmp_str(struct in_addr inaddr, char *addr) -{ - return strcmp(ast_inet_ntoa(inaddr), addr); -} - -static struct oh323_user *find_user(const call_details_t *cd, int realtime) -{ - struct oh323_user *u; - - if (userbyalias) - u = ASTOBJ_CONTAINER_FIND(&userl, cd->call_source_aliases); - else - u = ASTOBJ_CONTAINER_FIND_FULL(&userl, cd->sourceIp, addr.sin_addr, 0, 0, oh323_addrcmp_str); - - if (!u && realtime) - u = realtime_user(cd); - - if (!u && h323debug) - ast_log(LOG_DEBUG, "Could not find user by name %s or address %s\n", cd->call_source_aliases, cd->sourceIp); - - return u; -} - -static int oh323_addrcmp(struct sockaddr_in addr, struct sockaddr_in *sin) -{ - int res; - - if (!sin) - res = -1; - else - res = inaddrcmp(&addr , sin); - - return res; -} - -static struct oh323_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime) -{ - struct oh323_peer *p; - - if (peer) - p = ASTOBJ_CONTAINER_FIND(&peerl, peer); - else - p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, addr, 0, 0, oh323_addrcmp); - - if (!p && realtime) - p = realtime_peer(peer, sin); - - if (!p && h323debug) - ast_log(LOG_DEBUG, "Could not find peer by name %s or address %s\n", (peer ? peer : "<NONE>"), (sin ? ast_inet_ntoa(sin->sin_addr) : "<NONE>")); - - return p; -} - -static int create_addr(struct oh323_pvt *pvt, char *opeer) -{ - struct hostent *hp; - struct ast_hostent ahp; - struct oh323_peer *p; - int portno; - int found = 0; - char *port; - char *hostn; - char peer[256] = ""; - - ast_copy_string(peer, opeer, sizeof(peer)); - port = strchr(peer, ':'); - if (port) { - *port = '\0'; - port++; - } - pvt->sa.sin_family = AF_INET; - p = find_peer(peer, NULL, 1); - if (p) { - found++; - memcpy(&pvt->options, &p->options, sizeof(pvt->options)); - pvt->jointcapability = pvt->options.capability; - if (pvt->options.dtmfmode) { - if (pvt->options.dtmfmode & H323_DTMF_RFC2833) { - pvt->nonCodecCapability |= AST_RTP_DTMF; - } else { - pvt->nonCodecCapability &= ~AST_RTP_DTMF; - } - } - if (p->addr.sin_addr.s_addr) { - pvt->sa.sin_addr = p->addr.sin_addr; - pvt->sa.sin_port = p->addr.sin_port; - } - ASTOBJ_UNREF(p, oh323_destroy_peer); - } - if (!p && !found) { - hostn = peer; - if (port) { - portno = atoi(port); - } else { - portno = h323_signalling_port; - } - hp = ast_gethostbyname(hostn, &ahp); - if (hp) { - memcpy(&pvt->sa.sin_addr, hp->h_addr, sizeof(pvt->sa.sin_addr)); - pvt->sa.sin_port = htons(portno); - /* Look peer by address */ - p = find_peer(NULL, &pvt->sa, 1); - memcpy(&pvt->options, (p ? &p->options : &global_options), sizeof(pvt->options)); - pvt->jointcapability = pvt->options.capability; - if (p) { - ASTOBJ_UNREF(p, oh323_destroy_peer); - } - if (pvt->options.dtmfmode) { - if (pvt->options.dtmfmode & H323_DTMF_RFC2833) { - pvt->nonCodecCapability |= AST_RTP_DTMF; - } else { - pvt->nonCodecCapability &= ~AST_RTP_DTMF; - } - } - return 0; - } else { - ast_log(LOG_WARNING, "No such host: %s\n", peer); - return -1; - } - } else if (!found) { - return -1; - } else { - return 0; - } -} -static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause) -{ - int oldformat; - struct oh323_pvt *pvt; - struct ast_channel *tmpc = NULL; - char *dest = (char *)data; - char *ext, *host; - char *h323id = NULL; - char tmp[256], tmp1[256]; - - if (h323debug) - ast_log(LOG_DEBUG, "type=%s, format=%d, data=%s.\n", type, format, (char *)data); - - pvt = oh323_alloc(0); - if (!pvt) { - ast_log(LOG_WARNING, "Unable to build pvt data for '%s'\n", (char *)data); - return NULL; - } - oldformat = format; - format &= ((AST_FORMAT_MAX_AUDIO << 1) - 1); - if (!format) { - ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format); - oh323_destroy(pvt); - if (cause) - *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION; - return NULL; - } - ast_copy_string(tmp, dest, sizeof(tmp)); - host = strchr(tmp, '@'); - if (host) { - *host = '\0'; - host++; - ext = tmp; - } else { - ext = strrchr(tmp, '/'); - if (ext) - *ext++ = '\0'; - host = tmp; - } - strtok_r(host, "/", &(h323id)); - if (!ast_strlen_zero(h323id)) { - h323_set_id(h323id); - } - if (ext) { - ast_copy_string(pvt->exten, ext, sizeof(pvt->exten)); - } - if (h323debug) - ast_log(LOG_DEBUG, "Extension: %s Host: %s\n", pvt->exten, host); - - if (gatekeeper_disable) { - if (create_addr(pvt, host)) { - oh323_destroy(pvt); - if (cause) - *cause = AST_CAUSE_DESTINATION_OUT_OF_ORDER; - return NULL; - } - } - else { - memcpy(&pvt->options, &global_options, sizeof(pvt->options)); - pvt->jointcapability = pvt->options.capability; - if (pvt->options.dtmfmode) { - if (pvt->options.dtmfmode & H323_DTMF_RFC2833) { - pvt->nonCodecCapability |= AST_RTP_DTMF; - } else { - pvt->nonCodecCapability &= ~AST_RTP_DTMF; - } - } - } - - ast_mutex_lock(&caplock); - /* Generate unique channel identifier */ - snprintf(tmp1, sizeof(tmp1)-1, "%s-%u", host, ++unique); - tmp1[sizeof(tmp1)-1] = '\0'; - ast_mutex_unlock(&caplock); - - ast_mutex_lock(&pvt->lock); - tmpc = __oh323_new(pvt, AST_STATE_DOWN, tmp1); - ast_mutex_unlock(&pvt->lock); - if (!tmpc) { - oh323_destroy(pvt); - if (cause) - *cause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; - } - ast_update_use_count(); - restart_monitor(); - return tmpc; -} - -/** Find a call by alias */ -static struct oh323_alias *find_alias(const char *source_aliases, int realtime) -{ - struct oh323_alias *a; - - a = ASTOBJ_CONTAINER_FIND(&aliasl, source_aliases); - - if (!a && realtime) - a = realtime_alias(source_aliases); - - return a; -} - -/** - * Callback for sending digits from H.323 up to asterisk - * - */ -static int receive_digit(unsigned call_reference, char digit, const char *token, int duration) -{ - struct oh323_pvt *pvt; - int res; - - pvt = find_call_locked(call_reference, token); - if (!pvt) { - ast_log(LOG_ERROR, "Received digit '%c' (%u ms) for call %s without private structure\n", digit, duration, token); - return -1; - } - if (h323debug) - ast_log(LOG_DTMF, "Received %s digit '%c' (%u ms) for call %s\n", (digit == ' ' ? "update for" : "new"), (digit == ' ' ? pvt->curDTMF : digit), duration, token); - - if (pvt->owner && !ast_channel_trylock(pvt->owner)) { - if (digit == '!') - res = ast_queue_control(pvt->owner, AST_CONTROL_FLASH); - else { - struct ast_frame f = { - .frametype = AST_FRAME_DTMF_END, - .subclass = digit, - .samples = duration * 8, - .len = duration, - .src = "SEND_DIGIT", - }; - if (digit == ' ') { /* signalUpdate message */ - f.subclass = pvt->curDTMF; - AST_SCHED_DEL(sched, pvt->DTMFsched); - } else { /* Regular input or signal message */ - if (pvt->DTMFsched >= 0) { - /* We still don't send DTMF END from previous event, send it now */ - AST_SCHED_DEL(sched, pvt->DTMFsched); - f.subclass = pvt->curDTMF; - f.samples = f.len = 0; - ast_queue_frame(pvt->owner, &f); - /* Restore values */ - f.subclass = digit; - f.samples = duration * 8; - f.len = duration; - } - if (duration) { /* This is a signal, signalUpdate follows */ - f.frametype = AST_FRAME_DTMF_BEGIN; - pvt->DTMFsched = ast_sched_add(sched, duration, oh323_simulate_dtmf_end, pvt); - if (h323debug) - ast_log(LOG_DTMF, "Scheduled DTMF END simulation for %d ms, id=%d\n", duration, pvt->DTMFsched); - } - pvt->curDTMF = digit; - } - res = ast_queue_frame(pvt->owner, &f); - } - ast_channel_unlock(pvt->owner); - } else { - if (digit == '!') - pvt->newcontrol = AST_CONTROL_FLASH; - else { - pvt->newduration = duration; - pvt->newdigit = digit; - } - res = 0; - } - ast_mutex_unlock(&pvt->lock); - return res; -} - -/** - * Callback function used to inform the H.323 stack of the local rtp ip/port details - * - * Returns the local RTP information - */ -static struct rtp_info *external_rtp_create(unsigned call_reference, const char * token) -{ - struct oh323_pvt *pvt; - struct sockaddr_in us; - struct rtp_info *info; - - info = (struct rtp_info *)malloc(sizeof(struct rtp_info)); - if (!info) { - ast_log(LOG_ERROR, "Unable to allocated info structure, this is very bad\n"); - return NULL; - } - pvt = find_call_locked(call_reference, token); - if (!pvt) { - free(info); - ast_log(LOG_ERROR, "Unable to find call %s(%d)\n", token, call_reference); - return NULL; - } - if (!pvt->rtp) - __oh323_rtp_create(pvt); - if (!pvt->rtp) { - ast_mutex_unlock(&pvt->lock); - free(info); - ast_log(LOG_ERROR, "No RTP stream is available for call %s (%d)", token, call_reference); - return NULL; - } - /* figure out our local RTP port and tell the H.323 stack about it */ - ast_rtp_get_us(pvt->rtp, &us); - ast_mutex_unlock(&pvt->lock); - - ast_copy_string(info->addr, ast_inet_ntoa(us.sin_addr), sizeof(info->addr)); - info->port = ntohs(us.sin_port); - if (h323debug) - ast_log(LOG_DEBUG, "Sending RTP 'US' %s:%d\n", info->addr, info->port); - return info; -} - -/** - * Definition taken from rtp.c for rtpPayloadType because we need it here. - */ -struct rtpPayloadType { - int isAstFormat; /* whether the following code is an AST_FORMAT */ - int code; -}; - -/** - * Call-back function passing remote ip/port information from H.323 to asterisk - * - * Returns nothing - */ -static void setup_rtp_connection(unsigned call_reference, const char *remoteIp, int remotePort, const char *token, int pt) -{ - struct oh323_pvt *pvt; - struct sockaddr_in them; - struct rtpPayloadType rtptype; - int nativeformats_changed; - enum { NEED_NONE, NEED_HOLD, NEED_UNHOLD } rtp_change = NEED_NONE; - - if (h323debug) - ast_log(LOG_DEBUG, "Setting up RTP connection for %s\n", token); - - /* Find the call or allocate a private structure if call not found */ - pvt = find_call_locked(call_reference, token); - if (!pvt) { - ast_log(LOG_ERROR, "Something is wrong: rtp\n"); - return; - } - if (pvt->alreadygone) { - ast_mutex_unlock(&pvt->lock); - return; - } - - if (!pvt->rtp) - __oh323_rtp_create(pvt); - - them.sin_family = AF_INET; - /* only works for IPv4 */ - them.sin_addr.s_addr = inet_addr(remoteIp); - them.sin_port = htons(remotePort); - - if (them.sin_addr.s_addr) { - ast_rtp_set_peer(pvt->rtp, &them); - if (pvt->recvonly) { - pvt->recvonly = 0; - rtp_change = NEED_UNHOLD; - } - } else { - ast_rtp_stop(pvt->rtp); - if (!pvt->recvonly) { - pvt->recvonly = 1; - rtp_change = NEED_HOLD; - } - } - - /* Change native format to reflect information taken from OLC/OLCAck */ - nativeformats_changed = 0; - if (pt != 128 && pvt->rtp) { /* Payload type is invalid, so try to use previously decided */ - rtptype = ast_rtp_lookup_pt(pvt->rtp, pt); - if (h323debug) - ast_log(LOG_DEBUG, "Native format is set to %d from %d by RTP payload type %d\n", rtptype.code, pvt->nativeformats, pt); - if (pvt->nativeformats != rtptype.code) { - pvt->nativeformats = rtptype.code; - nativeformats_changed = 1; - } - } else if (h323debug) - ast_log(LOG_NOTICE, "Payload type is unknown, formats isn't changed\n"); - - /* Don't try to lock the channel if nothing changed */ - if (nativeformats_changed || pvt->options.progress_audio || (rtp_change != NEED_NONE)) { - if (pvt->owner && !ast_channel_trylock(pvt->owner)) { - /* Re-build translation path only if native format(s) has been changed */ - if (pvt->owner->nativeformats != pvt->nativeformats) { - if (h323debug) - ast_log(LOG_DEBUG, "Native format changed to %d from %d, read format is %d, write format is %d\n", pvt->nativeformats, pvt->owner->nativeformats, pvt->owner->readformat, pvt->owner->writeformat); - pvt->owner->nativeformats = pvt->nativeformats; - ast_set_read_format(pvt->owner, pvt->owner->readformat); - ast_set_write_format(pvt->owner, pvt->owner->writeformat); - } - if (pvt->options.progress_audio) - ast_queue_control(pvt->owner, AST_CONTROL_PROGRESS); - switch (rtp_change) { - case NEED_HOLD: - ast_queue_control(pvt->owner, AST_CONTROL_HOLD); - break; - case NEED_UNHOLD: - ast_queue_control(pvt->owner, AST_CONTROL_UNHOLD); - break; - default: - break; - } - ast_channel_unlock(pvt->owner); - } - else { - if (pvt->options.progress_audio) - pvt->newcontrol = AST_CONTROL_PROGRESS; - else if (rtp_change == NEED_HOLD) - pvt->newcontrol = AST_CONTROL_HOLD; - else if (rtp_change == NEED_UNHOLD) - pvt->newcontrol = AST_CONTROL_UNHOLD; - if (h323debug) - ast_log(LOG_DEBUG, "RTP connection preparation for %s is pending...\n", token); - } - } - ast_mutex_unlock(&pvt->lock); - - if (h323debug) - ast_log(LOG_DEBUG, "RTP connection prepared for %s\n", token); - - return; -} - -/** - * Call-back function to signal asterisk that the channel has been answered - * Returns nothing - */ -static void connection_made(unsigned call_reference, const char *token) -{ - struct oh323_pvt *pvt; - - if (h323debug) - ast_log(LOG_DEBUG, "Call %s answered\n", token); - - pvt = find_call_locked(call_reference, token); - if (!pvt) { - ast_log(LOG_ERROR, "Something is wrong: connection\n"); - return; - } - - /* Inform asterisk about remote party connected only on outgoing calls */ - if (!pvt->outgoing) { - ast_mutex_unlock(&pvt->lock); - return; - } - /* Do not send ANSWER message more than once */ - if (!pvt->connection_established) { - pvt->connection_established = 1; - update_state(pvt, -1, AST_CONTROL_ANSWER); - } - ast_mutex_unlock(&pvt->lock); - return; -} - -static int progress(unsigned call_reference, const char *token, int inband) -{ - struct oh323_pvt *pvt; - - if (h323debug) - ast_log(LOG_DEBUG, "Received ALERT/PROGRESS message for %s tones\n", (inband ? "inband" : "self-generated")); - - pvt = find_call_locked(call_reference, token); - if (!pvt) { - ast_log(LOG_ERROR, "Private structure not found in progress.\n"); - return -1; - } - if (!pvt->owner) { - ast_mutex_unlock(&pvt->lock); - ast_log(LOG_ERROR, "No Asterisk channel associated with private structure.\n"); - return -1; - } - update_state(pvt, -1, (inband ? AST_CONTROL_PROGRESS : AST_CONTROL_RINGING)); - ast_mutex_unlock(&pvt->lock); - - return 0; -} - -/** - * Call-back function for incoming calls - * - * Returns 1 on success - */ -static call_options_t *setup_incoming_call(call_details_t *cd) -{ - struct oh323_pvt *pvt; - struct oh323_user *user = NULL; - struct oh323_alias *alias = NULL; - - if (h323debug) - ast_log(LOG_DEBUG, "Setting up incoming call for %s\n", cd->call_token); - - /* allocate the call*/ - pvt = oh323_alloc(cd->call_reference); - - if (!pvt) { - ast_log(LOG_ERROR, "Unable to allocate private structure, this is bad.\n"); - cleanup_call_details(cd); - return NULL; - } - - /* Populate the call details in the private structure */ - memcpy(&pvt->cd, cd, sizeof(pvt->cd)); - memcpy(&pvt->options, &global_options, sizeof(pvt->options)); - pvt->jointcapability = pvt->options.capability; - - if (h323debug) { - ast_verbose(VERBOSE_PREFIX_3 "Setting up Call\n"); - ast_verbose(VERBOSE_PREFIX_3 " \tCall token: [%s]\n", pvt->cd.call_token); - ast_verbose(VERBOSE_PREFIX_3 " \tCalling party name: [%s]\n", pvt->cd.call_source_name); - ast_verbose(VERBOSE_PREFIX_3 " \tCalling party number: [%s]\n", pvt->cd.call_source_e164); - ast_verbose(VERBOSE_PREFIX_3 " \tCalled party name: [%s]\n", pvt->cd.call_dest_alias); - ast_verbose(VERBOSE_PREFIX_3 " \tCalled party number: [%s]\n", pvt->cd.call_dest_e164); - if (pvt->cd.redirect_reason >= 0) - ast_verbose(VERBOSE_PREFIX_3 " \tRedirecting party number: [%s] (reason %d)\n", pvt->cd.redirect_number, pvt->cd.redirect_reason); - ast_verbose(VERBOSE_PREFIX_3 " \tCalling party IP: [%s]\n", pvt->cd.sourceIp); - } - - /* Decide if we are allowing Gatekeeper routed calls*/ - if ((!strcasecmp(cd->sourceIp, gatekeeper)) && (gkroute == -1) && !gatekeeper_disable) { - if (!ast_strlen_zero(cd->call_dest_e164)) { - ast_copy_string(pvt->exten, cd->call_dest_e164, sizeof(pvt->exten)); - ast_copy_string(pvt->context, default_context, sizeof(pvt->context)); - } else { - alias = find_alias(cd->call_dest_alias, 1); - if (!alias) { - ast_log(LOG_ERROR, "Call for %s rejected, alias not found\n", cd->call_dest_alias); - oh323_destroy(pvt); - return NULL; - } - ast_copy_string(pvt->exten, alias->name, sizeof(pvt->exten)); - ast_copy_string(pvt->context, alias->context, sizeof(pvt->context)); - } - } else { - /* Either this call is not from the Gatekeeper - or we are not allowing gk routed calls */ - user = find_user(cd, 1); - if (!user) { - if (!acceptAnonymous) { - ast_log(LOG_NOTICE, "Anonymous call from '%s@%s' rejected\n", pvt->cd.call_source_aliases, pvt->cd.sourceIp); - oh323_destroy(pvt); - return NULL; - } - if (ast_strlen_zero(default_context)) { - ast_log(LOG_ERROR, "Call from '%s@%s' rejected due to no default context\n", pvt->cd.call_source_aliases, pvt->cd.sourceIp); - oh323_destroy(pvt); - return NULL; - } - ast_copy_string(pvt->context, default_context, sizeof(pvt->context)); - if (!ast_strlen_zero(pvt->cd.call_dest_e164)) { - ast_copy_string(pvt->exten, cd->call_dest_e164, sizeof(pvt->exten)); - } else { - ast_copy_string(pvt->exten, cd->call_dest_alias, sizeof(pvt->exten)); - } - if (h323debug) - ast_log(LOG_DEBUG, "Sending %s@%s to context [%s] extension %s\n", cd->call_source_aliases, cd->sourceIp, pvt->context, pvt->exten); - } else { - if (user->host) { - if (strcasecmp(cd->sourceIp, ast_inet_ntoa(user->addr.sin_addr))) { - if (ast_strlen_zero(user->context)) { - if (ast_strlen_zero(default_context)) { - ast_log(LOG_ERROR, "Call from '%s' rejected due to non-matching IP address (%s) and no default context\n", user->name, cd->sourceIp); - oh323_destroy(pvt); - ASTOBJ_UNREF(user, oh323_destroy_user); - return NULL; - } - ast_copy_string(pvt->context, default_context, sizeof(pvt->context)); - } else { - ast_copy_string(pvt->context, user->context, sizeof(pvt->context)); - } - pvt->exten[0] = 'i'; - pvt->exten[1] = '\0'; - ast_log(LOG_ERROR, "Call from '%s' rejected due to non-matching IP address (%s)s\n", user->name, cd->sourceIp); - oh323_destroy(pvt); - ASTOBJ_UNREF(user, oh323_destroy_user); - return NULL; /* XXX: Hmmm... Why to setup context if we drop connection immediately??? */ - } - } - ast_copy_string(pvt->context, user->context, sizeof(pvt->context)); - memcpy(&pvt->options, &user->options, sizeof(pvt->options)); - pvt->jointcapability = pvt->options.capability; - if (!ast_strlen_zero(pvt->cd.call_dest_e164)) { - ast_copy_string(pvt->exten, cd->call_dest_e164, sizeof(pvt->exten)); - } else { - ast_copy_string(pvt->exten, cd->call_dest_alias, sizeof(pvt->exten)); - } - if (!ast_strlen_zero(user->accountcode)) { - ast_copy_string(pvt->accountcode, user->accountcode, sizeof(pvt->accountcode)); - } - if (user->amaflags) { - pvt->amaflags = user->amaflags; - } - ASTOBJ_UNREF(user, oh323_destroy_user); - } - } - return &pvt->options; -} - -/** - * Call-back function to start PBX when OpenH323 ready to serve incoming call - * - * Returns 1 on success - */ -static int answer_call(unsigned call_reference, const char *token) -{ - struct oh323_pvt *pvt; - struct ast_channel *c = NULL; - enum {ext_original, ext_s, ext_i, ext_notexists} try_exten; - char tmp_exten[sizeof(pvt->exten)]; - - if (h323debug) - ast_log(LOG_DEBUG, "Preparing Asterisk to answer for %s\n", token); - - /* Find the call or allocate a private structure if call not found */ - pvt = find_call_locked(call_reference, token); - if (!pvt) { - ast_log(LOG_ERROR, "Something is wrong: answer_call\n"); - return 0; - } - /* Check if requested extension@context pair exists in the dialplan */ - ast_copy_string(tmp_exten, pvt->exten, sizeof(tmp_exten)); - - /* Try to find best extension in specified context */ - if ((tmp_exten[0] != '\0') && (tmp_exten[1] == '\0')) { - if (tmp_exten[0] == 's') - try_exten = ext_s; - else if (tmp_exten[0] == 'i') - try_exten = ext_i; - else - try_exten = ext_original; - } else - try_exten = ext_original; - do { - if (ast_exists_extension(NULL, pvt->context, tmp_exten, 1, NULL)) - break; - switch (try_exten) { - case ext_original: - tmp_exten[0] = 's'; - tmp_exten[1] = '\0'; - try_exten = ext_s; - break; - case ext_s: - tmp_exten[0] = 'i'; - try_exten = ext_i; - break; - case ext_i: - try_exten = ext_notexists; - break; - default: - break; - } - } while (try_exten != ext_notexists); - - /* Drop the call if we don't have <exten>, s and i extensions */ - if (try_exten == ext_notexists) { - ast_log(LOG_NOTICE, "Dropping call because extensions '%s', 's' and 'i' doesn't exists in context [%s]\n", pvt->exten, pvt->context); - ast_mutex_unlock(&pvt->lock); - h323_clear_call(token, AST_CAUSE_UNALLOCATED); - return 0; - } else if ((try_exten != ext_original) && (strcmp(pvt->exten, tmp_exten) != 0)) { - if (h323debug) - ast_log(LOG_DEBUG, "Going to extension %s@%s because %s@%s isn't exists\n", tmp_exten, pvt->context, pvt->exten, pvt->context); - ast_copy_string(pvt->exten, tmp_exten, sizeof(pvt->exten)); - } - - /* allocate a channel and tell asterisk about it */ - c = __oh323_new(pvt, AST_STATE_RINGING, pvt->cd.call_token); - - /* And release when done */ - ast_mutex_unlock(&pvt->lock); - if (!c) { - ast_log(LOG_ERROR, "Couldn't create channel. This is bad\n"); - return 0; - } - return 1; -} - -/** - * Call-back function to establish an outgoing H.323 call - * - * Returns 1 on success - */ -static int setup_outgoing_call(call_details_t *cd) -{ - /* Use argument here or free it immediately */ - cleanup_call_details(cd); - - return 1; -} - -/** - * Call-back function to signal asterisk that the channel is ringing - * Returns nothing - */ -static void chan_ringing(unsigned call_reference, const char *token) -{ - struct oh323_pvt *pvt; - - if (h323debug) - ast_log(LOG_DEBUG, "Ringing on %s\n", token); - - pvt = find_call_locked(call_reference, token); - if (!pvt) { - ast_log(LOG_ERROR, "Something is wrong: ringing\n"); - return; - } - if (!pvt->owner) { - ast_mutex_unlock(&pvt->lock); - ast_log(LOG_ERROR, "Channel has no owner\n"); - return; - } - update_state(pvt, AST_STATE_RINGING, AST_CONTROL_RINGING); - ast_mutex_unlock(&pvt->lock); - return; -} - -/** - * Call-back function to cleanup communication - * Returns nothing, - */ -static void cleanup_connection(unsigned call_reference, const char *call_token) -{ - struct oh323_pvt *pvt; - - if (h323debug) - ast_log(LOG_DEBUG, "Cleaning connection to %s\n", call_token); - - while (1) { - pvt = find_call_locked(call_reference, call_token); - if (!pvt) { - if (h323debug) - ast_log(LOG_DEBUG, "No connection for %s\n", call_token); - return; - } - if (!pvt->owner || !ast_channel_trylock(pvt->owner)) - break; -#if 1 -#ifdef DEBUG_THREADS - ast_log(LOG_NOTICE, "Avoiding H.323 destory deadlock on %s, locked at %ld/%d by %s (%s:%d)\n", call_token, pvt->owner->lock.thread[0], pvt->owner->lock.reentrancy, pvt->owner->lock.func[0], pvt->owner->lock.file[0], pvt->owner->lock.lineno[0]); -#else - ast_log(LOG_NOTICE, "Avoiding H.323 destory deadlock on %s\n", call_token); -#endif -#endif - ast_mutex_unlock(&pvt->lock); - usleep(1); - } - if (pvt->rtp) { - /* Immediately stop RTP */ - ast_rtp_destroy(pvt->rtp); - pvt->rtp = NULL; - } - /* Free dsp used for in-band DTMF detection */ - if (pvt->vad) { - ast_dsp_free(pvt->vad); - pvt->vad = NULL; - } - cleanup_call_details(&pvt->cd); - pvt->alreadygone = 1; - /* Send hangup */ - if (pvt->owner) { - pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV; - ast_queue_hangup(pvt->owner); - ast_channel_unlock(pvt->owner); - } - ast_mutex_unlock(&pvt->lock); - if (h323debug) - ast_log(LOG_DEBUG, "Connection to %s cleaned\n", call_token); - return; -} - -static void hangup_connection(unsigned int call_reference, const char *token, int cause) -{ - struct oh323_pvt *pvt; - - if (h323debug) { - ast_log(LOG_DEBUG, "Hanging up connection to %s with cause %d\n", token, cause); - } - - pvt = find_call_locked(call_reference, token); - if (!pvt) { - if (h323debug) { - ast_log(LOG_DEBUG, "Connection to %s already cleared\n", token); - } - return; - } - if (pvt->owner && !ast_channel_trylock(pvt->owner)) { - pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV; - pvt->owner->hangupcause = pvt->hangupcause = cause; - ast_queue_hangup(pvt->owner); - ast_channel_unlock(pvt->owner); - } - else { - pvt->needhangup = 1; - pvt->hangupcause = cause; - if (h323debug) - ast_log(LOG_DEBUG, "Hangup for %s is pending\n", token); - } - ast_mutex_unlock(&pvt->lock); -} - -static void set_dtmf_payload(unsigned call_reference, const char *token, int payload) -{ - struct oh323_pvt *pvt; - - if (h323debug) - ast_log(LOG_DEBUG, "Setting DTMF payload to %d on %s\n", payload, token); - - pvt = find_call_locked(call_reference, token); - if (!pvt) { - return; - } - if (pvt->rtp) { - ast_rtp_set_rtpmap_type(pvt->rtp, payload, "audio", "telephone-event", 0); - } - pvt->dtmf_pt = payload; - ast_mutex_unlock(&pvt->lock); - if (h323debug) - ast_log(LOG_DEBUG, "DTMF payload on %s set to %d\n", token, payload); -} - -static void set_peer_capabilities(unsigned call_reference, const char *token, int capabilities, struct ast_codec_pref *prefs) -{ - struct oh323_pvt *pvt; - - if (h323debug) - ast_log(LOG_DEBUG, "Got remote capabilities from connection %s\n", token); - - pvt = find_call_locked(call_reference, token); - if (!pvt) - return; - pvt->peercapability = capabilities; - pvt->jointcapability = pvt->options.capability & capabilities; - if (prefs) { - memcpy(&pvt->peer_prefs, prefs, sizeof(pvt->peer_prefs)); - if (h323debug) { - int i; - for (i = 0; i < 32; ++i) { - if (!prefs->order[i]) - break; - ast_log(LOG_DEBUG, "prefs[%d]=%s:%d\n", i, (prefs->order[i] ? ast_getformatname(1 << (prefs->order[i]-1)) : "<none>"), prefs->framing[i]); - } - } - if (pvt->rtp) - ast_rtp_codec_setpref(pvt->rtp, &pvt->peer_prefs); - } - ast_mutex_unlock(&pvt->lock); -} - -static void set_local_capabilities(unsigned call_reference, const char *token) -{ - struct oh323_pvt *pvt; - int capability, dtmfmode, pref_codec; - struct ast_codec_pref prefs; - - if (h323debug) - ast_log(LOG_DEBUG, "Setting capabilities for connection %s\n", token); - - pvt = find_call_locked(call_reference, token); - if (!pvt) - return; - capability = (pvt->jointcapability) ? pvt->jointcapability : pvt->options.capability; - dtmfmode = pvt->options.dtmfmode; - prefs = pvt->options.prefs; - pref_codec = pvt->pref_codec; - ast_mutex_unlock(&pvt->lock); - h323_set_capabilities(token, capability, dtmfmode, &prefs, pref_codec); - - if (h323debug) - ast_log(LOG_DEBUG, "Capabilities for connection %s is set\n", token); -} - -static void *do_monitor(void *data) -{ - int res; - int reloading; - struct oh323_pvt *oh323 = NULL; - - for(;;) { - /* Check for a reload request */ - ast_mutex_lock(&h323_reload_lock); - reloading = h323_reloading; - h323_reloading = 0; - ast_mutex_unlock(&h323_reload_lock); - if (reloading) { - if (option_verbose > 0) { - ast_verbose(VERBOSE_PREFIX_1 "Reloading H.323\n"); - } - h323_do_reload(); - } - /* Check for interfaces needing to be killed */ - if (!ast_mutex_trylock(&iflock)) { -#if 1 - do { - for (oh323 = iflist; oh323; oh323 = oh323->next) { - if (!ast_mutex_trylock(&oh323->lock)) { - if (oh323->needdestroy) { - __oh323_destroy(oh323); - break; - } - ast_mutex_unlock(&oh323->lock); - } - } - } while (/*oh323*/ 0); -#else -restartsearch: - oh323 = iflist; - while(oh323) { - if (!ast_mutex_trylock(&oh323->lock)) { - if (oh323->needdestroy) { - __oh323_destroy(oh323); - goto restartsearch; - } - ast_mutex_unlock(&oh323->lock); - oh323 = oh323->next; - } - } -#endif - ast_mutex_unlock(&iflock); - } else - oh323 = (struct oh323_pvt *)1; /* Force fast loop */ - pthread_testcancel(); - /* Wait for sched or io */ - res = ast_sched_wait(sched); - if ((res < 0) || (res > 1000)) { - res = 1000; - } - /* Do not wait if some channel(s) is destroyed, probably, more available too */ - if (oh323) - res = 1; - res = ast_io_wait(io, res); - pthread_testcancel(); - ast_mutex_lock(&monlock); - if (res >= 0) { - ast_sched_runq(sched); - } - ast_mutex_unlock(&monlock); - } - /* Never reached */ - return NULL; -} - -static int restart_monitor(void) -{ - /* If we're supposed to be stopped -- stay stopped */ - if (ast_mutex_lock(&monlock)) { - ast_log(LOG_WARNING, "Unable to lock monitor\n"); - return -1; - } - if (monitor_thread == AST_PTHREADT_STOP) { - ast_mutex_unlock(&monlock); - return 0; - } - if (monitor_thread == pthread_self()) { - ast_mutex_unlock(&monlock); - ast_log(LOG_WARNING, "Cannot kill myself\n"); - return -1; - } - if (monitor_thread && (monitor_thread != AST_PTHREADT_NULL)) { - /* Wake up the thread */ - pthread_kill(monitor_thread, SIGURG); - } else { - /* Start a new monitor */ - if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) { - monitor_thread = AST_PTHREADT_NULL; - ast_mutex_unlock(&monlock); - ast_log(LOG_ERROR, "Unable to start monitor thread.\n"); - return -1; - } - } - ast_mutex_unlock(&monlock); - return 0; -} - -static int h323_do_trace(int fd, int argc, char *argv[]) -{ - if (argc != 4) { - return RESULT_SHOWUSAGE; - } - h323_debug(1, atoi(argv[3])); - ast_cli(fd, "H.323 trace set to level %s\n", argv[2]); - return RESULT_SUCCESS; -} - -static int h323_no_trace(int fd, int argc, char *argv[]) -{ - if (argc < 3 || argc > 4) { - return RESULT_SHOWUSAGE; - } - h323_debug(0,0); - ast_cli(fd, "H.323 trace disabled\n"); - return RESULT_SUCCESS; -} - -static int h323_do_debug(int fd, int argc, char *argv[]) -{ - if (argc < 2 || argc > 3) { - return RESULT_SHOWUSAGE; - } - h323debug = 1; - ast_cli(fd, "H.323 debug enabled\n"); - return RESULT_SUCCESS; -} - -static int h323_no_debug(int fd, int argc, char *argv[]) -{ - if (argc < 3 || argc > 4) { - return RESULT_SHOWUSAGE; - } - h323debug = 0; - ast_cli(fd, "H.323 debug disabled\n"); - return RESULT_SUCCESS; -} - -static int h323_gk_cycle(int fd, int argc, char *argv[]) -{ - if (argc != 3) { - return RESULT_SHOWUSAGE; - } - h323_gk_urq(); - - /* Possibly register with a GK */ - if (!gatekeeper_disable) { - if (h323_set_gk(gatekeeper_discover, gatekeeper, secret)) { - ast_log(LOG_ERROR, "Gatekeeper registration failed.\n"); - } - } - return RESULT_SUCCESS; -} - -static int h323_ep_hangup(int fd, int argc, char *argv[]) -{ - if (argc != 3) { - return RESULT_SHOWUSAGE; - } - if (h323_soft_hangup(argv[2])) { - ast_verbose(VERBOSE_PREFIX_3 "Hangup succeeded on %s\n", argv[2]); - } else { - ast_verbose(VERBOSE_PREFIX_3 "Hangup failed for %s\n", argv[2]); - } - return RESULT_SUCCESS; -} - -static int h323_tokens_show(int fd, int argc, char *argv[]) -{ - if (argc != 3) { - return RESULT_SHOWUSAGE; - } - h323_show_tokens(); - return RESULT_SUCCESS; -} - -static char trace_usage[] = -"Usage: h.323 trace <level num>\n" -" Enables H.323 stack tracing for debugging purposes\n"; - -static char no_trace_usage[] = -"Usage: h.323 trace off\n" -" Disables H.323 stack tracing for debugging purposes\n"; - -static char debug_usage[] = -"Usage: h.323 debug\n" -" Enables H.323 debug output\n"; - -static char no_debug_usage[] = -"Usage: h.323 debug off\n" -" Disables H.323 debug output\n"; - -static char show_cycle_usage[] = -"Usage: h.323 gk cycle\n" -" Manually re-register with the Gatekeper (Currently Disabled)\n"; - -static char show_hangup_usage[] = -"Usage: h.323 hangup <token>\n" -" Manually try to hang up call identified by <token>\n"; - -static char show_tokens_usage[] = -"Usage: h.323 show tokens\n" -" Print out all active call tokens\n"; - -static char h323_reload_usage[] = -"Usage: h323 reload\n" -" Reloads H.323 configuration from h323.conf\n"; - -static struct ast_cli_entry cli_h323_no_trace_deprecated = { - { "h.323", "no", "trace", NULL }, - h323_no_trace, "Disable H.323 Stack Tracing", - no_trace_usage }; - -static struct ast_cli_entry cli_h323_no_debug_deprecated = { - { "h.323", "no", "debug", NULL }, - h323_no_debug, "Disable H.323 debug", - no_debug_usage }; - -static struct ast_cli_entry cli_h323_debug_deprecated = { - { "h.323", "debug", NULL }, - h323_do_debug, "Enable H.323 debug", - debug_usage }; - -static struct ast_cli_entry cli_h323_trace_deprecated = { - { "h.323", "trace", NULL }, - h323_do_trace, "Enable H.323 Stack Tracing", - trace_usage }; - -static struct ast_cli_entry cli_h323_gk_cycle_deprecated = { - { "h.323", "gk", "cycle", NULL }, - h323_gk_cycle, "Manually re-register with the Gatekeper", - show_cycle_usage }; - -static struct ast_cli_entry cli_h323[] = { - { { "h323", "set", "trace", NULL }, - h323_do_trace, "Enable H.323 Stack Tracing", - trace_usage, NULL, &cli_h323_trace_deprecated }, - - { { "h323", "set", "trace", "off", NULL }, - h323_no_trace, "Disable H.323 Stack Tracing", - no_trace_usage, NULL, &cli_h323_no_trace_deprecated }, - - { { "h323", "set", "debug", NULL }, - h323_do_debug, "Enable H.323 debug", - debug_usage, NULL, &cli_h323_debug_deprecated }, - - { { "h323", "set", "debug", "off", NULL }, - h323_no_debug, "Disable H.323 debug", - no_debug_usage, NULL, &cli_h323_no_debug_deprecated }, - - { { "h323", "cycle", "gk", NULL }, - h323_gk_cycle, "Manually re-register with the Gatekeper", - show_cycle_usage, NULL, &cli_h323_gk_cycle_deprecated }, - - { { "h323", "hangup", NULL }, - h323_ep_hangup, "Manually try to hang up a call", - show_hangup_usage }, - - { { "h323", "show", "tokens", NULL }, - h323_tokens_show, "Show all active call tokens", - show_tokens_usage }, -}; - -static void delete_users(void) -{ - int pruned = 0; - - /* Delete all users */ - ASTOBJ_CONTAINER_WRLOCK(&userl); - ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do { - ASTOBJ_RDLOCK(iterator); - ASTOBJ_MARK(iterator); - ++pruned; - ASTOBJ_UNLOCK(iterator); - } while (0) ); - if (pruned) { - ASTOBJ_CONTAINER_PRUNE_MARKED(&userl, oh323_destroy_user); - } - ASTOBJ_CONTAINER_UNLOCK(&userl); - - ASTOBJ_CONTAINER_WRLOCK(&peerl); - ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do { - ASTOBJ_RDLOCK(iterator); - ASTOBJ_MARK(iterator); - ASTOBJ_UNLOCK(iterator); - } while (0) ); - ASTOBJ_CONTAINER_UNLOCK(&peerl); -} - -static void delete_aliases(void) -{ - int pruned = 0; - - /* Delete all aliases */ - ASTOBJ_CONTAINER_WRLOCK(&aliasl); - ASTOBJ_CONTAINER_TRAVERSE(&aliasl, 1, do { - ASTOBJ_RDLOCK(iterator); - ASTOBJ_MARK(iterator); - ++pruned; - ASTOBJ_UNLOCK(iterator); - } while (0) ); - if (pruned) { - ASTOBJ_CONTAINER_PRUNE_MARKED(&aliasl, oh323_destroy_alias); - } - ASTOBJ_CONTAINER_UNLOCK(&aliasl); -} - -static void prune_peers(void) -{ - /* Prune peers who still are supposed to be deleted */ - ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl, oh323_destroy_peer); -} - -static int reload_config(int is_reload) -{ - int format; - struct ast_config *cfg, *ucfg; - struct ast_variable *v; - struct oh323_peer *peer = NULL; - struct oh323_user *user = NULL; - struct oh323_alias *alias = NULL; - struct ast_hostent ahp; struct hostent *hp; - char *cat; - const char *utype; - int is_user, is_peer, is_alias; - char _gatekeeper[100]; - int gk_discover, gk_disable, gk_changed; - - cfg = ast_config_load(config); - - /* We *must* have a config file otherwise stop immediately */ - if (!cfg) { - ast_log(LOG_NOTICE, "Unable to load config %s, H.323 disabled\n", config); - return 1; - } - - if (is_reload) { - delete_users(); - delete_aliases(); - prune_peers(); - } - - /* fire up the H.323 Endpoint */ - if (!h323_end_point_exist()) { - h323_end_point_create(); - } - ast_copy_string(_gatekeeper, gatekeeper, sizeof(_gatekeeper)); - gk_discover = gatekeeper_discover; - gk_disable = gatekeeper_disable; - memset(&bindaddr, 0, sizeof(bindaddr)); - memset(&global_options, 0, sizeof(global_options)); - global_options.fastStart = 1; - global_options.h245Tunneling = 1; - global_options.dtmfcodec = 101; - global_options.dtmfmode = H323_DTMF_RFC2833; - global_options.capability = GLOBAL_CAPABILITY; - global_options.bridge = 1; /* Do native bridging by default */ - strcpy(default_context, "default"); - h323_signalling_port = 1720; - gatekeeper_disable = 1; - gatekeeper_discover = 0; - gkroute = 0; - userbyalias = 1; - acceptAnonymous = 1; - tos = 0; - - /* Copy the default jb config over global_jbconf */ - memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); - - /* Load configuration from users.conf */ - ucfg = ast_config_load("users.conf"); - if (ucfg) { - struct ast_variable *gen; - int genhas_h323; - const char *has_h323; - - genhas_h323 = ast_true(ast_variable_retrieve(ucfg, "general", "hash323")); - gen = ast_variable_browse(ucfg, "general"); - for (cat = ast_category_browse(ucfg, NULL); cat; cat = ast_category_browse(ucfg, cat)) { - if (strcasecmp(cat, "general")) { - has_h323 = ast_variable_retrieve(ucfg, cat, "hash323"); - if (ast_true(has_h323) || (!has_h323 && genhas_h323)) { - user = build_user(cat, gen, ast_variable_browse(ucfg, cat), 0); - if (user) { - ASTOBJ_CONTAINER_LINK(&userl, user); - ASTOBJ_UNREF(user, oh323_destroy_user); - } - peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0); - if (peer) { - ASTOBJ_CONTAINER_LINK(&peerl, peer); - ASTOBJ_UNREF(peer, oh323_destroy_peer); - } - } - } - } - ast_config_destroy(ucfg); - } - - for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { - /* handle jb conf */ - if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) - continue; - /* Create the interface list */ - if (!strcasecmp(v->name, "port")) { - h323_signalling_port = (int)strtol(v->value, NULL, 10); - } else if (!strcasecmp(v->name, "bindaddr")) { - if (!(hp = ast_gethostbyname(v->value, &ahp))) { - ast_log(LOG_WARNING, "Invalid address: %s\n", v->value); - } else { - memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr)); - } - } else if (!strcasecmp(v->name, "tos")) { - if (sscanf(v->value, "%d", &format)) { - tos = format & 0xff; - } else if (!strcasecmp(v->value, "lowdelay")) { - tos = IPTOS_LOWDELAY; - } else if (!strcasecmp(v->value, "throughput")) { - tos = IPTOS_THROUGHPUT; - } else if (!strcasecmp(v->value, "reliability")) { - tos = IPTOS_RELIABILITY; - } else if (!strcasecmp(v->value, "mincost")) { - tos = IPTOS_MINCOST; - } else if (!strcasecmp(v->value, "none")) { - tos = 0; - } else { - ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno); - } - } else if (!strcasecmp(v->name, "gatekeeper")) { - if (!strcasecmp(v->value, "DISABLE")) { - gatekeeper_disable = 1; - } else if (!strcasecmp(v->value, "DISCOVER")) { - gatekeeper_disable = 0; - gatekeeper_discover = 1; - } else { - gatekeeper_disable = 0; - ast_copy_string(gatekeeper, v->value, sizeof(gatekeeper)); - } - } else if (!strcasecmp(v->name, "secret")) { - ast_copy_string(secret, v->value, sizeof(secret)); - } else if (!strcasecmp(v->name, "AllowGKRouted")) { - gkroute = ast_true(v->value); - } else if (!strcasecmp(v->name, "context")) { - ast_copy_string(default_context, v->value, sizeof(default_context)); - ast_verbose(VERBOSE_PREFIX_2 "Setting default context to %s\n", default_context); - } else if (!strcasecmp(v->name, "UserByAlias")) { - userbyalias = ast_true(v->value); - } else if (!strcasecmp(v->name, "AcceptAnonymous")) { - acceptAnonymous = ast_true(v->value); - } else if (!update_common_options(v, &global_options)) { - /* dummy */ - } - } - - for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) { - if (strcasecmp(cat, "general")) { - utype = ast_variable_retrieve(cfg, cat, "type"); - if (utype) { - is_user = is_peer = is_alias = 0; - if (!strcasecmp(utype, "user")) - is_user = 1; - else if (!strcasecmp(utype, "peer")) - is_peer = 1; - else if (!strcasecmp(utype, "friend")) - is_user = is_peer = 1; - else if (!strcasecmp(utype, "h323") || !strcasecmp(utype, "alias")) - is_alias = 1; - else { - ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config); - continue; - } - if (is_user) { - user = build_user(cat, ast_variable_browse(cfg, cat), NULL, 0); - if (user) { - ASTOBJ_CONTAINER_LINK(&userl, user); - ASTOBJ_UNREF(user, oh323_destroy_user); - } - } - if (is_peer) { - peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0); - if (peer) { - ASTOBJ_CONTAINER_LINK(&peerl, peer); - ASTOBJ_UNREF(peer, oh323_destroy_peer); - } - } - if (is_alias) { - alias = build_alias(cat, ast_variable_browse(cfg, cat), NULL, 0); - if (alias) { - ASTOBJ_CONTAINER_LINK(&aliasl, alias); - ASTOBJ_UNREF(alias, oh323_destroy_alias); - } - } - } else { - ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat); - } - } - } - ast_config_destroy(cfg); - - /* Register our H.323 aliases if any*/ - ASTOBJ_CONTAINER_WRLOCK(&aliasl); - ASTOBJ_CONTAINER_TRAVERSE(&aliasl, 1, do { - ASTOBJ_RDLOCK(iterator); - if (h323_set_alias(iterator)) { - ast_log(LOG_ERROR, "Alias %s rejected by endpoint\n", alias->name); - ASTOBJ_UNLOCK(iterator); - continue; - } - ASTOBJ_UNLOCK(iterator); - } while (0) ); - ASTOBJ_CONTAINER_UNLOCK(&aliasl); - - /* Don't touch GK if nothing changed because URQ will drop all existing calls */ - gk_changed = 0; - if (gatekeeper_disable != gk_disable) - gk_changed = is_reload; - else if(!gatekeeper_disable && (gatekeeper_discover != gk_discover)) - gk_changed = is_reload; - else if(!gatekeeper_disable && (strncmp(_gatekeeper, gatekeeper, sizeof(_gatekeeper)) != 0)) - gk_changed = is_reload; - if (gk_changed) { - if(!gk_disable) - h323_gk_urq(); - if (!gatekeeper_disable) { - if (h323_set_gk(gatekeeper_discover, gatekeeper, secret)) { - ast_log(LOG_ERROR, "Gatekeeper registration failed.\n"); - gatekeeper_disable = 1; - } - } - } - return 0; -} - -static int h323_reload(int fd, int argc, char *argv[]) -{ - ast_mutex_lock(&h323_reload_lock); - if (h323_reloading) { - ast_verbose("Previous H.323 reload not yet done\n"); - } else { - h323_reloading = 1; - } - ast_mutex_unlock(&h323_reload_lock); - restart_monitor(); - return 0; -} - -static int h323_do_reload(void) -{ - reload_config(1); - return 0; -} - -static int reload(void) -{ - if (!sched || !io) { - ast_log(LOG_NOTICE, "Unload and load chan_h323.so again in order to receive configuration changes.\n"); - return 0; - } - return h323_reload(0, 0, NULL); -} - -static struct ast_cli_entry cli_h323_reload = - { { "h.323", "reload", NULL }, - h323_reload, "Reload H.323 configuration", - h323_reload_usage -}; - -static enum ast_rtp_get_result oh323_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) -{ - struct oh323_pvt *pvt; - enum ast_rtp_get_result res = AST_RTP_GET_FAILED; - - if (!(pvt = (struct oh323_pvt *)chan->tech_pvt)) - return res; - - ast_mutex_lock(&pvt->lock); - if (pvt->rtp && pvt->options.bridge) { - *rtp = pvt->rtp; - res = AST_RTP_TRY_NATIVE; - } - ast_mutex_unlock(&pvt->lock); - - return res; -} - -static enum ast_rtp_get_result oh323_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) -{ - return AST_RTP_GET_FAILED; -} - -static char *convertcap(int cap) -{ - switch (cap) { - case AST_FORMAT_G723_1: - return "G.723"; - case AST_FORMAT_GSM: - return "GSM"; - case AST_FORMAT_ULAW: - return "ULAW"; - case AST_FORMAT_ALAW: - return "ALAW"; - case AST_FORMAT_G722: - return "G.722"; - case AST_FORMAT_ADPCM: - return "G.728"; - case AST_FORMAT_G729A: - return "G.729"; - case AST_FORMAT_SPEEX: - return "SPEEX"; - case AST_FORMAT_ILBC: - return "ILBC"; - default: - ast_log(LOG_NOTICE, "Don't know how to deal with mode %d\n", cap); - return NULL; - } -} - -static int oh323_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active) -{ - /* XXX Deal with Video */ - struct oh323_pvt *pvt; - struct sockaddr_in them; - struct sockaddr_in us; - char *mode; - - if (!rtp) { - return 0; - } - - mode = convertcap(chan->writeformat); - pvt = (struct oh323_pvt *) chan->tech_pvt; - if (!pvt) { - ast_log(LOG_ERROR, "No Private Structure, this is bad\n"); - return -1; - } - ast_rtp_get_peer(rtp, &them); - ast_rtp_get_us(rtp, &us); -#if 0 /* Native bridge still isn't ready */ - h323_native_bridge(pvt->cd.call_token, ast_inet_ntoa(them.sin_addr), mode); -#endif - return 0; -} - -static struct ast_rtp_protocol oh323_rtp = { - .type = "H323", - .get_rtp_info = oh323_get_rtp_peer, - .get_vrtp_info = oh323_get_vrtp_peer, - .set_rtp_peer = oh323_set_rtp_peer, -}; - -static enum ast_module_load_result load_module(void) -{ - int res; - - h323debug = 0; - sched = sched_context_create(); - if (!sched) { - ast_log(LOG_WARNING, "Unable to create schedule context\n"); - return AST_MODULE_LOAD_FAILURE; - } - io = io_context_create(); - if (!io) { - ast_log(LOG_WARNING, "Unable to create I/O context\n"); - return AST_MODULE_LOAD_FAILURE; - } - ast_cli_register(&cli_h323_reload); - ASTOBJ_CONTAINER_INIT(&userl); - ASTOBJ_CONTAINER_INIT(&peerl); - ASTOBJ_CONTAINER_INIT(&aliasl); - res = reload_config(0); - if (res) { - /* No config entry */ - ast_log(LOG_NOTICE, "Unload and load chan_h323.so again in order to receive configuration changes.\n"); - ast_cli_unregister(&cli_h323_reload); - io_context_destroy(io); - io = NULL; - sched_context_destroy(sched); - sched = NULL; - ASTOBJ_CONTAINER_DESTROY(&userl); - ASTOBJ_CONTAINER_DESTROY(&peerl); - ASTOBJ_CONTAINER_DESTROY(&aliasl); - return AST_MODULE_LOAD_DECLINE; - } else { - /* Make sure we can register our channel type */ - if (ast_channel_register(&oh323_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class 'H323'\n"); - ast_cli_unregister(&cli_h323_reload); - h323_end_process(); - io_context_destroy(io); - sched_context_destroy(sched); - - ASTOBJ_CONTAINER_DESTROYALL(&userl, oh323_destroy_user); - ASTOBJ_CONTAINER_DESTROY(&userl); - ASTOBJ_CONTAINER_DESTROYALL(&peerl, oh323_destroy_peer); - ASTOBJ_CONTAINER_DESTROY(&peerl); - ASTOBJ_CONTAINER_DESTROYALL(&aliasl, oh323_destroy_alias); - ASTOBJ_CONTAINER_DESTROY(&aliasl); - - return AST_MODULE_LOAD_FAILURE; - } - ast_cli_register_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry)); - - ast_rtp_proto_register(&oh323_rtp); - - /* Register our callback functions */ - h323_callback_register(setup_incoming_call, - setup_outgoing_call, - external_rtp_create, - setup_rtp_connection, - cleanup_connection, - chan_ringing, - connection_made, - receive_digit, - answer_call, - progress, - set_dtmf_payload, - hangup_connection, - set_local_capabilities, - set_peer_capabilities); - /* start the h.323 listener */ - if (h323_start_listener(h323_signalling_port, bindaddr)) { - ast_log(LOG_ERROR, "Unable to create H323 listener.\n"); - ast_rtp_proto_unregister(&oh323_rtp); - ast_cli_unregister_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry)); - ast_cli_unregister(&cli_h323_reload); - h323_end_process(); - io_context_destroy(io); - sched_context_destroy(sched); - - ASTOBJ_CONTAINER_DESTROYALL(&userl, oh323_destroy_user); - ASTOBJ_CONTAINER_DESTROY(&userl); - ASTOBJ_CONTAINER_DESTROYALL(&peerl, oh323_destroy_peer); - ASTOBJ_CONTAINER_DESTROY(&peerl); - ASTOBJ_CONTAINER_DESTROYALL(&aliasl, oh323_destroy_alias); - ASTOBJ_CONTAINER_DESTROY(&aliasl); - - return AST_MODULE_LOAD_FAILURE; - } - /* Possibly register with a GK */ - if (!gatekeeper_disable) { - if (h323_set_gk(gatekeeper_discover, gatekeeper, secret)) { - ast_log(LOG_ERROR, "Gatekeeper registration failed.\n"); - gatekeeper_disable = 1; - res = AST_MODULE_LOAD_SUCCESS; - } - } - /* And start the monitor for the first time */ - restart_monitor(); - } - return res; -} - -static int unload_module(void) -{ - struct oh323_pvt *p, *pl; - - /* unregister commands */ - ast_cli_unregister_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry)); - ast_cli_unregister(&cli_h323_reload); - - ast_channel_unregister(&oh323_tech); - ast_rtp_proto_unregister(&oh323_rtp); - - if (!ast_mutex_lock(&iflock)) { - /* hangup all interfaces if they have an owner */ - p = iflist; - while(p) { - if (p->owner) { - ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD); - } - p = p->next; - } - iflist = NULL; - ast_mutex_unlock(&iflock); - } else { - ast_log(LOG_WARNING, "Unable to lock the interface list\n"); - return -1; - } - if (!ast_mutex_lock(&monlock)) { - if ((monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) { - if (monitor_thread != pthread_self()) { - pthread_cancel(monitor_thread); - } - pthread_kill(monitor_thread, SIGURG); - pthread_join(monitor_thread, NULL); - } - monitor_thread = AST_PTHREADT_STOP; - ast_mutex_unlock(&monlock); - } else { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - return -1; - } - if (!ast_mutex_lock(&iflock)) { - /* destroy all the interfaces and free their memory */ - p = iflist; - while(p) { - pl = p; - p = p->next; - /* free associated memory */ - ast_mutex_destroy(&pl->lock); - free(pl); - } - iflist = NULL; - ast_mutex_unlock(&iflock); - } else { - ast_log(LOG_WARNING, "Unable to lock the interface list\n"); - return -1; - } - if (!gatekeeper_disable) - h323_gk_urq(); - h323_end_process(); - if (io) - io_context_destroy(io); - if (sched) - sched_context_destroy(sched); - - ASTOBJ_CONTAINER_DESTROYALL(&userl, oh323_destroy_user); - ASTOBJ_CONTAINER_DESTROY(&userl); - ASTOBJ_CONTAINER_DESTROYALL(&peerl, oh323_destroy_peer); - ASTOBJ_CONTAINER_DESTROY(&peerl); - ASTOBJ_CONTAINER_DESTROYALL(&aliasl, oh323_destroy_alias); - ASTOBJ_CONTAINER_DESTROY(&aliasl); - - return 0; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "The NuFone Network's OpenH323 Channel Driver", - .load = load_module, - .unload = unload_module, - .reload = reload, -); diff --git a/1.4.23-rc4/channels/chan_iax2.c b/1.4.23-rc4/channels/chan_iax2.c deleted file mode 100644 index d03f47c6c..000000000 --- a/1.4.23-rc4/channels/chan_iax2.c +++ /dev/null @@ -1,11336 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief Implementation of Inter-Asterisk eXchange Version 2 - * - * \author Mark Spencer <markster@digium.com> - * - * \par See also - * \arg \ref Config_iax - * - * \ingroup channel_drivers - */ - -/*** MODULEINFO - <use>dahdi</use> - <depend>res_features</depend> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdlib.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/mman.h> -#include <dirent.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <sys/time.h> -#include <sys/signal.h> -#include <signal.h> -#include <string.h> -#include <strings.h> -#include <errno.h> -#include <unistd.h> -#include <netdb.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <regex.h> - -#if defined(HAVE_ZAPTEL) || defined (HAVE_DAHDI) -#include <sys/ioctl.h> -#include "asterisk/dahdi_compat.h" -#endif - -#include "asterisk/lock.h" -#include "asterisk/frame.h" -#include "asterisk/channel.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/sched.h" -#include "asterisk/io.h" -#include "asterisk/config.h" -#include "asterisk/options.h" -#include "asterisk/cli.h" -#include "asterisk/translate.h" -#include "asterisk/md5.h" -#include "asterisk/cdr.h" -#include "asterisk/crypto.h" -#include "asterisk/acl.h" -#include "asterisk/manager.h" -#include "asterisk/callerid.h" -#include "asterisk/app.h" -#include "asterisk/astdb.h" -#include "asterisk/musiconhold.h" -#include "asterisk/features.h" -#include "asterisk/utils.h" -#include "asterisk/causes.h" -#include "asterisk/localtime.h" -#include "asterisk/aes.h" -#include "asterisk/dnsmgr.h" -#include "asterisk/devicestate.h" -#include "asterisk/netsock.h" -#include "asterisk/stringfields.h" -#include "asterisk/linkedlists.h" -#include "asterisk/astobj2.h" - -#include "iax2.h" -#include "iax2-parser.h" -#include "iax2-provision.h" -#include "jitterbuf.h" - -/* Define SCHED_MULTITHREADED to run the scheduler in a special - multithreaded mode. */ -#define SCHED_MULTITHREADED - -/* Define DEBUG_SCHED_MULTITHREADED to keep track of where each - thread is actually doing. */ -#define DEBUG_SCHED_MULTITHREAD - -#ifndef IPTOS_MINCOST -#define IPTOS_MINCOST 0x02 -#endif - -#ifdef SO_NO_CHECK -static int nochecksums = 0; -#endif - - -#define PTR_TO_CALLNO(a) ((unsigned short)(unsigned long)(a)) -#define CALLNO_TO_PTR(a) ((void *)(unsigned long)(a)) - -#define DEFAULT_THREAD_COUNT 10 -#define DEFAULT_MAX_THREAD_COUNT 100 -#define DEFAULT_RETRY_TIME 1000 -#define MEMORY_SIZE 100 -#define DEFAULT_DROP 3 - -#define DEBUG_SUPPORT - -#define MIN_REUSE_TIME 60 /* Don't reuse a call number within 60 seconds */ - -/* Sample over last 100 units to determine historic jitter */ -#define GAMMA (0.01) - -static struct ast_codec_pref prefs; - -static const char tdesc[] = "Inter Asterisk eXchange Driver (Ver 2)"; - -static char context[80] = "default"; - -static char language[MAX_LANGUAGE] = ""; -static char regcontext[AST_MAX_CONTEXT] = ""; - -static int maxauthreq = 3; -static int max_retries = 4; -static int ping_time = 21; -static int lagrq_time = 10; -static int maxjitterbuffer=1000; -static int resyncthreshold=1000; -static int maxjitterinterps=10; -static int trunkfreq = 20; -static int authdebug = 1; -static int autokill = 0; -static int iaxcompat = 0; -static int last_authmethod = 0; - -static int iaxdefaultdpcache=10 * 60; /* Cache dialplan entries for 10 minutes by default */ - -static int iaxdefaulttimeout = 5; /* Default to wait no more than 5 seconds for a reply to come back */ - -static unsigned int tos = 0; - -static int min_reg_expire; -static int max_reg_expire; - -static int timingfd = -1; /* Timing file descriptor */ - -static struct ast_netsock_list *netsock; -static struct ast_netsock_list *outsock; /*!< used if sourceaddress specified and bindaddr == INADDR_ANY */ -static int defaultsockfd = -1; - -int (*iax2_regfunk)(const char *username, int onoff) = NULL; - -/* Ethernet, etc */ -#define IAX_CAPABILITY_FULLBANDWIDTH 0xFFFF -/* T1, maybe ISDN */ -#define IAX_CAPABILITY_MEDBANDWIDTH (IAX_CAPABILITY_FULLBANDWIDTH & \ - ~AST_FORMAT_SLINEAR & \ - ~AST_FORMAT_ULAW & \ - ~AST_FORMAT_ALAW & \ - ~AST_FORMAT_G722) -/* A modem */ -#define IAX_CAPABILITY_LOWBANDWIDTH (IAX_CAPABILITY_MEDBANDWIDTH & \ - ~AST_FORMAT_G726 & \ - ~AST_FORMAT_G726_AAL2 & \ - ~AST_FORMAT_ADPCM) - -#define IAX_CAPABILITY_LOWFREE (IAX_CAPABILITY_LOWBANDWIDTH & \ - ~AST_FORMAT_G723_1) - - -#define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */ -#define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */ -#define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */ - -static struct io_context *io; -static struct sched_context *sched; - -static int iax2_capability = IAX_CAPABILITY_FULLBANDWIDTH; - -static int iaxdebug = 0; - -static int iaxtrunkdebug = 0; - -static int test_losspct = 0; -#ifdef IAXTESTS -static int test_late = 0; -static int test_resync = 0; -static int test_jit = 0; -static int test_jitpct = 0; -#endif /* IAXTESTS */ - -static char accountcode[AST_MAX_ACCOUNT_CODE]; -static char mohinterpret[MAX_MUSICCLASS]; -static char mohsuggest[MAX_MUSICCLASS]; -static int amaflags = 0; -static int adsi = 0; -static int delayreject = 0; -static int iax2_encryption = 0; - -static struct ast_flags globalflags = { 0 }; - -static pthread_t netthreadid = AST_PTHREADT_NULL; -static pthread_t schedthreadid = AST_PTHREADT_NULL; -AST_MUTEX_DEFINE_STATIC(sched_lock); -static ast_cond_t sched_cond; - -enum { - IAX_STATE_STARTED = (1 << 0), - IAX_STATE_AUTHENTICATED = (1 << 1), - IAX_STATE_TBD = (1 << 2), - IAX_STATE_UNCHANGED = (1 << 3), -} iax2_state; - -struct iax2_context { - char context[AST_MAX_CONTEXT]; - struct iax2_context *next; -}; - -enum { - IAX_HASCALLERID = (1 << 0), /*!< CallerID has been specified */ - IAX_DELME = (1 << 1), /*!< Needs to be deleted */ - IAX_TEMPONLY = (1 << 2), /*!< Temporary (realtime) */ - IAX_TRUNK = (1 << 3), /*!< Treat as a trunk */ - IAX_NOTRANSFER = (1 << 4), /*!< Don't native bridge */ - IAX_USEJITTERBUF = (1 << 5), /*!< Use jitter buffer */ - IAX_DYNAMIC = (1 << 6), /*!< dynamic peer */ - IAX_SENDANI = (1 << 7), /*!< Send ANI along with CallerID */ - /* (1 << 8) is currently unused due to the deprecation of an old option. Go ahead, take it! */ - IAX_ALREADYGONE = (1 << 9), /*!< Already disconnected */ - IAX_PROVISION = (1 << 10), /*!< This is a provisioning request */ - IAX_QUELCH = (1 << 11), /*!< Whether or not we quelch audio */ - IAX_ENCRYPTED = (1 << 12), /*!< Whether we should assume encrypted tx/rx */ - IAX_KEYPOPULATED = (1 << 13), /*!< Whether we have a key populated */ - IAX_CODEC_USER_FIRST = (1 << 14), /*!< are we willing to let the other guy choose the codec? */ - IAX_CODEC_NOPREFS = (1 << 15), /*!< Force old behaviour by turning off prefs */ - IAX_CODEC_NOCAP = (1 << 16), /*!< only consider requested format and ignore capabilities*/ - IAX_RTCACHEFRIENDS = (1 << 17), /*!< let realtime stay till your reload */ - IAX_RTUPDATE = (1 << 18), /*!< Send a realtime update */ - IAX_RTAUTOCLEAR = (1 << 19), /*!< erase me on expire */ - IAX_FORCEJITTERBUF = (1 << 20), /*!< Force jitterbuffer, even when bridged to a channel that can take jitter */ - IAX_RTIGNOREREGEXPIRE = (1 << 21), /*!< When using realtime, ignore registration expiration */ - IAX_TRUNKTIMESTAMPS = (1 << 22), /*!< Send trunk timestamps */ - IAX_TRANSFERMEDIA = (1 << 23), /*!< When doing IAX2 transfers, transfer media only */ - IAX_MAXAUTHREQ = (1 << 24), /*!< Maximum outstanding AUTHREQ restriction is in place */ - IAX_DELAYPBXSTART = (1 << 25), /*!< Don't start a PBX on the channel until the peer sends us a - response, so that we've achieved a three-way handshake with - them before sending voice or anything else*/ - IAX_ALLOWFWDOWNLOAD = (1 << 26), /*!< Allow the FWDOWNL command? */ -} iax2_flags; - -static int global_rtautoclear = 120; - -static int reload_config(void); -static int iax2_reload(int fd, int argc, char *argv[]); - - -struct iax2_user { - AST_DECLARE_STRING_FIELDS( - AST_STRING_FIELD(name); - AST_STRING_FIELD(secret); - AST_STRING_FIELD(dbsecret); - AST_STRING_FIELD(accountcode); - AST_STRING_FIELD(mohinterpret); - AST_STRING_FIELD(mohsuggest); - AST_STRING_FIELD(inkeys); /*!< Key(s) this user can use to authenticate to us */ - AST_STRING_FIELD(language); - AST_STRING_FIELD(cid_num); - AST_STRING_FIELD(cid_name); - ); - - int authmethods; - int encmethods; - int amaflags; - int adsi; - unsigned int flags; - int capability; - int maxauthreq; /*!< Maximum allowed outstanding AUTHREQs */ - int curauthreq; /*!< Current number of outstanding AUTHREQs */ - struct ast_codec_pref prefs; - struct ast_ha *ha; - struct iax2_context *contexts; - struct ast_variable *vars; -}; - -struct iax2_peer { - AST_DECLARE_STRING_FIELDS( - AST_STRING_FIELD(name); - AST_STRING_FIELD(username); - AST_STRING_FIELD(secret); - AST_STRING_FIELD(dbsecret); - AST_STRING_FIELD(outkey); /*!< What key we use to talk to this peer */ - - AST_STRING_FIELD(regexten); /*!< Extension to register (if regcontext is used) */ - AST_STRING_FIELD(context); /*!< For transfers only */ - AST_STRING_FIELD(peercontext); /*!< Context to pass to peer */ - AST_STRING_FIELD(mailbox); /*!< Mailbox */ - AST_STRING_FIELD(mohinterpret); - AST_STRING_FIELD(mohsuggest); - AST_STRING_FIELD(inkeys); /*!< Key(s) this peer can use to authenticate to us */ - /* Suggested caller id if registering */ - AST_STRING_FIELD(cid_num); /*!< Default context (for transfer really) */ - AST_STRING_FIELD(cid_name); /*!< Default context (for transfer really) */ - AST_STRING_FIELD(zonetag); /*!< Time Zone */ - ); - struct ast_codec_pref prefs; - struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager */ - struct sockaddr_in addr; - int formats; - int sockfd; /*!< Socket to use for transmission */ - struct in_addr mask; - int adsi; - unsigned int flags; - - /* Dynamic Registration fields */ - struct sockaddr_in defaddr; /*!< Default address if there is one */ - int authmethods; /*!< Authentication methods (IAX_AUTH_*) */ - int encmethods; /*!< Encryption methods (IAX_ENCRYPT_*) */ - - int expire; /*!< Schedule entry for expiry */ - int expiry; /*!< How soon to expire */ - int capability; /*!< Capability */ - - /* Qualification */ - int callno; /*!< Call number of POKE request */ - int pokeexpire; /*!< Scheduled qualification-related task (ie iax2_poke_peer_s or iax2_poke_noanswer) */ - int lastms; /*!< How long last response took (in ms), or -1 for no response */ - int maxms; /*!< Max ms we will accept for the host to be up, 0 to not monitor */ - - int pokefreqok; /*!< How often to check if the host is up */ - int pokefreqnotok; /*!< How often to check when the host has been determined to be down */ - int historicms; /*!< How long recent average responses took */ - int smoothing; /*!< Sample over how many units to determine historic ms */ - - struct ast_ha *ha; -}; - -#define IAX2_TRUNK_PREFACE (sizeof(struct iax_frame) + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr)) - -static struct iax2_trunk_peer { - ast_mutex_t lock; - int sockfd; - struct sockaddr_in addr; - struct timeval txtrunktime; /*!< Transmit trunktime */ - struct timeval rxtrunktime; /*!< Receive trunktime */ - struct timeval lasttxtime; /*!< Last transmitted trunktime */ - struct timeval trunkact; /*!< Last trunk activity */ - unsigned int lastsent; /*!< Last sent time */ - /* Trunk data and length */ - unsigned char *trunkdata; - unsigned int trunkdatalen; - unsigned int trunkdataalloc; - struct iax2_trunk_peer *next; - int trunkerror; - int calls; -} *tpeers = NULL; - -AST_MUTEX_DEFINE_STATIC(tpeerlock); - -struct iax_firmware { - struct iax_firmware *next; - int fd; - int mmaplen; - int dead; - struct ast_iax2_firmware_header *fwh; - unsigned char *buf; -}; - -enum iax_reg_state { - REG_STATE_UNREGISTERED = 0, - REG_STATE_REGSENT, - REG_STATE_AUTHSENT, - REG_STATE_REGISTERED, - REG_STATE_REJECTED, - REG_STATE_TIMEOUT, - REG_STATE_NOAUTH -}; - -enum iax_transfer_state { - TRANSFER_NONE = 0, - TRANSFER_BEGIN, - TRANSFER_READY, - TRANSFER_RELEASED, - TRANSFER_PASSTHROUGH, - TRANSFER_MBEGIN, - TRANSFER_MREADY, - TRANSFER_MRELEASED, - TRANSFER_MPASSTHROUGH, - TRANSFER_MEDIA, - TRANSFER_MEDIAPASS -}; - -struct iax2_registry { - struct sockaddr_in addr; /*!< Who we connect to for registration purposes */ - char username[80]; - char secret[80]; /*!< Password or key name in []'s */ - char random[80]; - int expire; /*!< Sched ID of expiration */ - int refresh; /*!< How often to refresh */ - enum iax_reg_state regstate; - int messages; /*!< Message count, low 8 bits = new, high 8 bits = old */ - int callno; /*!< Associated call number if applicable */ - struct sockaddr_in us; /*!< Who the server thinks we are */ - struct ast_dnsmgr_entry *dnsmgr; /*!< DNS refresh manager */ - AST_LIST_ENTRY(iax2_registry) entry; -}; - -static AST_LIST_HEAD_STATIC(registrations, iax2_registry); - -/* Don't retry more frequently than every 10 ms, or less frequently than every 5 seconds */ -#define MIN_RETRY_TIME 100 -#define MAX_RETRY_TIME 10000 - -#define MAX_JITTER_BUFFER 50 -#define MIN_JITTER_BUFFER 10 - -#define DEFAULT_TRUNKDATA 640 * 10 /*!< 40ms, uncompressed linear * 10 channels */ -#define MAX_TRUNKDATA 640 * 200 /*!< 40ms, uncompressed linear * 200 channels */ - -#define MAX_TIMESTAMP_SKEW 160 /*!< maximum difference between actual and predicted ts for sending */ - -/* If consecutive voice frame timestamps jump by more than this many milliseconds, then jitter buffer will resync */ -#define TS_GAP_FOR_JB_RESYNC 5000 - -static int iaxthreadcount = DEFAULT_THREAD_COUNT; -static int iaxmaxthreadcount = DEFAULT_MAX_THREAD_COUNT; -static int iaxdynamicthreadcount = 0; -static int iaxdynamicthreadnum = 0; -static int iaxactivethreadcount = 0; - -struct iax_rr { - int jitter; - int losspct; - int losscnt; - int packets; - int delay; - int dropped; - int ooo; -}; - -struct chan_iax2_pvt { - /*! Socket to send/receive on for this call */ - int sockfd; - /*! Last received voice format */ - int voiceformat; - /*! Last received video format */ - int videoformat; - /*! Last sent voice format */ - int svoiceformat; - /*! Last sent video format */ - int svideoformat; - /*! What we are capable of sending */ - int capability; - /*! Last received timestamp */ - unsigned int last; - /*! Last sent timestamp - never send the same timestamp twice in a single call */ - unsigned int lastsent; - /*! Timestamp of the last video frame sent */ - unsigned int lastvsent; - /*! Next outgoing timestamp if everything is good */ - unsigned int nextpred; - /*! True if the last voice we transmitted was not silence/CNG */ - int notsilenttx; - /*! Ping time */ - unsigned int pingtime; - /*! Max time for initial response */ - int maxtime; - /*! Peer Address */ - struct sockaddr_in addr; - /*! Actual used codec preferences */ - struct ast_codec_pref prefs; - /*! Requested codec preferences */ - struct ast_codec_pref rprefs; - /*! Our call number */ - unsigned short callno; - /*! Peer callno */ - unsigned short peercallno; - /*! Negotiated format, this is only used to remember what format was - chosen for an unauthenticated call so that the channel can get - created later using the right format */ - int chosenformat; - /*! Peer selected format */ - int peerformat; - /*! Peer capability */ - int peercapability; - /*! timeval that we base our transmission on */ - struct timeval offset; - /*! timeval that we base our delivery on */ - struct timeval rxcore; - /*! The jitterbuffer */ - jitterbuf *jb; - /*! active jb read scheduler id */ - int jbid; - /*! LAG */ - int lag; - /*! Error, as discovered by the manager */ - int error; - /*! Owner if we have one */ - struct ast_channel *owner; - /*! What's our state? */ - struct ast_flags state; - /*! Expiry (optional) */ - int expiry; - /*! Next outgoing sequence number */ - unsigned char oseqno; - /*! Next sequence number they have not yet acknowledged */ - unsigned char rseqno; - /*! Next incoming sequence number */ - unsigned char iseqno; - /*! Last incoming sequence number we have acknowledged */ - unsigned char aseqno; - - AST_DECLARE_STRING_FIELDS( - /*! Peer name */ - AST_STRING_FIELD(peer); - /*! Default Context */ - AST_STRING_FIELD(context); - /*! Caller ID if available */ - AST_STRING_FIELD(cid_num); - AST_STRING_FIELD(cid_name); - /*! Hidden Caller ID (i.e. ANI) if appropriate */ - AST_STRING_FIELD(ani); - /*! DNID */ - AST_STRING_FIELD(dnid); - /*! RDNIS */ - AST_STRING_FIELD(rdnis); - /*! Requested Extension */ - AST_STRING_FIELD(exten); - /*! Expected Username */ - AST_STRING_FIELD(username); - /*! Expected Secret */ - AST_STRING_FIELD(secret); - /*! MD5 challenge */ - AST_STRING_FIELD(challenge); - /*! Public keys permitted keys for incoming authentication */ - AST_STRING_FIELD(inkeys); - /*! Private key for outgoing authentication */ - AST_STRING_FIELD(outkey); - /*! Preferred language */ - AST_STRING_FIELD(language); - /*! Hostname/peername for naming purposes */ - AST_STRING_FIELD(host); - - AST_STRING_FIELD(dproot); - AST_STRING_FIELD(accountcode); - AST_STRING_FIELD(mohinterpret); - AST_STRING_FIELD(mohsuggest); - ); - - /*! permitted authentication methods */ - int authmethods; - /*! permitted encryption methods */ - int encmethods; - /*! Encryption AES-128 Key */ - aes_encrypt_ctx ecx; - /*! Decryption AES-128 Key */ - aes_decrypt_ctx dcx; - /*! 32 bytes of semi-random data */ - unsigned char semirand[32]; - /*! Associated registry */ - struct iax2_registry *reg; - /*! Associated peer for poking */ - struct iax2_peer *peerpoke; - /*! IAX_ flags */ - unsigned int flags; - int adsi; - - /*! Transferring status */ - enum iax_transfer_state transferring; - /*! Transfer identifier */ - int transferid; - /*! Who we are IAX transfering to */ - struct sockaddr_in transfer; - /*! What's the new call number for the transfer */ - unsigned short transfercallno; - /*! Transfer decrypt AES-128 Key */ - aes_encrypt_ctx tdcx; - - /*! Status of knowledge of peer ADSI capability */ - int peeradsicpe; - - /*! Who we are bridged to */ - unsigned short bridgecallno; - - int pingid; /*!< Transmit PING request */ - int lagid; /*!< Retransmit lag request */ - int autoid; /*!< Auto hangup for Dialplan requestor */ - int authid; /*!< Authentication rejection ID */ - int authfail; /*!< Reason to report failure */ - int initid; /*!< Initial peer auto-congest ID (based on qualified peers) */ - int calling_ton; - int calling_tns; - int calling_pres; - int amaflags; - struct iax2_dpcache *dpentries; - struct ast_variable *vars; - /*! last received remote rr */ - struct iax_rr remote_rr; - /*! Current base time: (just for stats) */ - int min; - /*! Dropped frame count: (just for stats) */ - int frames_dropped; - /*! received frame count: (just for stats) */ - int frames_received; -}; - -static struct ast_iax2_queue { - AST_LIST_HEAD(, iax_frame) queue; - int count; -} iaxq; - -/*! - * This module will get much higher performance when doing a lot of - * user and peer lookups if the number of buckets is increased from 1. - * However, to maintain old behavior for Asterisk 1.4, these are set to - * 1 by default. When using multiple buckets, search order through these - * containers is considered random, so you will not be able to depend on - * the order the entires are specified in iax.conf for matching order. */ -#ifdef LOW_MEMORY -#define MAX_PEER_BUCKETS 1 -/* #define MAX_PEER_BUCKETS 17 */ -#else -#define MAX_PEER_BUCKETS 1 -/* #define MAX_PEER_BUCKETS 563 */ -#endif -static struct ao2_container *peers; - -#define MAX_USER_BUCKETS MAX_PEER_BUCKETS -static struct ao2_container *users; - -static struct ast_firmware_list { - struct iax_firmware *wares; - ast_mutex_t lock; -} waresl; - -/*! Extension exists */ -#define CACHE_FLAG_EXISTS (1 << 0) -/*! Extension is nonexistent */ -#define CACHE_FLAG_NONEXISTENT (1 << 1) -/*! Extension can exist */ -#define CACHE_FLAG_CANEXIST (1 << 2) -/*! Waiting to hear back response */ -#define CACHE_FLAG_PENDING (1 << 3) -/*! Timed out */ -#define CACHE_FLAG_TIMEOUT (1 << 4) -/*! Request transmitted */ -#define CACHE_FLAG_TRANSMITTED (1 << 5) -/*! Timeout */ -#define CACHE_FLAG_UNKNOWN (1 << 6) -/*! Matchmore */ -#define CACHE_FLAG_MATCHMORE (1 << 7) - -static struct iax2_dpcache { - char peercontext[AST_MAX_CONTEXT]; - char exten[AST_MAX_EXTENSION]; - struct timeval orig; - struct timeval expiry; - int flags; - unsigned short callno; - int waiters[256]; - struct iax2_dpcache *next; - struct iax2_dpcache *peer; /*!< For linking in peers */ -} *dpcache; - -AST_MUTEX_DEFINE_STATIC(dpcache_lock); - -static void reg_source_db(struct iax2_peer *p); -static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin); - -static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt); - -#define IAX_IOSTATE_IDLE 0 -#define IAX_IOSTATE_READY 1 -#define IAX_IOSTATE_PROCESSING 2 -#define IAX_IOSTATE_SCHEDREADY 3 - -#define IAX_TYPE_POOL 1 -#define IAX_TYPE_DYNAMIC 2 - -struct iax2_pkt_buf { - AST_LIST_ENTRY(iax2_pkt_buf) entry; - size_t len; - unsigned char buf[1]; -}; - -struct iax2_thread { - AST_LIST_ENTRY(iax2_thread) list; - int type; - int iostate; -#ifdef SCHED_MULTITHREADED - void (*schedfunc)(const void *); - const void *scheddata; -#endif -#ifdef DEBUG_SCHED_MULTITHREAD - char curfunc[80]; -#endif - int actions; - pthread_t threadid; - int threadnum; - struct sockaddr_in iosin; - unsigned char readbuf[4096]; - unsigned char *buf; - ssize_t buf_len; - size_t buf_size; - int iofd; - time_t checktime; - ast_mutex_t lock; - ast_cond_t cond; - unsigned int ready_for_signal:1; - /*! if this thread is processing a full frame, - some information about that frame will be stored - here, so we can avoid dispatching any more full - frames for that callno to other threads */ - struct { - unsigned short callno; - struct sockaddr_in sin; - unsigned char type; - unsigned char csub; - } ffinfo; - /*! Queued up full frames for processing. If more full frames arrive for - * a call which this thread is already processing a full frame for, they - * are queued up here. */ - AST_LIST_HEAD_NOLOCK(, iax2_pkt_buf) full_frames; -}; - -/* Thread lists */ -static AST_LIST_HEAD_STATIC(idle_list, iax2_thread); -static AST_LIST_HEAD_STATIC(active_list, iax2_thread); -static AST_LIST_HEAD_STATIC(dynamic_list, iax2_thread); - -static void *iax2_process_thread(void *data); - -static void signal_condition(ast_mutex_t *lock, ast_cond_t *cond) -{ - ast_mutex_lock(lock); - ast_cond_signal(cond); - ast_mutex_unlock(lock); -} - -static void iax_debug_output(const char *data) -{ - if (iaxdebug) - ast_verbose("%s", data); -} - -static void iax_error_output(const char *data) -{ - ast_log(LOG_WARNING, "%s", data); -} - -static void __attribute__((format(printf, 1, 2))) jb_error_output(const char *fmt, ...) -{ - va_list args; - char buf[1024]; - - va_start(args, fmt); - vsnprintf(buf, 1024, fmt, args); - va_end(args); - - ast_log(LOG_ERROR, "%s", buf); -} - -static void __attribute__((format(printf, 1, 2))) jb_warning_output(const char *fmt, ...) -{ - va_list args; - char buf[1024]; - - va_start(args, fmt); - vsnprintf(buf, 1024, fmt, args); - va_end(args); - - ast_log(LOG_WARNING, "%s", buf); -} - -static void __attribute__((format(printf, 1, 2))) jb_debug_output(const char *fmt, ...) -{ - va_list args; - char buf[1024]; - - va_start(args, fmt); - vsnprintf(buf, 1024, fmt, args); - va_end(args); - - ast_verbose("%s", buf); -} - -/* XXX We probably should use a mutex when working with this XXX */ -static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS]; -static ast_mutex_t iaxsl[ARRAY_LEN(iaxs)]; -static struct timeval lastused[ARRAY_LEN(iaxs)]; - -/*! - * \brief Another container of iax2_pvt structures - * - * Active IAX2 pvt structs are also stored in this container, if they are a part - * of an active call where we know the remote side's call number. The reason - * for this is that incoming media frames do not contain our call number. So, - * instead of having to iterate the entire iaxs array, we use this container to - * look up calls where the remote side is using a given call number. - */ -static struct ao2_container *iax_peercallno_pvts; - -/* Flag to use with trunk calls, keeping these calls high up. It halves our effective use - but keeps the division between trunked and non-trunked better. */ -#define TRUNK_CALL_START ARRAY_LEN(iaxs) / 2 - -static int maxtrunkcall = TRUNK_CALL_START; -static int maxnontrunkcall = 1; - -static enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); -static int expire_registry(const void *data); -static int iax2_answer(struct ast_channel *c); -static int iax2_call(struct ast_channel *c, char *dest, int timeout); -static int iax2_devicestate(void *data); -static int iax2_digit_begin(struct ast_channel *c, char digit); -static int iax2_digit_end(struct ast_channel *c, char digit, unsigned int duration); -static int iax2_do_register(struct iax2_registry *reg); -static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan); -static int iax2_hangup(struct ast_channel *c); -static int iax2_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen); -static int iax2_poke_peer(struct iax2_peer *peer, int heldcall); -static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force); -static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final); -static int iax2_sendhtml(struct ast_channel *c, int subclass, const char *data, int datalen); -static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img); -static int iax2_sendtext(struct ast_channel *c, const char *text); -static int iax2_setoption(struct ast_channel *c, int option, void *data, int datalen); -static int iax2_transfer(struct ast_channel *c, const char *dest); -static int iax2_write(struct ast_channel *c, struct ast_frame *f); -static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int); -static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int); -static int send_command_immediate(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int); -static int send_command_locked(unsigned short callno, char, int, unsigned int, const unsigned char *, int, int); -static int send_command_transfer(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int); -static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause); -static struct ast_frame *iax2_read(struct ast_channel *c); -static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly); -static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly); -static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime); -static void prune_peers(void); - -static const struct ast_channel_tech iax2_tech = { - .type = "IAX2", - .description = tdesc, - .capabilities = IAX_CAPABILITY_FULLBANDWIDTH, - .properties = AST_CHAN_TP_WANTSJITTER, - .requester = iax2_request, - .devicestate = iax2_devicestate, - .send_digit_begin = iax2_digit_begin, - .send_digit_end = iax2_digit_end, - .send_text = iax2_sendtext, - .send_image = iax2_sendimage, - .send_html = iax2_sendhtml, - .call = iax2_call, - .hangup = iax2_hangup, - .answer = iax2_answer, - .read = iax2_read, - .write = iax2_write, - .write_video = iax2_write, - .indicate = iax2_indicate, - .setoption = iax2_setoption, - .bridge = iax2_bridge, - .transfer = iax2_transfer, - .fixup = iax2_fixup, -}; - -/* WARNING: insert_idle_thread should only ever be called within the - * context of an iax2_process_thread() thread. - */ -static void insert_idle_thread(struct iax2_thread *thread) -{ - if (thread->type == IAX_TYPE_DYNAMIC) { - AST_LIST_LOCK(&dynamic_list); - AST_LIST_INSERT_TAIL(&dynamic_list, thread, list); - AST_LIST_UNLOCK(&dynamic_list); - } else { - AST_LIST_LOCK(&idle_list); - AST_LIST_INSERT_TAIL(&idle_list, thread, list); - AST_LIST_UNLOCK(&idle_list); - } - - return; -} - -static struct iax2_thread *find_idle_thread(void) -{ - pthread_attr_t attr; - struct iax2_thread *thread = NULL; - - /* Pop the head of the list off */ - AST_LIST_LOCK(&idle_list); - thread = AST_LIST_REMOVE_HEAD(&idle_list, list); - AST_LIST_UNLOCK(&idle_list); - - /* If no idle thread is available from the regular list, try dynamic */ - if (thread == NULL) { - AST_LIST_LOCK(&dynamic_list); - thread = AST_LIST_REMOVE_HEAD(&dynamic_list, list); - /* Make sure we absolutely have a thread... if not, try to make one if allowed */ - if (thread == NULL && iaxmaxthreadcount > iaxdynamicthreadcount) { - /* We need to MAKE a thread! */ - if ((thread = ast_calloc(1, sizeof(*thread)))) { - thread->threadnum = iaxdynamicthreadnum++; - thread->type = IAX_TYPE_DYNAMIC; - ast_mutex_init(&thread->lock); - ast_cond_init(&thread->cond, NULL); - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (ast_pthread_create(&thread->threadid, &attr, iax2_process_thread, thread)) { - free(thread); - thread = NULL; - } else { - /* All went well and the thread is up, so increment our count */ - iaxdynamicthreadcount++; - - /* Wait for the thread to be ready before returning it to the caller */ - while (!thread->ready_for_signal) - usleep(1); - } - } - } - AST_LIST_UNLOCK(&dynamic_list); - } - - /* this thread is not processing a full frame (since it is idle), - so ensure that the field for the full frame call number is empty */ - if (thread) - memset(&thread->ffinfo, 0, sizeof(thread->ffinfo)); - - return thread; -} - -#ifdef SCHED_MULTITHREADED -static int __schedule_action(void (*func)(const void *data), const void *data, const char *funcname) -{ - struct iax2_thread *thread = NULL; - static time_t lasterror; - static time_t t; - - thread = find_idle_thread(); - - if (thread != NULL) { - thread->schedfunc = func; - thread->scheddata = data; - thread->iostate = IAX_IOSTATE_SCHEDREADY; -#ifdef DEBUG_SCHED_MULTITHREAD - ast_copy_string(thread->curfunc, funcname, sizeof(thread->curfunc)); -#endif - signal_condition(&thread->lock, &thread->cond); - return 0; - } - time(&t); - if (t != lasterror && option_debug) - ast_log(LOG_DEBUG, "Out of idle IAX2 threads for scheduling!\n"); - lasterror = t; - - return -1; -} -#define schedule_action(func, data) __schedule_action(func, data, __PRETTY_FUNCTION__) -#endif - -static int iax2_sched_add(struct sched_context *con, int when, ast_sched_cb callback, const void *data) -{ - int res; - - res = ast_sched_add(con, when, callback, data); - signal_condition(&sched_lock, &sched_cond); - - return res; -} - -static int send_ping(const void *data); - -static void __send_ping(const void *data) -{ - int callno = (long) data; - - ast_mutex_lock(&iaxsl[callno]); - - if (iaxs[callno]) { - if (iaxs[callno]->peercallno) { - send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1); - iaxs[callno]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, data); - } else { - /* I am the schedule, so I'm allowed to do this */ - iaxs[callno]->pingid = -1; - } - } else if (option_debug > 0) { - ast_log(LOG_DEBUG, "I was supposed to send a PING with callno %d, but no such call exists (and I cannot remove pingid, either).\n", callno); - } - - ast_mutex_unlock(&iaxsl[callno]); -} - -static int send_ping(const void *data) -{ -#ifdef SCHED_MULTITHREADED - if (schedule_action(__send_ping, data)) -#endif - __send_ping(data); - - return 0; -} - -static int get_encrypt_methods(const char *s) -{ - int e; - if (!strcasecmp(s, "aes128")) - e = IAX_ENCRYPT_AES128; - else if (ast_true(s)) - e = IAX_ENCRYPT_AES128; - else - e = 0; - return e; -} - -static int send_lagrq(const void *data); - -static void __send_lagrq(const void *data) -{ - int callno = (long) data; - - ast_mutex_lock(&iaxsl[callno]); - - if (iaxs[callno]) { - if (iaxs[callno]->peercallno) { - send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1); - iaxs[callno]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, data); - } else { - /* I am the schedule, so I'm allowed to do this */ - iaxs[callno]->lagid = -1; - } - } else { - ast_log(LOG_WARNING, "I was supposed to send a LAGRQ with callno %d, but no such call exists (and I cannot remove lagid, either).\n", callno); - } - - ast_mutex_unlock(&iaxsl[callno]); -} - -static int send_lagrq(const void *data) -{ -#ifdef SCHED_MULTITHREADED - if (schedule_action(__send_lagrq, data)) -#endif - __send_lagrq(data); - - return 0; -} - -static unsigned char compress_subclass(int subclass) -{ - int x; - int power=-1; - /* If it's 128 or smaller, just return it */ - if (subclass < IAX_FLAG_SC_LOG) - return subclass; - /* Otherwise find its power */ - for (x = 0; x < IAX_MAX_SHIFT; x++) { - if (subclass & (1 << x)) { - if (power > -1) { - ast_log(LOG_WARNING, "Can't compress subclass %d\n", subclass); - return 0; - } else - power = x; - } - } - return power | IAX_FLAG_SC_LOG; -} - -static int uncompress_subclass(unsigned char csub) -{ - /* If the SC_LOG flag is set, return 2^csub otherwise csub */ - if (csub & IAX_FLAG_SC_LOG) { - /* special case for 'compressed' -1 */ - if (csub == 0xff) - return -1; - else - return 1 << (csub & ~IAX_FLAG_SC_LOG & IAX_MAX_SHIFT); - } - else - return csub; -} - -/*! - * \note The only member of the peer passed here guaranteed to be set is the name field - */ -static int peer_hash_cb(const void *obj, const int flags) -{ - const struct iax2_peer *peer = obj; - - return ast_str_hash(peer->name); -} - -/*! - * \note The only member of the peer passed here guaranteed to be set is the name field - */ -static int peer_cmp_cb(void *obj, void *arg, int flags) -{ - struct iax2_peer *peer = obj, *peer2 = arg; - - return !strcmp(peer->name, peer2->name) ? CMP_MATCH | CMP_STOP : 0; -} - -/*! - * \note The only member of the user passed here guaranteed to be set is the name field - */ -static int user_hash_cb(const void *obj, const int flags) -{ - const struct iax2_user *user = obj; - - return ast_str_hash(user->name); -} - -/*! - * \note The only member of the user passed here guaranteed to be set is the name field - */ -static int user_cmp_cb(void *obj, void *arg, int flags) -{ - struct iax2_user *user = obj, *user2 = arg; - - return !strcmp(user->name, user2->name) ? CMP_MATCH | CMP_STOP : 0; -} - -/*! - * \note This funtion calls realtime_peer -> reg_source_db -> iax2_poke_peer -> find_callno, - * so do not call it with a pvt lock held. - */ -static struct iax2_peer *find_peer(const char *name, int realtime) -{ - struct iax2_peer *peer = NULL; - struct iax2_peer tmp_peer = { - .name = name, - }; - - peer = ao2_find(peers, &tmp_peer, OBJ_POINTER); - - /* Now go for realtime if applicable */ - if(!peer && realtime) - peer = realtime_peer(name, NULL); - - return peer; -} - -static struct iax2_peer *peer_ref(struct iax2_peer *peer) -{ - ao2_ref(peer, +1); - return peer; -} - -static inline struct iax2_peer *peer_unref(struct iax2_peer *peer) -{ - ao2_ref(peer, -1); - return NULL; -} - -static inline struct iax2_user *user_ref(struct iax2_user *user) -{ - ao2_ref(user, +1); - return user; -} - -static inline struct iax2_user *user_unref(struct iax2_user *user) -{ - ao2_ref(user, -1); - return NULL; -} - -static int iax2_getpeername(struct sockaddr_in sin, char *host, int len) -{ - struct iax2_peer *peer = NULL; - int res = 0; - struct ao2_iterator i; - - i = ao2_iterator_init(peers, 0); - while ((peer = ao2_iterator_next(&i))) { - if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) && - (peer->addr.sin_port == sin.sin_port)) { - ast_copy_string(host, peer->name, len); - peer_unref(peer); - res = 1; - break; - } - peer_unref(peer); - } - - if (!peer) { - peer = realtime_peer(NULL, &sin); - if (peer) { - ast_copy_string(host, peer->name, len); - peer_unref(peer); - res = 1; - } - } - - return res; -} - -static void iax2_destroy_helper(struct chan_iax2_pvt *pvt) -{ - /* Decrement AUTHREQ count if needed */ - if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) { - struct iax2_user *user; - struct iax2_user tmp_user = { - .name = pvt->username, - }; - - user = ao2_find(users, &tmp_user, OBJ_POINTER); - if (user) { - ast_atomic_fetchadd_int(&user->curauthreq, -1); - user = user_unref(user); - } - - ast_clear_flag(pvt, IAX_MAXAUTHREQ); - } - - /* No more pings or lagrq's */ - AST_SCHED_DEL(sched, pvt->pingid); - AST_SCHED_DEL(sched, pvt->lagid); - AST_SCHED_DEL(sched, pvt->autoid); - AST_SCHED_DEL(sched, pvt->authid); - AST_SCHED_DEL(sched, pvt->initid); - AST_SCHED_DEL(sched, pvt->jbid); -} - -static void store_by_peercallno(struct chan_iax2_pvt *pvt) -{ - if (!pvt->peercallno) { - ast_log(LOG_ERROR, "This should not be called without a peer call number.\n"); - return; - } - - ao2_link(iax_peercallno_pvts, pvt); -} - -static void remove_by_peercallno(struct chan_iax2_pvt *pvt) -{ - if (!pvt->peercallno) { - ast_log(LOG_ERROR, "This should not be called without a peer call number.\n"); - return; - } - - ao2_unlink(iax_peercallno_pvts, pvt); -} - -static void update_max_trunk(void) -{ - int max = TRUNK_CALL_START; - int x; - - /* XXX Prolly don't need locks here XXX */ - for (x = TRUNK_CALL_START; x < ARRAY_LEN(iaxs) - 1; x++) { - if (iaxs[x]) { - max = x + 1; - } - } - - maxtrunkcall = max; - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "New max trunk callno is %d\n", max); -} - -static void iax2_frame_free(struct iax_frame *fr) -{ - AST_SCHED_DEL(sched, fr->retrans); - iax_frame_free(fr); -} - -static void iax2_destroy(int callno) -{ - struct chan_iax2_pvt *pvt; - struct ast_channel *owner; - -retry: - pvt = iaxs[callno]; - gettimeofday(&lastused[callno], NULL); - - owner = pvt ? pvt->owner : NULL; - - if (owner) { - if (ast_mutex_trylock(&owner->lock)) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Avoiding IAX destroy deadlock\n"); - DEADLOCK_AVOIDANCE(&iaxsl[callno]); - goto retry; - } - } - if (!owner && iaxs[callno]) { - AST_SCHED_DEL_SPINLOCK(sched, iaxs[callno]->lagid, &iaxsl[callno]); - AST_SCHED_DEL_SPINLOCK(sched, iaxs[callno]->pingid, &iaxsl[callno]); - iaxs[callno] = NULL; - } - - if (pvt) { - if (!owner) { - pvt->owner = NULL; - } else { - /* If there's an owner, prod it to give up */ - /* It is ok to use ast_queue_hangup() here instead of iax2_queue_hangup() - * because we already hold the owner channel lock. */ - ast_queue_hangup(owner); - } - - if (pvt->peercallno) { - remove_by_peercallno(pvt); - } - - if (!owner) { - ao2_ref(pvt, -1); - pvt = NULL; - } - } - - if (owner) { - ast_mutex_unlock(&owner->lock); - } - - if (callno & 0x4000) { - update_max_trunk(); - } -} - -static int scheduled_destroy(const void *vid) -{ - short callno = PTR_TO_CALLNO(vid); - ast_mutex_lock(&iaxsl[callno]); - if (iaxs[callno]) { - if (option_debug) { - ast_log(LOG_DEBUG, "Really destroying %d now...\n", callno); - } - iax2_destroy(callno); - } - ast_mutex_unlock(&iaxsl[callno]); - return 0; -} - -static void pvt_destructor(void *obj) -{ - struct chan_iax2_pvt *pvt = obj; - struct iax_frame *cur = NULL; - - iax2_destroy_helper(pvt); - - /* Already gone */ - ast_set_flag(pvt, IAX_ALREADYGONE); - - AST_LIST_LOCK(&iaxq.queue); - AST_LIST_TRAVERSE(&iaxq.queue, cur, list) { - /* Cancel any pending transmissions */ - if (cur->callno == pvt->callno) { - cur->retries = -1; - } - } - AST_LIST_UNLOCK(&iaxq.queue); - - if (pvt->reg) { - pvt->reg->callno = 0; - } - - if (!pvt->owner) { - jb_frame frame; - if (pvt->vars) { - ast_variables_destroy(pvt->vars); - pvt->vars = NULL; - } - - while (jb_getall(pvt->jb, &frame) == JB_OK) { - iax2_frame_free(frame.data); - } - - jb_destroy(pvt->jb); - ast_string_field_free_memory(pvt); - } -} - -static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, const char *host) -{ - struct chan_iax2_pvt *tmp; - jb_conf jbconf; - - if (!(tmp = ao2_alloc(sizeof(*tmp), pvt_destructor))) { - return NULL; - } - - if (ast_string_field_init(tmp, 32)) { - ao2_ref(tmp, -1); - tmp = NULL; - return NULL; - } - - tmp->prefs = prefs; - tmp->callno = 0; - tmp->peercallno = 0; - tmp->transfercallno = 0; - tmp->bridgecallno = 0; - tmp->pingid = -1; - tmp->lagid = -1; - tmp->autoid = -1; - tmp->authid = -1; - tmp->initid = -1; - - ast_string_field_set(tmp,exten, "s"); - ast_string_field_set(tmp,host, host); - - tmp->jb = jb_new(); - tmp->jbid = -1; - jbconf.max_jitterbuf = maxjitterbuffer; - jbconf.resync_threshold = resyncthreshold; - jbconf.max_contig_interp = maxjitterinterps; - jb_setconf(tmp->jb,&jbconf); - - return tmp; -} - -static struct iax_frame *iaxfrdup2(struct iax_frame *fr) -{ - struct iax_frame *new = iax_frame_new(DIRECTION_INGRESS, fr->af.datalen, fr->cacheable); - if (new) { - size_t afdatalen = new->afdatalen; - memcpy(new, fr, sizeof(*new)); - iax_frame_wrap(new, &fr->af); - new->afdatalen = afdatalen; - new->data = NULL; - new->datalen = 0; - new->direction = DIRECTION_INGRESS; - new->retrans = -1; - } - return new; -} - -#define NEW_PREVENT 0 -#define NEW_ALLOW 1 -#define NEW_FORCE 2 - -static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, struct chan_iax2_pvt *cur, int check_dcallno) -{ - if ((cur->addr.sin_addr.s_addr == sin->sin_addr.s_addr) && - (cur->addr.sin_port == sin->sin_port)) { - /* This is the main host */ - if ( (cur->peercallno == 0 || cur->peercallno == callno) && - (check_dcallno ? dcallno == cur->callno : 1) ) { - /* That's us. Be sure we keep track of the peer call number */ - return 1; - } - } - if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) && - (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) { - /* We're transferring */ - if ((dcallno == cur->callno) || (cur->transferring == TRANSFER_MEDIAPASS && cur->transfercallno == callno)) - return 1; - } - return 0; -} - -static void update_max_nontrunk(void) -{ - int max = 1; - int x; - /* XXX Prolly don't need locks here XXX */ - for (x=1;x<TRUNK_CALL_START - 1; x++) { - if (iaxs[x]) - max = x + 1; - } - maxnontrunkcall = max; - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "New max nontrunk callno is %d\n", max); -} - -static int make_trunk(unsigned short callno, int locked) -{ - int x; - int res= 0; - struct timeval now; - if (iaxs[callno]->oseqno) { - ast_log(LOG_WARNING, "Can't make trunk once a call has started!\n"); - return -1; - } - if (callno & TRUNK_CALL_START) { - ast_log(LOG_WARNING, "Call %d is already a trunk\n", callno); - return -1; - } - gettimeofday(&now, NULL); - for (x = TRUNK_CALL_START; x < ARRAY_LEN(iaxs) - 1; x++) { - ast_mutex_lock(&iaxsl[x]); - if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) { - /* Update the two timers that should have been started */ - /*! - * \note We delete these before switching the slot, because if - * they fire in the meantime, they will generate a warning. - */ - AST_SCHED_DEL(sched, iaxs[callno]->pingid); - AST_SCHED_DEL(sched, iaxs[callno]->lagid); - iaxs[x] = iaxs[callno]; - iaxs[x]->callno = x; - iaxs[callno] = NULL; - iaxs[x]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x); - iaxs[x]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x); - if (locked) - ast_mutex_unlock(&iaxsl[callno]); - res = x; - if (!locked) - ast_mutex_unlock(&iaxsl[x]); - break; - } - ast_mutex_unlock(&iaxsl[x]); - } - if (x >= ARRAY_LEN(iaxs) - 1) { - ast_log(LOG_WARNING, "Unable to trunk call: Insufficient space\n"); - return -1; - } - if (option_debug) - ast_log(LOG_DEBUG, "Made call %d into trunk call %d\n", callno, x); - /* We move this call from a non-trunked to a trunked call */ - update_max_trunk(); - update_max_nontrunk(); - return res; -} - -/*! - * \note Calling this function while holding another pvt lock can cause a deadlock. - */ -static int __find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd, int return_locked, int check_dcallno) -{ - int res = 0; - int x; - struct timeval now; - char host[80]; - - if (new <= NEW_ALLOW) { - if (callno) { - struct chan_iax2_pvt *pvt; - struct chan_iax2_pvt tmp_pvt = { - .callno = dcallno, - .peercallno = callno, - /* hack!! */ - .frames_received = check_dcallno, - }; - - memcpy(&tmp_pvt.addr, sin, sizeof(tmp_pvt.addr)); - - if ((pvt = ao2_find(iax_peercallno_pvts, &tmp_pvt, OBJ_POINTER))) { - if (return_locked) { - ast_mutex_lock(&iaxsl[pvt->callno]); - } - res = pvt->callno; - ao2_ref(pvt, -1); - pvt = NULL; - return res; - } - } - - /* This will occur on the first response to a message that we initiated, - * such as a PING. */ - if (dcallno) { - ast_mutex_lock(&iaxsl[dcallno]); - } - if (callno && dcallno && iaxs[dcallno] && !iaxs[dcallno]->peercallno && match(sin, callno, dcallno, iaxs[dcallno], check_dcallno)) { - iaxs[dcallno]->peercallno = callno; - res = dcallno; - store_by_peercallno(iaxs[dcallno]); - if (!res || !return_locked) { - ast_mutex_unlock(&iaxsl[dcallno]); - } - return res; - } - if (dcallno) { - ast_mutex_unlock(&iaxsl[dcallno]); - } - -#ifdef IAX_OLD_FIND - /* If we get here, we SHOULD NOT find a call structure for this - callno; if we do, it means that there is a call structure that - has a peer callno but did NOT get entered into the hash table, - which is bad. - - If we find a call structure using this old, slow method, output a log - message so we'll know about it. After a few months of leaving this in - place, if we don't hear about people seeing these messages, we can - remove this code for good. - */ - - for (x = 1; !res && x < maxnontrunkcall; x++) { - ast_mutex_lock(&iaxsl[x]); - if (iaxs[x]) { - /* Look for an exact match */ - if (match(sin, callno, dcallno, iaxs[x], check_dcallno)) { - res = x; - } - } - if (!res || !return_locked) - ast_mutex_unlock(&iaxsl[x]); - } - - for (x = TRUNK_CALL_START; !res && x < maxtrunkcall; x++) { - ast_mutex_lock(&iaxsl[x]); - if (iaxs[x]) { - /* Look for an exact match */ - if (match(sin, callno, dcallno, iaxs[x], check_dcallno)) { - res = x; - } - } - if (!res || !return_locked) - ast_mutex_unlock(&iaxsl[x]); - } - - if (res) { - ast_log(LOG_WARNING, "Old call search code found call number %d that was not in hash table!\n", res); - } -#endif - } - if (!res && (new >= NEW_ALLOW)) { - int start, found = 0; - - /* It may seem odd that we look through the peer list for a name for - * this *incoming* call. Well, it is weird. However, users don't - * have an IP address/port number that we can match against. So, - * this is just checking for a peer that has that IP/port and - * assuming that we have a user of the same name. This isn't always - * correct, but it will be changed if needed after authentication. */ - if (!iax2_getpeername(*sin, host, sizeof(host))) - snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); - - now = ast_tvnow(); - start = 2 + (ast_random() % (TRUNK_CALL_START - 1)); - for (x = start; 1; x++) { - if (x == TRUNK_CALL_START) { - x = 1; - continue; - } - - /* Find first unused call number that hasn't been used in a while */ - ast_mutex_lock(&iaxsl[x]); - if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) { - found = 1; - break; - } - ast_mutex_unlock(&iaxsl[x]); - - if (x == start - 1) { - break; - } - } - /* We've still got lock held if we found a spot */ - if (x == start - 1 && !found) { - ast_log(LOG_WARNING, "No more space\n"); - return 0; - } - iaxs[x] = new_iax(sin, host); - update_max_nontrunk(); - if (iaxs[x]) { - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Creating new call structure %d\n", x); - iaxs[x]->sockfd = sockfd; - iaxs[x]->addr.sin_port = sin->sin_port; - iaxs[x]->addr.sin_family = sin->sin_family; - iaxs[x]->addr.sin_addr.s_addr = sin->sin_addr.s_addr; - iaxs[x]->peercallno = callno; - iaxs[x]->callno = x; - iaxs[x]->pingtime = DEFAULT_RETRY_TIME; - iaxs[x]->expiry = min_reg_expire; - iaxs[x]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x); - iaxs[x]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x); - iaxs[x]->amaflags = amaflags; - ast_copy_flags(iaxs[x], (&globalflags), IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); - - ast_string_field_set(iaxs[x], accountcode, accountcode); - ast_string_field_set(iaxs[x], mohinterpret, mohinterpret); - ast_string_field_set(iaxs[x], mohsuggest, mohsuggest); - - if (iaxs[x]->peercallno) { - store_by_peercallno(iaxs[x]); - } - } else { - ast_log(LOG_WARNING, "Out of resources\n"); - ast_mutex_unlock(&iaxsl[x]); - return 0; - } - if (!return_locked) - ast_mutex_unlock(&iaxsl[x]); - res = x; - } - return res; -} - -static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd, int full_frame) { - - return __find_callno(callno, dcallno, sin, new, sockfd, 0, full_frame); -} - -static int find_callno_locked(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd, int full_frame) { - - return __find_callno(callno, dcallno, sin, new, sockfd, 1, full_frame); -} - -/*! - * \brief Queue a frame to a call's owning asterisk channel - * - * \pre This function assumes that iaxsl[callno] is locked when called. - * - * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno] - * was valid before calling it, it may no longer be valid after calling it. - * This function may unlock and lock the mutex associated with this callno, - * meaning that another thread may grab it and destroy the call. - */ -static int iax2_queue_frame(int callno, struct ast_frame *f) -{ - for (;;) { - if (iaxs[callno] && iaxs[callno]->owner) { - if (ast_mutex_trylock(&iaxs[callno]->owner->lock)) { - /* Avoid deadlock by pausing and trying again */ - DEADLOCK_AVOIDANCE(&iaxsl[callno]); - } else { - ast_queue_frame(iaxs[callno]->owner, f); - ast_mutex_unlock(&iaxs[callno]->owner->lock); - break; - } - } else - break; - } - return 0; -} - -/*! - * \brief Queue a hangup frame on the ast_channel owner - * - * This function queues a hangup frame on the owner of the IAX2 pvt struct that - * is active for the given call number. - * - * \pre Assumes lock for callno is already held. - * - * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno] - * was valid before calling it, it may no longer be valid after calling it. - * This function may unlock and lock the mutex associated with this callno, - * meaning that another thread may grab it and destroy the call. - */ -static int iax2_queue_hangup(int callno) -{ - for (;;) { - if (iaxs[callno] && iaxs[callno]->owner) { - if (ast_mutex_trylock(&iaxs[callno]->owner->lock)) { - /* Avoid deadlock by pausing and trying again */ - DEADLOCK_AVOIDANCE(&iaxsl[callno]); - } else { - ast_queue_hangup(iaxs[callno]->owner); - ast_mutex_unlock(&iaxs[callno]->owner->lock); - break; - } - } else - break; - } - return 0; -} - -/*! - * \brief Queue a control frame on the ast_channel owner - * - * This function queues a control frame on the owner of the IAX2 pvt struct that - * is active for the given call number. - * - * \pre Assumes lock for callno is already held. - * - * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno] - * was valid before calling it, it may no longer be valid after calling it. - * This function may unlock and lock the mutex associated with this callno, - * meaning that another thread may grab it and destroy the call. - */ -static int iax2_queue_control_data(int callno, - enum ast_control_frame_type control, const void *data, size_t datalen) -{ - for (;;) { - if (iaxs[callno] && iaxs[callno]->owner) { - if (ast_mutex_trylock(&iaxs[callno]->owner->lock)) { - /* Avoid deadlock by pausing and trying again */ - DEADLOCK_AVOIDANCE(&iaxsl[callno]); - } else { - ast_queue_control_data(iaxs[callno]->owner, control, data, datalen); - ast_mutex_unlock(&iaxs[callno]->owner->lock); - break; - } - } else - break; - } - return 0; -} -static void destroy_firmware(struct iax_firmware *cur) -{ - /* Close firmware */ - if (cur->fwh) { - munmap((void*)cur->fwh, ntohl(cur->fwh->datalen) + sizeof(*(cur->fwh))); - } - close(cur->fd); - free(cur); -} - -static int try_firmware(char *s) -{ - struct stat stbuf; - struct iax_firmware *cur; - int ifd; - int fd; - int res; - - struct ast_iax2_firmware_header *fwh, fwh2; - struct MD5Context md5; - unsigned char sum[16]; - unsigned char buf[1024]; - int len, chunk; - char *s2; - char *last; - s2 = alloca(strlen(s) + 100); - if (!s2) { - ast_log(LOG_WARNING, "Alloca failed!\n"); - return -1; - } - last = strrchr(s, '/'); - if (last) - last++; - else - last = s; - snprintf(s2, strlen(s) + 100, "/var/tmp/%s-%ld", last, (unsigned long)ast_random()); - res = stat(s, &stbuf); - if (res < 0) { - ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno)); - return -1; - } - /* Make sure it's not a directory */ - if (S_ISDIR(stbuf.st_mode)) - return -1; - ifd = open(s, O_RDONLY); - if (ifd < 0) { - ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno)); - return -1; - } - fd = open(s2, O_RDWR | O_CREAT | O_EXCL, 0600); - if (fd < 0) { - ast_log(LOG_WARNING, "Cannot open '%s' for writing: %s\n", s2, strerror(errno)); - close(ifd); - return -1; - } - /* Unlink our newly created file */ - unlink(s2); - - /* Now copy the firmware into it */ - len = stbuf.st_size; - while(len) { - chunk = len; - if (chunk > sizeof(buf)) - chunk = sizeof(buf); - res = read(ifd, buf, chunk); - if (res != chunk) { - ast_log(LOG_WARNING, "Only read %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno)); - close(ifd); - close(fd); - return -1; - } - res = write(fd, buf, chunk); - if (res != chunk) { - ast_log(LOG_WARNING, "Only write %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno)); - close(ifd); - close(fd); - return -1; - } - len -= chunk; - } - close(ifd); - /* Return to the beginning */ - lseek(fd, 0, SEEK_SET); - if ((res = read(fd, &fwh2, sizeof(fwh2))) != sizeof(fwh2)) { - ast_log(LOG_WARNING, "Unable to read firmware header in '%s'\n", s); - close(fd); - return -1; - } - if (ntohl(fwh2.magic) != IAX_FIRMWARE_MAGIC) { - ast_log(LOG_WARNING, "'%s' is not a valid firmware file\n", s); - close(fd); - return -1; - } - if (ntohl(fwh2.datalen) != (stbuf.st_size - sizeof(fwh2))) { - ast_log(LOG_WARNING, "Invalid data length in firmware '%s'\n", s); - close(fd); - return -1; - } - if (fwh2.devname[sizeof(fwh2.devname) - 1] || ast_strlen_zero((char *)fwh2.devname)) { - ast_log(LOG_WARNING, "No or invalid device type specified for '%s'\n", s); - close(fd); - return -1; - } - fwh = (struct ast_iax2_firmware_header*)mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (fwh == (void *) -1) { - ast_log(LOG_WARNING, "mmap failed: %s\n", strerror(errno)); - close(fd); - return -1; - } - MD5Init(&md5); - MD5Update(&md5, fwh->data, ntohl(fwh->datalen)); - MD5Final(sum, &md5); - if (memcmp(sum, fwh->chksum, sizeof(sum))) { - ast_log(LOG_WARNING, "Firmware file '%s' fails checksum\n", s); - munmap((void*)fwh, stbuf.st_size); - close(fd); - return -1; - } - cur = waresl.wares; - while(cur) { - if (!strcmp((char *)cur->fwh->devname, (char *)fwh->devname)) { - /* Found a candidate */ - if (cur->dead || (ntohs(cur->fwh->version) < ntohs(fwh->version))) - /* The version we have on loaded is older, load this one instead */ - break; - /* This version is no newer than what we have. Don't worry about it. - We'll consider it a proper load anyhow though */ - munmap((void*)fwh, stbuf.st_size); - close(fd); - return 0; - } - cur = cur->next; - } - if (!cur) { - /* Allocate a new one and link it */ - if ((cur = ast_calloc(1, sizeof(*cur)))) { - cur->fd = -1; - cur->next = waresl.wares; - waresl.wares = cur; - } - } - if (cur) { - if (cur->fwh) { - munmap((void*)cur->fwh, cur->mmaplen); - } - if (cur->fd > -1) - close(cur->fd); - cur->fwh = fwh; - cur->fd = fd; - cur->mmaplen = stbuf.st_size; - cur->dead = 0; - } - return 0; -} - -static int iax_check_version(char *dev) -{ - int res = 0; - struct iax_firmware *cur; - if (!ast_strlen_zero(dev)) { - ast_mutex_lock(&waresl.lock); - cur = waresl.wares; - while(cur) { - if (!strcmp(dev, (char *)cur->fwh->devname)) { - res = ntohs(cur->fwh->version); - break; - } - cur = cur->next; - } - ast_mutex_unlock(&waresl.lock); - } - return res; -} - -static int iax_firmware_append(struct iax_ie_data *ied, const unsigned char *dev, unsigned int desc) -{ - int res = -1; - unsigned int bs = desc & 0xff; - unsigned int start = (desc >> 8) & 0xffffff; - unsigned int bytes; - struct iax_firmware *cur; - if (!ast_strlen_zero((char *)dev) && bs) { - start *= bs; - ast_mutex_lock(&waresl.lock); - cur = waresl.wares; - while(cur) { - if (!strcmp((char *)dev, (char *)cur->fwh->devname)) { - iax_ie_append_int(ied, IAX_IE_FWBLOCKDESC, desc); - if (start < ntohl(cur->fwh->datalen)) { - bytes = ntohl(cur->fwh->datalen) - start; - if (bytes > bs) - bytes = bs; - iax_ie_append_raw(ied, IAX_IE_FWBLOCKDATA, cur->fwh->data + start, bytes); - } else { - bytes = 0; - iax_ie_append(ied, IAX_IE_FWBLOCKDATA); - } - if (bytes == bs) - res = 0; - else - res = 1; - break; - } - cur = cur->next; - } - ast_mutex_unlock(&waresl.lock); - } - return res; -} - - -static void reload_firmware(int unload) -{ - struct iax_firmware *cur, *curl, *curp; - DIR *fwd; - struct dirent *de; - char dir[256]; - char fn[256]; - /* Mark all as dead */ - ast_mutex_lock(&waresl.lock); - cur = waresl.wares; - while(cur) { - cur->dead = 1; - cur = cur->next; - } - - /* Now that we've freed them, load the new ones */ - if (!unload) { - snprintf(dir, sizeof(dir), "%s/firmware/iax", (char *)ast_config_AST_DATA_DIR); - fwd = opendir(dir); - if (fwd) { - while((de = readdir(fwd))) { - if (de->d_name[0] != '.') { - snprintf(fn, sizeof(fn), "%s/%s", dir, de->d_name); - if (!try_firmware(fn)) { - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Loaded firmware '%s'\n", de->d_name); - } - } - } - closedir(fwd); - } else - ast_log(LOG_WARNING, "Error opening firmware directory '%s': %s\n", dir, strerror(errno)); - } - - /* Clean up leftovers */ - cur = waresl.wares; - curp = NULL; - while(cur) { - curl = cur; - cur = cur->next; - if (curl->dead) { - if (curp) { - curp->next = cur; - } else { - waresl.wares = cur; - } - destroy_firmware(curl); - } else { - curp = cur; - } - } - ast_mutex_unlock(&waresl.lock); -} - -/*! - * \note This function assumes that iaxsl[callno] is locked when called. - * - * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno] - * was valid before calling it, it may no longer be valid after calling it. - * This function calls iax2_queue_frame(), which may unlock and lock the mutex - * associated with this callno, meaning that another thread may grab it and destroy the call. - */ -static int __do_deliver(void *data) -{ - /* Just deliver the packet by using queueing. This is called by - the IAX thread with the iaxsl lock held. */ - struct iax_frame *fr = data; - fr->retrans = -1; - ast_clear_flag(&fr->af, AST_FRFLAG_HAS_TIMING_INFO); - if (iaxs[fr->callno] && !ast_test_flag(iaxs[fr->callno], IAX_ALREADYGONE)) - iax2_queue_frame(fr->callno, &fr->af); - /* Free our iax frame */ - iax2_frame_free(fr); - /* And don't run again */ - return 0; -} - -static int handle_error(void) -{ - /* XXX Ideally we should figure out why an error occured and then abort those - rather than continuing to try. Unfortunately, the published interface does - not seem to work XXX */ -#if 0 - struct sockaddr_in *sin; - int res; - struct msghdr m; - struct sock_extended_err e; - m.msg_name = NULL; - m.msg_namelen = 0; - m.msg_iov = NULL; - m.msg_control = &e; - m.msg_controllen = sizeof(e); - m.msg_flags = 0; - res = recvmsg(netsocket, &m, MSG_ERRQUEUE); - if (res < 0) - ast_log(LOG_WARNING, "Error detected, but unable to read error: %s\n", strerror(errno)); - else { - if (m.msg_controllen) { - sin = (struct sockaddr_in *)SO_EE_OFFENDER(&e); - if (sin) - ast_log(LOG_WARNING, "Receive error from %s\n", ast_inet_ntoa(sin->sin_addr)); - else - ast_log(LOG_WARNING, "No address detected??\n"); - } else { - ast_log(LOG_WARNING, "Local error: %s\n", strerror(e.ee_errno)); - } - } -#endif - return 0; -} - -static int transmit_trunk(struct iax_frame *f, struct sockaddr_in *sin, int sockfd) -{ - int res; - res = sendto(sockfd, f->data, f->datalen, 0,(struct sockaddr *)sin, - sizeof(*sin)); - if (res < 0) { - if (option_debug) - ast_log(LOG_DEBUG, "Received error: %s\n", strerror(errno)); - handle_error(); - } else - res = 0; - return res; -} - -static int send_packet(struct iax_frame *f) -{ - int res; - int callno = f->callno; - - /* Don't send if there was an error, but return error instead */ - if (!callno || !iaxs[callno] || iaxs[callno]->error) - return -1; - - /* Called with iaxsl held */ - if (option_debug > 2 && iaxdebug) - ast_log(LOG_DEBUG, "Sending %d on %d/%d to %s:%d\n", f->ts, callno, iaxs[callno]->peercallno, ast_inet_ntoa(iaxs[callno]->addr.sin_addr), ntohs(iaxs[callno]->addr.sin_port)); - if (f->transfer) { - if (iaxdebug) - iax_showframe(f, NULL, 0, &iaxs[callno]->transfer, f->datalen - sizeof(struct ast_iax2_full_hdr)); - res = sendto(iaxs[callno]->sockfd, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[callno]->transfer, - sizeof(iaxs[callno]->transfer)); - } else { - if (iaxdebug) - iax_showframe(f, NULL, 0, &iaxs[callno]->addr, f->datalen - sizeof(struct ast_iax2_full_hdr)); - res = sendto(iaxs[callno]->sockfd, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[callno]->addr, - sizeof(iaxs[callno]->addr)); - } - if (res < 0) { - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Received error: %s\n", strerror(errno)); - handle_error(); - } else - res = 0; - return res; -} - -/*! - * \note Since this function calls iax2_queue_hangup(), the pvt struct - * for the given call number may disappear during its execution. - */ -static int iax2_predestroy(int callno) -{ - struct ast_channel *c; - struct chan_iax2_pvt *pvt = iaxs[callno]; - - if (!pvt) - return -1; - if (!ast_test_flag(pvt, IAX_ALREADYGONE)) { - iax2_destroy_helper(pvt); - ast_set_flag(pvt, IAX_ALREADYGONE); - } - c = pvt->owner; - if (c) { - c->tech_pvt = NULL; - iax2_queue_hangup(callno); - pvt->owner = NULL; - ast_module_unref(ast_module_info->self); - } - return 0; -} - -static int update_packet(struct iax_frame *f) -{ - /* Called with iaxsl lock held, and iaxs[callno] non-NULL */ - struct ast_iax2_full_hdr *fh = f->data; - /* Mark this as a retransmission */ - fh->dcallno = ntohs(IAX_FLAG_RETRANS | f->dcallno); - /* Update iseqno */ - f->iseqno = iaxs[f->callno]->iseqno; - fh->iseqno = f->iseqno; - return 0; -} - -static int attempt_transmit(const void *data); -static void __attempt_transmit(const void *data) -{ - /* Attempt to transmit the frame to the remote peer... - Called without iaxsl held. */ - struct iax_frame *f = (struct iax_frame *)data; - int freeme=0; - int callno = f->callno; - /* Make sure this call is still active */ - if (callno) - ast_mutex_lock(&iaxsl[callno]); - if (callno && iaxs[callno]) { - if ((f->retries < 0) /* Already ACK'd */ || - (f->retries >= max_retries) /* Too many attempts */) { - /* Record an error if we've transmitted too many times */ - if (f->retries >= max_retries) { - if (f->transfer) { - /* Transfer timeout */ - send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1); - } else if (f->final) { - if (f->final) - iax2_destroy(callno); - } else { - if (iaxs[callno]->owner) - ast_log(LOG_WARNING, "Max retries exceeded to host %s on %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", ast_inet_ntoa(iaxs[f->callno]->addr.sin_addr),iaxs[f->callno]->owner->name , f->af.frametype, f->af.subclass, f->ts, f->oseqno); - iaxs[callno]->error = ETIMEDOUT; - if (iaxs[callno]->owner) { - struct ast_frame fr = { 0, }; - /* Hangup the fd */ - fr.frametype = AST_FRAME_CONTROL; - fr.subclass = AST_CONTROL_HANGUP; - iax2_queue_frame(callno, &fr); // XXX - /* Remember, owner could disappear */ - if (iaxs[callno] && iaxs[callno]->owner) - iaxs[callno]->owner->hangupcause = AST_CAUSE_DESTINATION_OUT_OF_ORDER; - } else { - if (iaxs[callno]->reg) { - memset(&iaxs[callno]->reg->us, 0, sizeof(iaxs[callno]->reg->us)); - iaxs[callno]->reg->regstate = REG_STATE_TIMEOUT; - iaxs[callno]->reg->refresh = IAX_DEFAULT_REG_EXPIRE; - } - iax2_destroy(callno); - } - } - - } - freeme++; - } else { - /* Update it if it needs it */ - update_packet(f); - /* Attempt transmission */ - send_packet(f); - f->retries++; - /* Try again later after 10 times as long */ - f->retrytime *= 10; - if (f->retrytime > MAX_RETRY_TIME) - f->retrytime = MAX_RETRY_TIME; - /* Transfer messages max out at one second */ - if (f->transfer && (f->retrytime > 1000)) - f->retrytime = 1000; - f->retrans = iax2_sched_add(sched, f->retrytime, attempt_transmit, f); - } - } else { - /* Make sure it gets freed */ - f->retries = -1; - freeme++; - } - if (callno) - ast_mutex_unlock(&iaxsl[callno]); - /* Do not try again */ - if (freeme) { - /* Don't attempt delivery, just remove it from the queue */ - AST_LIST_LOCK(&iaxq.queue); - AST_LIST_REMOVE(&iaxq.queue, f, list); - iaxq.count--; - AST_LIST_UNLOCK(&iaxq.queue); - f->retrans = -1; - /* Free the IAX frame */ - iax2_frame_free(f); - } -} - -static int attempt_transmit(const void *data) -{ -#ifdef SCHED_MULTITHREADED - if (schedule_action(__attempt_transmit, data)) -#endif - __attempt_transmit(data); - return 0; -} - -static int iax2_prune_realtime(int fd, int argc, char *argv[]) -{ - struct iax2_peer *peer; - - if (argc != 4) - return RESULT_SHOWUSAGE; - if (!strcmp(argv[3],"all")) { - reload_config(); - ast_cli(fd, "OK cache is flushed.\n"); - } else if ((peer = find_peer(argv[3], 0))) { - if(ast_test_flag(peer, IAX_RTCACHEFRIENDS)) { - ast_set_flag(peer, IAX_RTAUTOCLEAR); - expire_registry(peer_ref(peer)); - ast_cli(fd, "OK peer %s was removed from the cache.\n", argv[3]); - } else { - ast_cli(fd, "SORRY peer %s is not eligible for this operation.\n", argv[3]); - } - peer_unref(peer); - } else { - ast_cli(fd, "SORRY peer %s was not found in the cache.\n", argv[3]); - } - - return RESULT_SUCCESS; -} - -static int iax2_test_losspct(int fd, int argc, char *argv[]) -{ - if (argc != 4) - return RESULT_SHOWUSAGE; - - test_losspct = atoi(argv[3]); - - return RESULT_SUCCESS; -} - -#ifdef IAXTESTS -static int iax2_test_late(int fd, int argc, char *argv[]) -{ - if (argc != 4) - return RESULT_SHOWUSAGE; - - test_late = atoi(argv[3]); - - return RESULT_SUCCESS; -} - -static int iax2_test_resync(int fd, int argc, char *argv[]) -{ - if (argc != 4) - return RESULT_SHOWUSAGE; - - test_resync = atoi(argv[3]); - - return RESULT_SUCCESS; -} - -static int iax2_test_jitter(int fd, int argc, char *argv[]) -{ - if (argc < 4 || argc > 5) - return RESULT_SHOWUSAGE; - - test_jit = atoi(argv[3]); - if (argc == 5) - test_jitpct = atoi(argv[4]); - - return RESULT_SUCCESS; -} -#endif /* IAXTESTS */ - -/*! \brief peer_status: Report Peer status in character string */ -/* returns 1 if peer is online, -1 if unmonitored */ -static int peer_status(struct iax2_peer *peer, char *status, int statuslen) -{ - int res = 0; - if (peer->maxms) { - if (peer->lastms < 0) { - ast_copy_string(status, "UNREACHABLE", statuslen); - } else if (peer->lastms > peer->maxms) { - snprintf(status, statuslen, "LAGGED (%d ms)", peer->lastms); - res = 1; - } else if (peer->lastms) { - snprintf(status, statuslen, "OK (%d ms)", peer->lastms); - res = 1; - } else { - ast_copy_string(status, "UNKNOWN", statuslen); - } - } else { - ast_copy_string(status, "Unmonitored", statuslen); - res = -1; - } - return res; -} - -/*! \brief Show one peer in detail */ -static int iax2_show_peer(int fd, int argc, char *argv[]) -{ - char status[30]; - char cbuf[256]; - struct iax2_peer *peer; - char codec_buf[512]; - int x = 0, codec = 0, load_realtime = 0; - - if (argc < 4) - return RESULT_SHOWUSAGE; - - load_realtime = (argc == 5 && !strcmp(argv[4], "load")) ? 1 : 0; - - peer = find_peer(argv[3], load_realtime); - if (peer) { - ast_cli(fd,"\n\n"); - ast_cli(fd, " * Name : %s\n", peer->name); - ast_cli(fd, " Secret : %s\n", ast_strlen_zero(peer->secret)?"<Not set>":"<Set>"); - ast_cli(fd, " Context : %s\n", peer->context); - ast_cli(fd, " Mailbox : %s\n", peer->mailbox); - ast_cli(fd, " Dynamic : %s\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes":"No"); - ast_cli(fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>")); - ast_cli(fd, " Expire : %d\n", peer->expire); - ast_cli(fd, " ACL : %s\n", (peer->ha?"Yes":"No")); - ast_cli(fd, " Addr->IP : %s Port %d\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", ntohs(peer->addr.sin_port)); - ast_cli(fd, " Defaddr->IP : %s Port %d\n", ast_inet_ntoa(peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port)); - ast_cli(fd, " Username : %s\n", peer->username); - ast_cli(fd, " Codecs : "); - ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability); - ast_cli(fd, "%s\n", codec_buf); - - ast_cli(fd, " Codec Order : ("); - for(x = 0; x < 32 ; x++) { - codec = ast_codec_pref_index(&peer->prefs,x); - if(!codec) - break; - ast_cli(fd, "%s", ast_getformatname(codec)); - if(x < 31 && ast_codec_pref_index(&peer->prefs,x+1)) - ast_cli(fd, "|"); - } - - if (!x) - ast_cli(fd, "none"); - ast_cli(fd, ")\n"); - - ast_cli(fd, " Status : "); - peer_status(peer, status, sizeof(status)); - ast_cli(fd, "%s\n",status); - ast_cli(fd, " Qualify : every %dms when OK, every %dms when UNREACHABLE (sample smoothing %s)\n", peer->pokefreqok, peer->pokefreqnotok, peer->smoothing ? "On" : "Off"); - ast_cli(fd,"\n"); - peer_unref(peer); - } else { - ast_cli(fd,"Peer %s not found.\n", argv[3]); - ast_cli(fd,"\n"); - } - - return RESULT_SUCCESS; -} - -static char *complete_iax2_show_peer(const char *line, const char *word, int pos, int state) -{ - int which = 0; - struct iax2_peer *peer; - char *res = NULL; - int wordlen = strlen(word); - struct ao2_iterator i; - - /* 0 - iax2; 1 - show; 2 - peer; 3 - <peername> */ - if (pos != 3) - return NULL; - - i = ao2_iterator_init(peers, 0); - while ((peer = ao2_iterator_next(&i))) { - if (!strncasecmp(peer->name, word, wordlen) && ++which > state) { - res = ast_strdup(peer->name); - peer_unref(peer); - break; - } - peer_unref(peer); - } - - return res; -} - -static int iax2_show_stats(int fd, int argc, char *argv[]) -{ - struct iax_frame *cur; - int cnt = 0, dead=0, final=0; - - if (argc != 3) - return RESULT_SHOWUSAGE; - - AST_LIST_LOCK(&iaxq.queue); - AST_LIST_TRAVERSE(&iaxq.queue, cur, list) { - if (cur->retries < 0) - dead++; - if (cur->final) - final++; - cnt++; - } - AST_LIST_UNLOCK(&iaxq.queue); - - ast_cli(fd, " IAX Statistics\n"); - ast_cli(fd, "---------------------\n"); - ast_cli(fd, "Outstanding frames: %d (%d ingress, %d egress)\n", iax_get_frames(), iax_get_iframes(), iax_get_oframes()); - ast_cli(fd, "Packets in transmit queue: %d dead, %d final, %d total\n\n", dead, final, cnt); - - return RESULT_SUCCESS; -} - -static int iax2_show_cache(int fd, int argc, char *argv[]) -{ - struct iax2_dpcache *dp; - char tmp[1024], *pc; - int s; - int x,y; - struct timeval tv; - gettimeofday(&tv, NULL); - ast_mutex_lock(&dpcache_lock); - dp = dpcache; - ast_cli(fd, "%-20.20s %-12.12s %-9.9s %-8.8s %s\n", "Peer/Context", "Exten", "Exp.", "Wait.", "Flags"); - while(dp) { - s = dp->expiry.tv_sec - tv.tv_sec; - tmp[0] = '\0'; - if (dp->flags & CACHE_FLAG_EXISTS) - strncat(tmp, "EXISTS|", sizeof(tmp) - strlen(tmp) - 1); - if (dp->flags & CACHE_FLAG_NONEXISTENT) - strncat(tmp, "NONEXISTENT|", sizeof(tmp) - strlen(tmp) - 1); - if (dp->flags & CACHE_FLAG_CANEXIST) - strncat(tmp, "CANEXIST|", sizeof(tmp) - strlen(tmp) - 1); - if (dp->flags & CACHE_FLAG_PENDING) - strncat(tmp, "PENDING|", sizeof(tmp) - strlen(tmp) - 1); - if (dp->flags & CACHE_FLAG_TIMEOUT) - strncat(tmp, "TIMEOUT|", sizeof(tmp) - strlen(tmp) - 1); - if (dp->flags & CACHE_FLAG_TRANSMITTED) - strncat(tmp, "TRANSMITTED|", sizeof(tmp) - strlen(tmp) - 1); - if (dp->flags & CACHE_FLAG_MATCHMORE) - strncat(tmp, "MATCHMORE|", sizeof(tmp) - strlen(tmp) - 1); - if (dp->flags & CACHE_FLAG_UNKNOWN) - strncat(tmp, "UNKNOWN|", sizeof(tmp) - strlen(tmp) - 1); - /* Trim trailing pipe */ - if (!ast_strlen_zero(tmp)) - tmp[strlen(tmp) - 1] = '\0'; - else - ast_copy_string(tmp, "(none)", sizeof(tmp)); - y=0; - pc = strchr(dp->peercontext, '@'); - if (!pc) - pc = dp->peercontext; - else - pc++; - for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++) - if (dp->waiters[x] > -1) - y++; - if (s > 0) - ast_cli(fd, "%-20.20s %-12.12s %-9d %-8d %s\n", pc, dp->exten, s, y, tmp); - else - ast_cli(fd, "%-20.20s %-12.12s %-9.9s %-8d %s\n", pc, dp->exten, "(expired)", y, tmp); - dp = dp->next; - } - ast_mutex_unlock(&dpcache_lock); - return RESULT_SUCCESS; -} - -static unsigned int calc_rxstamp(struct chan_iax2_pvt *p, unsigned int offset); - -static void unwrap_timestamp(struct iax_frame *fr) -{ - /* Video mini frames only encode the lower 15 bits of the session - * timestamp, but other frame types (e.g. audio) encode 16 bits. */ - const int ts_shift = (fr->af.frametype == AST_FRAME_VIDEO) ? 15 : 16; - const int lower_mask = (1 << ts_shift) - 1; - const int upper_mask = ~lower_mask; - const int last_upper = iaxs[fr->callno]->last & upper_mask; - - if ( (fr->ts & upper_mask) == last_upper ) { - const int x = fr->ts - iaxs[fr->callno]->last; - const int threshold = (ts_shift == 15) ? 25000 : 50000; - - if (x < -threshold) { - /* Sudden big jump backwards in timestamp: - What likely happened here is that miniframe timestamp has circled but we haven't - gotten the update from the main packet. We'll just pretend that we did, and - update the timestamp appropriately. */ - fr->ts = (last_upper + (1 << ts_shift)) | (fr->ts & lower_mask); - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "schedule_delivery: pushed forward timestamp\n"); - } else if (x > threshold) { - /* Sudden apparent big jump forwards in timestamp: - What's likely happened is this is an old miniframe belonging to the previous - top 15 or 16-bit timestamp that has turned up out of order. - Adjust the timestamp appropriately. */ - fr->ts = (last_upper - (1 << ts_shift)) | (fr->ts & lower_mask); - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "schedule_delivery: pushed back timestamp\n"); - } - } -} - -static int get_from_jb(const void *p); - -static void update_jbsched(struct chan_iax2_pvt *pvt) -{ - int when; - - when = ast_tvdiff_ms(ast_tvnow(), pvt->rxcore); - - when = jb_next(pvt->jb) - when; - - AST_SCHED_DEL(sched, pvt->jbid); - - if(when <= 0) { - /* XXX should really just empty until when > 0.. */ - when = 1; - } - - pvt->jbid = iax2_sched_add(sched, when, get_from_jb, CALLNO_TO_PTR(pvt->callno)); -} - -static void __get_from_jb(const void *p) -{ - int callno = PTR_TO_CALLNO(p); - struct chan_iax2_pvt *pvt = NULL; - struct iax_frame *fr; - jb_frame frame; - int ret; - long now; - long next; - struct timeval tv; - - /* Make sure we have a valid private structure before going on */ - ast_mutex_lock(&iaxsl[callno]); - pvt = iaxs[callno]; - if (!pvt) { - /* No go! */ - ast_mutex_unlock(&iaxsl[callno]); - return; - } - - pvt->jbid = -1; - - gettimeofday(&tv,NULL); - /* round up a millisecond since ast_sched_runq does; */ - /* prevents us from spinning while waiting for our now */ - /* to catch up with runq's now */ - tv.tv_usec += 1000; - - now = ast_tvdiff_ms(tv, pvt->rxcore); - - if(now >= (next = jb_next(pvt->jb))) { - ret = jb_get(pvt->jb,&frame,now,ast_codec_interp_len(pvt->voiceformat)); - switch(ret) { - case JB_OK: - fr = frame.data; - __do_deliver(fr); - /* __do_deliver() can cause the call to disappear */ - pvt = iaxs[callno]; - break; - case JB_INTERP: - { - struct ast_frame af = { 0, }; - - /* create an interpolation frame */ - af.frametype = AST_FRAME_VOICE; - af.subclass = pvt->voiceformat; - af.samples = frame.ms * 8; - af.src = "IAX2 JB interpolation"; - af.delivery = ast_tvadd(pvt->rxcore, ast_samp2tv(next, 1000)); - af.offset = AST_FRIENDLY_OFFSET; - - /* queue the frame: For consistency, we would call __do_deliver here, but __do_deliver wants an iax_frame, - * which we'd need to malloc, and then it would free it. That seems like a drag */ - if (!ast_test_flag(iaxs[callno], IAX_ALREADYGONE)) { - iax2_queue_frame(callno, &af); - /* iax2_queue_frame() could cause the call to disappear */ - pvt = iaxs[callno]; - } - } - break; - case JB_DROP: - iax2_frame_free(frame.data); - break; - case JB_NOFRAME: - case JB_EMPTY: - /* do nothing */ - break; - default: - /* shouldn't happen */ - break; - } - } - if (pvt) - update_jbsched(pvt); - ast_mutex_unlock(&iaxsl[callno]); -} - -static int get_from_jb(const void *data) -{ -#ifdef SCHED_MULTITHREADED - if (schedule_action(__get_from_jb, data)) -#endif - __get_from_jb(data); - return 0; -} - -/*! - * \note This function assumes fr->callno is locked - * - * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno] - * was valid before calling it, it may no longer be valid after calling it. - */ -static int schedule_delivery(struct iax_frame *fr, int updatehistory, int fromtrunk, unsigned int *tsout) -{ - int type, len; - int ret; - int needfree = 0; - struct ast_channel *owner = NULL; - struct ast_channel *bridge = NULL; - - /* Attempt to recover wrapped timestamps */ - unwrap_timestamp(fr); - - /* delivery time is sender's sent timestamp converted back into absolute time according to our clock */ - if ( !fromtrunk && !ast_tvzero(iaxs[fr->callno]->rxcore)) - fr->af.delivery = ast_tvadd(iaxs[fr->callno]->rxcore, ast_samp2tv(fr->ts, 1000)); - else { -#if 0 - if (option_debug) - ast_log(LOG_DEBUG, "schedule_delivery: set delivery to 0 as we don't have an rxcore yet, or frame is from trunk.\n"); -#endif - fr->af.delivery = ast_tv(0,0); - } - - type = JB_TYPE_CONTROL; - len = 0; - - if(fr->af.frametype == AST_FRAME_VOICE) { - type = JB_TYPE_VOICE; - len = ast_codec_get_samples(&fr->af) / 8; - } else if(fr->af.frametype == AST_FRAME_CNG) { - type = JB_TYPE_SILENCE; - } - - if ( (!ast_test_flag(iaxs[fr->callno], IAX_USEJITTERBUF)) ) { - if (tsout) - *tsout = fr->ts; - __do_deliver(fr); - return -1; - } - - if ((owner = iaxs[fr->callno]->owner)) - bridge = ast_bridged_channel(owner); - - /* if the user hasn't requested we force the use of the jitterbuffer, and we're bridged to - * a channel that can accept jitter, then flush and suspend the jb, and send this frame straight through */ - if ( (!ast_test_flag(iaxs[fr->callno], IAX_FORCEJITTERBUF)) && owner && bridge && (bridge->tech->properties & AST_CHAN_TP_WANTSJITTER) ) { - jb_frame frame; - - /* deliver any frames in the jb */ - while (jb_getall(iaxs[fr->callno]->jb, &frame) == JB_OK) { - __do_deliver(frame.data); - /* __do_deliver() can make the call disappear */ - if (!iaxs[fr->callno]) - return -1; - } - - jb_reset(iaxs[fr->callno]->jb); - - AST_SCHED_DEL(sched, iaxs[fr->callno]->jbid); - - /* deliver this frame now */ - if (tsout) - *tsout = fr->ts; - __do_deliver(fr); - return -1; - } - - /* insert into jitterbuffer */ - /* TODO: Perhaps we could act immediately if it's not droppable and late */ - ret = jb_put(iaxs[fr->callno]->jb, fr, type, len, fr->ts, - calc_rxstamp(iaxs[fr->callno],fr->ts)); - if (ret == JB_DROP) { - needfree++; - } else if (ret == JB_SCHED) { - update_jbsched(iaxs[fr->callno]); - } - if (tsout) - *tsout = fr->ts; - if (needfree) { - /* Free our iax frame */ - iax2_frame_free(fr); - return -1; - } - return 0; -} - -static int iax2_transmit(struct iax_frame *fr) -{ - /* Lock the queue and place this packet at the end */ - /* By setting this to 0, the network thread will send it for us, and - queue retransmission if necessary */ - fr->sentyet = 0; - AST_LIST_LOCK(&iaxq.queue); - AST_LIST_INSERT_TAIL(&iaxq.queue, fr, list); - iaxq.count++; - AST_LIST_UNLOCK(&iaxq.queue); - /* Wake up the network and scheduler thread */ - if (netthreadid != AST_PTHREADT_NULL) - pthread_kill(netthreadid, SIGURG); - signal_condition(&sched_lock, &sched_cond); - return 0; -} - - - -static int iax2_digit_begin(struct ast_channel *c, char digit) -{ - return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_BEGIN, digit, 0, NULL, 0, -1); -} - -static int iax2_digit_end(struct ast_channel *c, char digit, unsigned int duration) -{ - return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_END, digit, 0, NULL, 0, -1); -} - -static int iax2_sendtext(struct ast_channel *c, const char *text) -{ - - return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_TEXT, - 0, 0, (unsigned char *)text, strlen(text) + 1, -1); -} - -static int iax2_sendimage(struct ast_channel *c, struct ast_frame *img) -{ - return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_IMAGE, img->subclass, 0, img->data, img->datalen, -1); -} - -static int iax2_sendhtml(struct ast_channel *c, int subclass, const char *data, int datalen) -{ - return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_HTML, subclass, 0, (unsigned char *)data, datalen, -1); -} - -static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan) -{ - unsigned short callno = PTR_TO_CALLNO(newchan->tech_pvt); - ast_mutex_lock(&iaxsl[callno]); - if (iaxs[callno]) - iaxs[callno]->owner = newchan; - else - ast_log(LOG_WARNING, "Uh, this isn't a good sign...\n"); - ast_mutex_unlock(&iaxsl[callno]); - return 0; -} - -/*! - * \note This function calls reg_source_db -> iax2_poke_peer -> find_callno, - * so do not call this with a pvt lock held. - */ -static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin) -{ - struct ast_variable *var = NULL; - struct ast_variable *tmp; - struct iax2_peer *peer=NULL; - time_t regseconds = 0, nowtime; - int dynamic=0; - - if (peername) { - var = ast_load_realtime("iaxpeers", "name", peername, "host", "dynamic", NULL); - if (!var && sin) - var = ast_load_realtime("iaxpeers", "name", peername, "host", ast_inet_ntoa(sin->sin_addr), NULL); - } else if (sin) { - char porta[25]; - sprintf(porta, "%d", ntohs(sin->sin_port)); - var = ast_load_realtime("iaxpeers", "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", porta, NULL); - if (var) { - /* We'll need the peer name in order to build the structure! */ - for (tmp = var; tmp; tmp = tmp->next) { - if (!strcasecmp(tmp->name, "name")) - peername = tmp->value; - } - } - } - if (!var && peername) { /* Last ditch effort */ - var = ast_load_realtime("iaxpeers", "name", peername, NULL); - /*!\note - * If this one loaded something, then we need to ensure that the host - * field matched. The only reason why we can't have this as a criteria - * is because we only have the IP address and the host field might be - * set as a name (and the reverse PTR might not match). - */ - if (var && sin) { - for (tmp = var; tmp; tmp = tmp->next) { - if (!strcasecmp(tmp->name, "host")) { - struct ast_hostent ahp; - struct hostent *hp; - if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(&hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) { - /* No match */ - ast_variables_destroy(var); - var = NULL; - } - break; - } - } - } - } - if (!var) - return NULL; - - peer = build_peer(peername, var, NULL, ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS) ? 0 : 1); - - if (!peer) { - ast_variables_destroy(var); - return NULL; - } - - for (tmp = var; tmp; tmp = tmp->next) { - /* Make sure it's not a user only... */ - if (!strcasecmp(tmp->name, "type")) { - if (strcasecmp(tmp->value, "friend") && - strcasecmp(tmp->value, "peer")) { - /* Whoops, we weren't supposed to exist! */ - peer = peer_unref(peer); - break; - } - } else if (!strcasecmp(tmp->name, "regseconds")) { - ast_get_time_t(tmp->value, ®seconds, 0, NULL); - } else if (!strcasecmp(tmp->name, "ipaddr")) { - inet_aton(tmp->value, &(peer->addr.sin_addr)); - } else if (!strcasecmp(tmp->name, "port")) { - peer->addr.sin_port = htons(atoi(tmp->value)); - } else if (!strcasecmp(tmp->name, "host")) { - if (!strcasecmp(tmp->value, "dynamic")) - dynamic = 1; - } - } - - ast_variables_destroy(var); - - if (!peer) - return NULL; - - if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) { - ast_copy_flags(peer, &globalflags, IAX_RTAUTOCLEAR|IAX_RTCACHEFRIENDS); - if (ast_test_flag(peer, IAX_RTAUTOCLEAR)) { - if (peer->expire > -1) { - if (!ast_sched_del(sched, peer->expire)) { - peer->expire = -1; - peer_unref(peer); - } - } - peer->expire = iax2_sched_add(sched, (global_rtautoclear) * 1000, expire_registry, peer_ref(peer)); - if (peer->expire == -1) - peer_unref(peer); - } - ao2_link(peers, peer); - if (ast_test_flag(peer, IAX_DYNAMIC)) - reg_source_db(peer); - } else { - ast_set_flag(peer, IAX_TEMPONLY); - } - - if (!ast_test_flag(&globalflags, IAX_RTIGNOREREGEXPIRE) && dynamic) { - time(&nowtime); - if ((nowtime - regseconds) > IAX_DEFAULT_REG_EXPIRE) { - memset(&peer->addr, 0, sizeof(peer->addr)); - realtime_update_peer(peer->name, &peer->addr, 0); - if (option_debug) - ast_log(LOG_DEBUG, "realtime_peer: Bah, '%s' is expired (%d/%d/%d)!\n", - peername, (int)(nowtime - regseconds), (int)regseconds, (int)nowtime); - } - else { - if (option_debug) - ast_log(LOG_DEBUG, "realtime_peer: Registration for '%s' still active (%d/%d/%d)!\n", - peername, (int)(nowtime - regseconds), (int)regseconds, (int)nowtime); - } - } - - return peer; -} - -static struct iax2_user *realtime_user(const char *username, struct sockaddr_in *sin) -{ - struct ast_variable *var; - struct ast_variable *tmp; - struct iax2_user *user=NULL; - - var = ast_load_realtime("iaxusers", "name", username, "host", "dynamic", NULL); - if (!var) - var = ast_load_realtime("iaxusers", "name", username, "host", ast_inet_ntoa(sin->sin_addr), NULL); - if (!var && sin) { - char porta[6]; - snprintf(porta, sizeof(porta), "%d", ntohs(sin->sin_port)); - var = ast_load_realtime("iaxusers", "name", username, "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", porta, NULL); - if (!var) - var = ast_load_realtime("iaxusers", "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", porta, NULL); - } - if (!var) { /* Last ditch effort */ - var = ast_load_realtime("iaxusers", "name", username, NULL); - /*!\note - * If this one loaded something, then we need to ensure that the host - * field matched. The only reason why we can't have this as a criteria - * is because we only have the IP address and the host field might be - * set as a name (and the reverse PTR might not match). - */ - if (var) { - for (tmp = var; tmp; tmp = tmp->next) { - if (!strcasecmp(tmp->name, "host")) { - struct ast_hostent ahp; - struct hostent *hp; - if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(&hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) { - /* No match */ - ast_variables_destroy(var); - var = NULL; - } - break; - } - } - } - } - if (!var) - return NULL; - - tmp = var; - while(tmp) { - /* Make sure it's not a peer only... */ - if (!strcasecmp(tmp->name, "type")) { - if (strcasecmp(tmp->value, "friend") && - strcasecmp(tmp->value, "user")) { - return NULL; - } - } - tmp = tmp->next; - } - - user = build_user(username, var, NULL, !ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)); - - ast_variables_destroy(var); - - if (!user) - return NULL; - - if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) { - ast_set_flag(user, IAX_RTCACHEFRIENDS); - ao2_link(users, user); - } else { - ast_set_flag(user, IAX_TEMPONLY); - } - - return user; -} - -static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime) -{ - char port[10]; - char regseconds[20]; - - snprintf(regseconds, sizeof(regseconds), "%d", (int)regtime); - snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port)); - ast_update_realtime("iaxpeers", "name", peername, - "ipaddr", ast_inet_ntoa(sin->sin_addr), "port", port, - "regseconds", regseconds, NULL); -} - -struct create_addr_info { - int capability; - unsigned int flags; - int maxtime; - int encmethods; - int found; - int sockfd; - int adsi; - char username[80]; - char secret[80]; - char outkey[80]; - char timezone[80]; - char prefs[32]; - char context[AST_MAX_CONTEXT]; - char peercontext[AST_MAX_CONTEXT]; - char mohinterpret[MAX_MUSICCLASS]; - char mohsuggest[MAX_MUSICCLASS]; -}; - -static int create_addr(const char *peername, struct ast_channel *c, struct sockaddr_in *sin, struct create_addr_info *cai) -{ - struct ast_hostent ahp; - struct hostent *hp; - struct iax2_peer *peer; - int res = -1; - struct ast_codec_pref ourprefs; - - ast_clear_flag(cai, IAX_SENDANI | IAX_TRUNK); - cai->sockfd = defaultsockfd; - cai->maxtime = 0; - sin->sin_family = AF_INET; - - if (!(peer = find_peer(peername, 1))) { - cai->found = 0; - - hp = ast_gethostbyname(peername, &ahp); - if (hp) { - memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr)); - sin->sin_port = htons(IAX_DEFAULT_PORTNO); - /* use global iax prefs for unknown peer/user */ - /* But move the calling channel's native codec to the top of the preference list */ - memcpy(&ourprefs, &prefs, sizeof(ourprefs)); - if (c) - ast_codec_pref_prepend(&ourprefs, c->nativeformats, 1); - ast_codec_pref_convert(&ourprefs, cai->prefs, sizeof(cai->prefs), 1); - return 0; - } else { - ast_log(LOG_WARNING, "No such host: %s\n", peername); - return -1; - } - } - - cai->found = 1; - - /* if the peer has no address (current or default), return failure */ - if (!(peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr)) - goto return_unref; - - /* if the peer is being monitored and is currently unreachable, return failure */ - if (peer->maxms && ((peer->lastms > peer->maxms) || (peer->lastms < 0))) - goto return_unref; - - ast_copy_flags(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); - cai->maxtime = peer->maxms; - cai->capability = peer->capability; - cai->encmethods = peer->encmethods; - cai->sockfd = peer->sockfd; - cai->adsi = peer->adsi; - memcpy(&ourprefs, &peer->prefs, sizeof(ourprefs)); - /* Move the calling channel's native codec to the top of the preference list */ - if (c) { - ast_log(LOG_DEBUG, "prepending %x to prefs\n", c->nativeformats); - ast_codec_pref_prepend(&ourprefs, c->nativeformats, 1); - } - ast_codec_pref_convert(&ourprefs, cai->prefs, sizeof(cai->prefs), 1); - ast_copy_string(cai->context, peer->context, sizeof(cai->context)); - ast_copy_string(cai->peercontext, peer->peercontext, sizeof(cai->peercontext)); - ast_copy_string(cai->username, peer->username, sizeof(cai->username)); - ast_copy_string(cai->timezone, peer->zonetag, sizeof(cai->timezone)); - ast_copy_string(cai->outkey, peer->outkey, sizeof(cai->outkey)); - ast_copy_string(cai->mohinterpret, peer->mohinterpret, sizeof(cai->mohinterpret)); - ast_copy_string(cai->mohsuggest, peer->mohsuggest, sizeof(cai->mohsuggest)); - if (ast_strlen_zero(peer->dbsecret)) { - ast_copy_string(cai->secret, peer->secret, sizeof(cai->secret)); - } else { - char *family; - char *key = NULL; - - family = ast_strdupa(peer->dbsecret); - key = strchr(family, '/'); - if (key) - *key++ = '\0'; - if (!key || ast_db_get(family, key, cai->secret, sizeof(cai->secret))) { - ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", peer->dbsecret); - goto return_unref; - } - } - - if (peer->addr.sin_addr.s_addr) { - sin->sin_addr = peer->addr.sin_addr; - sin->sin_port = peer->addr.sin_port; - } else { - sin->sin_addr = peer->defaddr.sin_addr; - sin->sin_port = peer->defaddr.sin_port; - } - - res = 0; - -return_unref: - peer_unref(peer); - - return res; -} - -static void __auto_congest(const void *nothing) -{ - int callno = PTR_TO_CALLNO(nothing); - struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_CONGESTION }; - ast_mutex_lock(&iaxsl[callno]); - if (iaxs[callno]) { - iaxs[callno]->initid = -1; - iax2_queue_frame(callno, &f); - ast_log(LOG_NOTICE, "Auto-congesting call due to slow response\n"); - } - ast_mutex_unlock(&iaxsl[callno]); -} - -static int auto_congest(const void *data) -{ -#ifdef SCHED_MULTITHREADED - if (schedule_action(__auto_congest, data)) -#endif - __auto_congest(data); - return 0; -} - -static unsigned int iax2_datetime(const char *tz) -{ - time_t t; - struct tm tm; - unsigned int tmp; - time(&t); - if (!ast_strlen_zero(tz)) - ast_localtime(&t, &tm, tz); - else - ast_localtime(&t, &tm, NULL); - tmp = (tm.tm_sec >> 1) & 0x1f; /* 5 bits of seconds */ - tmp |= (tm.tm_min & 0x3f) << 5; /* 6 bits of minutes */ - tmp |= (tm.tm_hour & 0x1f) << 11; /* 5 bits of hours */ - tmp |= (tm.tm_mday & 0x1f) << 16; /* 5 bits of day of month */ - tmp |= ((tm.tm_mon + 1) & 0xf) << 21; /* 4 bits of month */ - tmp |= ((tm.tm_year - 100) & 0x7f) << 25; /* 7 bits of year */ - return tmp; -} - -struct parsed_dial_string { - char *username; - char *password; - char *key; - char *peer; - char *port; - char *exten; - char *context; - char *options; -}; - -static int send_apathetic_reply(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int command, int ts, unsigned char seqno) -{ - struct ast_iax2_full_hdr f = { .scallno = htons(0x8000 | callno), .dcallno = htons(dcallno), - .ts = htonl(ts), .iseqno = seqno, .oseqno = 0, .type = AST_FRAME_IAX, - .csub = compress_subclass(command) }; - - return sendto(defaultsockfd, &f, sizeof(f), 0, (struct sockaddr *)sin, sizeof(*sin)); -} - -/*! - * \brief Parses an IAX dial string into its component parts. - * \param data the string to be parsed - * \param pds pointer to a \c struct \c parsed_dial_string to be filled in - * \return nothing - * - * This function parses the string and fills the structure - * with pointers to its component parts. The input string - * will be modified. - * - * \note This function supports both plaintext passwords and RSA - * key names; if the password string is formatted as '[keyname]', - * then the keyname will be placed into the key field, and the - * password field will be set to NULL. - * - * \note The dial string format is: - * [username[:password]@]peer[:port][/exten[@@context]][/options] - */ -static void parse_dial_string(char *data, struct parsed_dial_string *pds) -{ - if (ast_strlen_zero(data)) - return; - - pds->peer = strsep(&data, "/"); - pds->exten = strsep(&data, "/"); - pds->options = data; - - if (pds->exten) { - data = pds->exten; - pds->exten = strsep(&data, "@"); - pds->context = data; - } - - if (strchr(pds->peer, '@')) { - data = pds->peer; - pds->username = strsep(&data, "@"); - pds->peer = data; - } - - if (pds->username) { - data = pds->username; - pds->username = strsep(&data, ":"); - pds->password = data; - } - - data = pds->peer; - pds->peer = strsep(&data, ":"); - pds->port = data; - - /* check for a key name wrapped in [] in the secret position, if found, - move it to the key field instead - */ - if (pds->password && (pds->password[0] == '[')) { - pds->key = ast_strip_quoted(pds->password, "[", "]"); - pds->password = NULL; - } -} - -static int iax2_call(struct ast_channel *c, char *dest, int timeout) -{ - struct sockaddr_in sin; - char *l=NULL, *n=NULL, *tmpstr; - struct iax_ie_data ied; - char *defaultrdest = "s"; - unsigned short callno = PTR_TO_CALLNO(c->tech_pvt); - struct parsed_dial_string pds; - struct create_addr_info cai; - - if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) { - ast_log(LOG_WARNING, "Channel is already in use (%s)?\n", c->name); - return -1; - } - - memset(&cai, 0, sizeof(cai)); - cai.encmethods = iax2_encryption; - - memset(&pds, 0, sizeof(pds)); - tmpstr = ast_strdupa(dest); - parse_dial_string(tmpstr, &pds); - - if (ast_strlen_zero(pds.peer)) { - ast_log(LOG_WARNING, "No peer provided in the IAX2 dial string '%s'\n", dest); - return -1; - } - - if (!pds.exten) { - pds.exten = defaultrdest; - } - - if (create_addr(pds.peer, c, &sin, &cai)) { - ast_log(LOG_WARNING, "No address associated with '%s'\n", pds.peer); - return -1; - } - - if (!pds.username && !ast_strlen_zero(cai.username)) - pds.username = cai.username; - if (!pds.password && !ast_strlen_zero(cai.secret)) - pds.password = cai.secret; - if (!pds.key && !ast_strlen_zero(cai.outkey)) - pds.key = cai.outkey; - if (!pds.context && !ast_strlen_zero(cai.peercontext)) - pds.context = cai.peercontext; - - /* Keep track of the context for outgoing calls too */ - ast_copy_string(c->context, cai.context, sizeof(c->context)); - - if (pds.port) - sin.sin_port = htons(atoi(pds.port)); - - l = c->cid.cid_num; - n = c->cid.cid_name; - - /* Now build request */ - memset(&ied, 0, sizeof(ied)); - - /* On new call, first IE MUST be IAX version of caller */ - iax_ie_append_short(&ied, IAX_IE_VERSION, IAX_PROTO_VERSION); - iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, pds.exten); - if (pds.options && strchr(pds.options, 'a')) { - /* Request auto answer */ - iax_ie_append(&ied, IAX_IE_AUTOANSWER); - } - - iax_ie_append_str(&ied, IAX_IE_CODEC_PREFS, cai.prefs); - - if (l) { - iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, l); - iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->cid.cid_pres); - } else { - if (n) - iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, c->cid.cid_pres); - else - iax_ie_append_byte(&ied, IAX_IE_CALLINGPRES, AST_PRES_NUMBER_NOT_AVAILABLE); - } - - iax_ie_append_byte(&ied, IAX_IE_CALLINGTON, c->cid.cid_ton); - iax_ie_append_short(&ied, IAX_IE_CALLINGTNS, c->cid.cid_tns); - - if (n) - iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, n); - if (ast_test_flag(iaxs[callno], IAX_SENDANI) && c->cid.cid_ani) - iax_ie_append_str(&ied, IAX_IE_CALLING_ANI, c->cid.cid_ani); - - if (!ast_strlen_zero(c->language)) - iax_ie_append_str(&ied, IAX_IE_LANGUAGE, c->language); - if (!ast_strlen_zero(c->cid.cid_dnid)) - iax_ie_append_str(&ied, IAX_IE_DNID, c->cid.cid_dnid); - if (!ast_strlen_zero(c->cid.cid_rdnis)) - iax_ie_append_str(&ied, IAX_IE_RDNIS, c->cid.cid_rdnis); - - if (pds.context) - iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, pds.context); - - if (pds.username) - iax_ie_append_str(&ied, IAX_IE_USERNAME, pds.username); - - if (cai.encmethods) - iax_ie_append_short(&ied, IAX_IE_ENCRYPTION, cai.encmethods); - - ast_mutex_lock(&iaxsl[callno]); - - if (!ast_strlen_zero(c->context)) - ast_string_field_set(iaxs[callno], context, c->context); - - if (pds.username) - ast_string_field_set(iaxs[callno], username, pds.username); - - iaxs[callno]->encmethods = cai.encmethods; - - iaxs[callno]->adsi = cai.adsi; - - ast_string_field_set(iaxs[callno], mohinterpret, cai.mohinterpret); - ast_string_field_set(iaxs[callno], mohsuggest, cai.mohsuggest); - - if (pds.key) - ast_string_field_set(iaxs[callno], outkey, pds.key); - if (pds.password) - ast_string_field_set(iaxs[callno], secret, pds.password); - - iax_ie_append_int(&ied, IAX_IE_FORMAT, c->nativeformats); - iax_ie_append_int(&ied, IAX_IE_CAPABILITY, iaxs[callno]->capability); - iax_ie_append_short(&ied, IAX_IE_ADSICPE, c->adsicpe); - iax_ie_append_int(&ied, IAX_IE_DATETIME, iax2_datetime(cai.timezone)); - - if (iaxs[callno]->maxtime) { - /* Initialize pingtime and auto-congest time */ - iaxs[callno]->pingtime = iaxs[callno]->maxtime / 2; - iaxs[callno]->initid = iax2_sched_add(sched, iaxs[callno]->maxtime * 2, auto_congest, CALLNO_TO_PTR(callno)); - } else if (autokill) { - iaxs[callno]->pingtime = autokill / 2; - iaxs[callno]->initid = iax2_sched_add(sched, autokill * 2, auto_congest, CALLNO_TO_PTR(callno)); - } - - /* send the command using the appropriate socket for this peer */ - iaxs[callno]->sockfd = cai.sockfd; - - /* Transmit the string in a "NEW" request */ - send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1); - - ast_mutex_unlock(&iaxsl[callno]); - ast_setstate(c, AST_STATE_RINGING); - - return 0; -} - -static int iax2_hangup(struct ast_channel *c) -{ - unsigned short callno = PTR_TO_CALLNO(c->tech_pvt); - struct iax_ie_data ied; - int alreadygone; - memset(&ied, 0, sizeof(ied)); - ast_mutex_lock(&iaxsl[callno]); - if (callno && iaxs[callno]) { - if (option_debug) - ast_log(LOG_DEBUG, "We're hanging up %s now...\n", c->name); - alreadygone = ast_test_flag(iaxs[callno], IAX_ALREADYGONE); - /* Send the hangup unless we have had a transmission error or are already gone */ - iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, (unsigned char)c->hangupcause); - if (!iaxs[callno]->error && !alreadygone) { - if (send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_HANGUP, 0, ied.buf, ied.pos, -1)) { - ast_log(LOG_WARNING, "No final packet could be sent for callno %d\n", callno); - } - if (!iaxs[callno]) { - ast_mutex_unlock(&iaxsl[callno]); - return 0; - } - } - /* Explicitly predestroy it */ - iax2_predestroy(callno); - /* If we were already gone to begin with, destroy us now */ - if (iaxs[callno] && alreadygone) { - if (option_debug) - ast_log(LOG_DEBUG, "Really destroying %s now...\n", c->name); - iax2_destroy(callno); - } else if (iaxs[callno]) { - ast_sched_add(sched, 10000, scheduled_destroy, CALLNO_TO_PTR(callno)); - } - } else if (c->tech_pvt) { - /* If this call no longer exists, but the channel still - * references it we need to set the channel's tech_pvt to null - * to avoid ast_channel_free() trying to free it. - */ - c->tech_pvt = NULL; - } - ast_mutex_unlock(&iaxsl[callno]); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Hungup '%s'\n", c->name); - return 0; -} - -/*! - * \note expects the pvt to be locked - */ -static int wait_for_peercallno(struct chan_iax2_pvt *pvt) -{ - unsigned short callno = pvt->callno; - - if (!pvt->peercallno) { - /* We don't know the remote side's call number, yet. :( */ - int count = 10; - while (count-- && pvt && !pvt->peercallno) { - DEADLOCK_AVOIDANCE(&iaxsl[callno]); - pvt = iaxs[callno]; - } - if (!pvt->peercallno) { - return -1; - } - } - - return 0; -} - -static int iax2_setoption(struct ast_channel *c, int option, void *data, int datalen) -{ - struct ast_option_header *h; - int res; - - switch (option) { - case AST_OPTION_TXGAIN: - case AST_OPTION_RXGAIN: - /* these two cannot be sent, because they require a result */ - errno = ENOSYS; - return -1; - default: - { - unsigned short callno = PTR_TO_CALLNO(c->tech_pvt); - struct chan_iax2_pvt *pvt; - - ast_mutex_lock(&iaxsl[callno]); - pvt = iaxs[callno]; - - if (wait_for_peercallno(pvt)) { - ast_mutex_unlock(&iaxsl[callno]); - return -1; - } - - ast_mutex_unlock(&iaxsl[callno]); - - if (!(h = ast_malloc(datalen + sizeof(*h)))) { - return -1; - } - - h->flag = AST_OPTION_FLAG_REQUEST; - h->option = htons(option); - memcpy(h->data, data, datalen); - res = send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_CONTROL, - AST_CONTROL_OPTION, 0, (unsigned char *) h, - datalen + sizeof(*h), -1); - free(h); - return res; - } - } -} - -static struct ast_frame *iax2_read(struct ast_channel *c) -{ - ast_log(LOG_NOTICE, "I should never be called!\n"); - return &ast_null_frame; -} - -static int iax2_start_transfer(unsigned short callno0, unsigned short callno1, int mediaonly) -{ - int res; - struct iax_ie_data ied0; - struct iax_ie_data ied1; - unsigned int transferid = (unsigned int)ast_random(); - memset(&ied0, 0, sizeof(ied0)); - iax_ie_append_addr(&ied0, IAX_IE_APPARENT_ADDR, &iaxs[callno1]->addr); - iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[callno1]->peercallno); - iax_ie_append_int(&ied0, IAX_IE_TRANSFERID, transferid); - - memset(&ied1, 0, sizeof(ied1)); - iax_ie_append_addr(&ied1, IAX_IE_APPARENT_ADDR, &iaxs[callno0]->addr); - iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[callno0]->peercallno); - iax_ie_append_int(&ied1, IAX_IE_TRANSFERID, transferid); - - res = send_command(iaxs[callno0], AST_FRAME_IAX, IAX_COMMAND_TXREQ, 0, ied0.buf, ied0.pos, -1); - if (res) - return -1; - res = send_command(iaxs[callno1], AST_FRAME_IAX, IAX_COMMAND_TXREQ, 0, ied1.buf, ied1.pos, -1); - if (res) - return -1; - iaxs[callno0]->transferring = mediaonly ? TRANSFER_MBEGIN : TRANSFER_BEGIN; - iaxs[callno1]->transferring = mediaonly ? TRANSFER_MBEGIN : TRANSFER_BEGIN; - return 0; -} - -static void lock_both(unsigned short callno0, unsigned short callno1) -{ - ast_mutex_lock(&iaxsl[callno0]); - while (ast_mutex_trylock(&iaxsl[callno1])) { - DEADLOCK_AVOIDANCE(&iaxsl[callno0]); - } -} - -static void unlock_both(unsigned short callno0, unsigned short callno1) -{ - ast_mutex_unlock(&iaxsl[callno1]); - ast_mutex_unlock(&iaxsl[callno0]); -} - -static enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms) -{ - struct ast_channel *cs[3]; - struct ast_channel *who, *other; - int to = -1; - int res = -1; - int transferstarted=0; - struct ast_frame *f; - unsigned short callno0 = PTR_TO_CALLNO(c0->tech_pvt); - unsigned short callno1 = PTR_TO_CALLNO(c1->tech_pvt); - struct timeval waittimer = {0, 0}, tv; - - lock_both(callno0, callno1); - if (!iaxs[callno0] || !iaxs[callno1]) { - unlock_both(callno0, callno1); - return AST_BRIDGE_FAILED; - } - /* Put them in native bridge mode */ - if (!flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1)) { - iaxs[callno0]->bridgecallno = callno1; - iaxs[callno1]->bridgecallno = callno0; - } - /* If the bridge got retried, don't queue up more packets - the transfer request will be retransmitted as necessary */ - if (iaxs[callno0]->transferring && iaxs[callno1]->transferring) { - transferstarted = 1; - } - unlock_both(callno0, callno1); - - /* If not, try to bridge until we can execute a transfer, if we can */ - cs[0] = c0; - cs[1] = c1; - for (/* ever */;;) { - /* Check in case we got masqueraded into */ - if ((c0->tech != &iax2_tech) || (c1->tech != &iax2_tech)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Can't masquerade, we're different...\n"); - /* Remove from native mode */ - if (c0->tech == &iax2_tech) { - ast_mutex_lock(&iaxsl[callno0]); - iaxs[callno0]->bridgecallno = 0; - ast_mutex_unlock(&iaxsl[callno0]); - } - if (c1->tech == &iax2_tech) { - ast_mutex_lock(&iaxsl[callno1]); - iaxs[callno1]->bridgecallno = 0; - ast_mutex_unlock(&iaxsl[callno1]); - } - return AST_BRIDGE_FAILED_NOWARN; - } - if (c0->nativeformats != c1->nativeformats) { - if (option_verbose > 2) { - char buf0[255]; - char buf1[255]; - ast_getformatname_multiple(buf0, sizeof(buf0) -1, c0->nativeformats); - ast_getformatname_multiple(buf1, sizeof(buf1) -1, c1->nativeformats); - ast_verbose(VERBOSE_PREFIX_3 "Operating with different codecs %d[%s] %d[%s] , can't native bridge...\n", c0->nativeformats, buf0, c1->nativeformats, buf1); - } - /* Remove from native mode */ - lock_both(callno0, callno1); - if (iaxs[callno0]) - iaxs[callno0]->bridgecallno = 0; - if (iaxs[callno1]) - iaxs[callno1]->bridgecallno = 0; - unlock_both(callno0, callno1); - return AST_BRIDGE_FAILED_NOWARN; - } - /* check if transfered and if we really want native bridging */ - if (!transferstarted && !ast_test_flag(iaxs[callno0], IAX_NOTRANSFER) && !ast_test_flag(iaxs[callno1], IAX_NOTRANSFER)) { - /* Try the transfer */ - if (iax2_start_transfer(callno0, callno1, (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1)) || - ast_test_flag(iaxs[callno0], IAX_TRANSFERMEDIA) | ast_test_flag(iaxs[callno1], IAX_TRANSFERMEDIA))) - ast_log(LOG_WARNING, "Unable to start the transfer\n"); - transferstarted = 1; - } - if ((iaxs[callno0]->transferring == TRANSFER_RELEASED) && (iaxs[callno1]->transferring == TRANSFER_RELEASED)) { - /* Call has been transferred. We're no longer involved */ - gettimeofday(&tv, NULL); - if (ast_tvzero(waittimer)) { - waittimer = tv; - } else if (tv.tv_sec - waittimer.tv_sec > IAX_LINGER_TIMEOUT) { - c0->_softhangup |= AST_SOFTHANGUP_DEV; - c1->_softhangup |= AST_SOFTHANGUP_DEV; - *fo = NULL; - *rc = c0; - res = AST_BRIDGE_COMPLETE; - break; - } - } - to = 1000; - who = ast_waitfor_n(cs, 2, &to); - if (timeoutms > -1) { - timeoutms -= (1000 - to); - if (timeoutms < 0) - timeoutms = 0; - } - if (!who) { - if (!timeoutms) { - res = AST_BRIDGE_RETRY; - break; - } - if (ast_check_hangup(c0) || ast_check_hangup(c1)) { - res = AST_BRIDGE_FAILED; - break; - } - continue; - } - f = ast_read(who); - if (!f) { - *fo = NULL; - *rc = who; - res = AST_BRIDGE_COMPLETE; - break; - } - if ((f->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) { - *fo = f; - *rc = who; - res = AST_BRIDGE_COMPLETE; - break; - } - other = (who == c0) ? c1 : c0; /* the 'other' channel */ - if ((f->frametype == AST_FRAME_VOICE) || - (f->frametype == AST_FRAME_TEXT) || - (f->frametype == AST_FRAME_VIDEO) || - (f->frametype == AST_FRAME_IMAGE) || - (f->frametype == AST_FRAME_DTMF)) { - /* monitored dtmf take out of the bridge. - * check if we monitor the specific source. - */ - int monitored_source = (who == c0) ? AST_BRIDGE_DTMF_CHANNEL_0 : AST_BRIDGE_DTMF_CHANNEL_1; - if (f->frametype == AST_FRAME_DTMF && (flags & monitored_source)) { - *rc = who; - *fo = f; - res = AST_BRIDGE_COMPLETE; - /* Remove from native mode */ - break; - } - /* everything else goes to the other side */ - ast_write(other, f); - } - ast_frfree(f); - /* Swap who gets priority */ - cs[2] = cs[0]; - cs[0] = cs[1]; - cs[1] = cs[2]; - } - lock_both(callno0, callno1); - if(iaxs[callno0]) - iaxs[callno0]->bridgecallno = 0; - if(iaxs[callno1]) - iaxs[callno1]->bridgecallno = 0; - unlock_both(callno0, callno1); - return res; -} - -static int iax2_answer(struct ast_channel *c) -{ - unsigned short callno = PTR_TO_CALLNO(c->tech_pvt); - if (option_debug) - ast_log(LOG_DEBUG, "Answering IAX2 call\n"); - return send_command_locked(callno, AST_FRAME_CONTROL, AST_CONTROL_ANSWER, 0, NULL, 0, -1); -} - -static int iax2_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen) -{ - unsigned short callno = PTR_TO_CALLNO(c->tech_pvt); - struct chan_iax2_pvt *pvt; - int res = 0; - - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Indicating condition %d\n", condition); - - ast_mutex_lock(&iaxsl[callno]); - pvt = iaxs[callno]; - - if (wait_for_peercallno(pvt)) { - res = -1; - goto done; - } - - switch (condition) { - case AST_CONTROL_HOLD: - if (strcasecmp(pvt->mohinterpret, "passthrough")) { - ast_moh_start(c, data, pvt->mohinterpret); - goto done; - } - break; - case AST_CONTROL_UNHOLD: - if (strcasecmp(pvt->mohinterpret, "passthrough")) { - ast_moh_stop(c); - goto done; - } - } - - res = send_command(pvt, AST_FRAME_CONTROL, condition, 0, data, datalen, -1); - -done: - ast_mutex_unlock(&iaxsl[callno]); - - return res; -} - -static int iax2_transfer(struct ast_channel *c, const char *dest) -{ - unsigned short callno = PTR_TO_CALLNO(c->tech_pvt); - struct iax_ie_data ied; - char tmp[256], *context; - ast_copy_string(tmp, dest, sizeof(tmp)); - context = strchr(tmp, '@'); - if (context) { - *context = '\0'; - context++; - } - memset(&ied, 0, sizeof(ied)); - iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, tmp); - if (context) - iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, context); - if (option_debug) - ast_log(LOG_DEBUG, "Transferring '%s' to '%s'\n", c->name, dest); - return send_command_locked(callno, AST_FRAME_IAX, IAX_COMMAND_TRANSFER, 0, ied.buf, ied.pos, -1); -} - -static int iax2_getpeertrunk(struct sockaddr_in sin) -{ - struct iax2_peer *peer; - int res = 0; - struct ao2_iterator i; - - i = ao2_iterator_init(peers, 0); - while ((peer = ao2_iterator_next(&i))) { - if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) && - (peer->addr.sin_port == sin.sin_port)) { - res = ast_test_flag(peer, IAX_TRUNK); - peer_unref(peer); - break; - } - peer_unref(peer); - } - - return res; -} - -/*! \brief Create new call, interface with the PBX core */ -static struct ast_channel *ast_iax2_new(int callno, int state, int capability) -{ - struct ast_channel *tmp; - struct chan_iax2_pvt *i; - struct ast_variable *v = NULL; - - if (!(i = iaxs[callno])) { - ast_log(LOG_WARNING, "No IAX2 pvt found for callno '%d' !\n", callno); - return NULL; - } - - /* Don't hold call lock */ - ast_mutex_unlock(&iaxsl[callno]); - tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "IAX2/%s-%d", i->host, i->callno); - ast_mutex_lock(&iaxsl[callno]); - if (i != iaxs[callno]) { - if (tmp) { - /* unlock and relock iaxsl[callno] to preserve locking order */ - ast_mutex_unlock(&iaxsl[callno]); - ast_channel_free(tmp); - ast_mutex_lock(&iaxsl[callno]); - } - return NULL; - } - - if (!tmp) - return NULL; - tmp->tech = &iax2_tech; - /* We can support any format by default, until we get restricted */ - tmp->nativeformats = capability; - tmp->readformat = tmp->rawreadformat = ast_best_codec(capability); - tmp->writeformat = tmp->rawwriteformat = ast_best_codec(capability); - tmp->tech_pvt = CALLNO_TO_PTR(i->callno); - - /* Don't use ast_set_callerid() here because it will - * generate a NewCallerID event before the NewChannel event */ - if (!ast_strlen_zero(i->ani)) - tmp->cid.cid_ani = ast_strdup(i->ani); - else - tmp->cid.cid_ani = ast_strdup(i->cid_num); - tmp->cid.cid_dnid = ast_strdup(i->dnid); - tmp->cid.cid_rdnis = ast_strdup(i->rdnis); - tmp->cid.cid_pres = i->calling_pres; - tmp->cid.cid_ton = i->calling_ton; - tmp->cid.cid_tns = i->calling_tns; - if (!ast_strlen_zero(i->language)) - ast_string_field_set(tmp, language, i->language); - if (!ast_strlen_zero(i->accountcode)) - ast_string_field_set(tmp, accountcode, i->accountcode); - if (i->amaflags) - tmp->amaflags = i->amaflags; - ast_copy_string(tmp->context, i->context, sizeof(tmp->context)); - ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten)); - if (i->adsi) - tmp->adsicpe = i->peeradsicpe; - else - tmp->adsicpe = AST_ADSI_UNAVAILABLE; - i->owner = tmp; - i->capability = capability; - - for (v = i->vars ; v ; v = v->next) - pbx_builtin_setvar_helper(tmp, v->name, v->value); - - if (state != AST_STATE_DOWN) { - if (ast_pbx_start(tmp)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); - ast_hangup(tmp); - i->owner = NULL; - return NULL; - } - } - - ast_module_ref(ast_module_info->self); - - return tmp; -} - -static unsigned int calc_txpeerstamp(struct iax2_trunk_peer *tpeer, int sampms, struct timeval *tv) -{ - unsigned long int mssincetx; /* unsigned to handle overflows */ - long int ms, pred; - - tpeer->trunkact = *tv; - mssincetx = ast_tvdiff_ms(*tv, tpeer->lasttxtime); - if (mssincetx > 5000 || ast_tvzero(tpeer->txtrunktime)) { - /* If it's been at least 5 seconds since the last time we transmitted on this trunk, reset our timers */ - tpeer->txtrunktime = *tv; - tpeer->lastsent = 999999; - } - /* Update last transmit time now */ - tpeer->lasttxtime = *tv; - - /* Calculate ms offset */ - ms = ast_tvdiff_ms(*tv, tpeer->txtrunktime); - /* Predict from last value */ - pred = tpeer->lastsent + sampms; - if (abs(ms - pred) < MAX_TIMESTAMP_SKEW) - ms = pred; - - /* We never send the same timestamp twice, so fudge a little if we must */ - if (ms == tpeer->lastsent) - ms = tpeer->lastsent + 1; - tpeer->lastsent = ms; - return ms; -} - -static unsigned int fix_peerts(struct timeval *tv, int callno, unsigned int ts) -{ - long ms; /* NOT unsigned */ - if (ast_tvzero(iaxs[callno]->rxcore)) { - /* Initialize rxcore time if appropriate */ - gettimeofday(&iaxs[callno]->rxcore, NULL); - /* Round to nearest 20ms so traces look pretty */ - iaxs[callno]->rxcore.tv_usec -= iaxs[callno]->rxcore.tv_usec % 20000; - } - /* Calculate difference between trunk and channel */ - ms = ast_tvdiff_ms(*tv, iaxs[callno]->rxcore); - /* Return as the sum of trunk time and the difference between trunk and real time */ - return ms + ts; -} - -static unsigned int calc_timestamp(struct chan_iax2_pvt *p, unsigned int ts, struct ast_frame *f) -{ - int ms; - int voice = 0; - int genuine = 0; - int adjust; - struct timeval *delivery = NULL; - - - /* What sort of frame do we have?: voice is self-explanatory - "genuine" means an IAX frame - things like LAGRQ/RP, PING/PONG, ACK - non-genuine frames are CONTROL frames [ringing etc], DTMF - The "genuine" distinction is needed because genuine frames must get a clock-based timestamp, - the others need a timestamp slaved to the voice frames so that they go in sequence - */ - if (f) { - if (f->frametype == AST_FRAME_VOICE) { - voice = 1; - delivery = &f->delivery; - } else if (f->frametype == AST_FRAME_IAX) { - genuine = 1; - } else if (f->frametype == AST_FRAME_CNG) { - p->notsilenttx = 0; - } - } - if (ast_tvzero(p->offset)) { - gettimeofday(&p->offset, NULL); - /* Round to nearest 20ms for nice looking traces */ - p->offset.tv_usec -= p->offset.tv_usec % 20000; - } - /* If the timestamp is specified, just send it as is */ - if (ts) - return ts; - /* If we have a time that the frame arrived, always use it to make our timestamp */ - if (delivery && !ast_tvzero(*delivery)) { - ms = ast_tvdiff_ms(*delivery, p->offset); - if (option_debug > 2 && iaxdebug) - ast_log(LOG_DEBUG, "calc_timestamp: call %d/%d: Timestamp slaved to delivery time\n", p->callno, iaxs[p->callno]->peercallno); - } else { - ms = ast_tvdiff_ms(ast_tvnow(), p->offset); - if (ms < 0) - ms = 0; - if (voice) { - /* On a voice frame, use predicted values if appropriate */ - if (p->notsilenttx && abs(ms - p->nextpred) <= MAX_TIMESTAMP_SKEW) { - /* Adjust our txcore, keeping voice and non-voice synchronized */ - /* AN EXPLANATION: - When we send voice, we usually send "calculated" timestamps worked out - on the basis of the number of samples sent. When we send other frames, - we usually send timestamps worked out from the real clock. - The problem is that they can tend to drift out of step because the - source channel's clock and our clock may not be exactly at the same rate. - We fix this by continuously "tweaking" p->offset. p->offset is "time zero" - for this call. Moving it adjusts timestamps for non-voice frames. - We make the adjustment in the style of a moving average. Each time we - adjust p->offset by 10% of the difference between our clock-derived - timestamp and the predicted timestamp. That's why you see "10000" - below even though IAX2 timestamps are in milliseconds. - The use of a moving average avoids offset moving too radically. - Generally, "adjust" roams back and forth around 0, with offset hardly - changing at all. But if a consistent different starts to develop it - will be eliminated over the course of 10 frames (200-300msecs) - */ - adjust = (ms - p->nextpred); - if (adjust < 0) - p->offset = ast_tvsub(p->offset, ast_samp2tv(abs(adjust), 10000)); - else if (adjust > 0) - p->offset = ast_tvadd(p->offset, ast_samp2tv(adjust, 10000)); - - if (!p->nextpred) { - p->nextpred = ms; /*f->samples / 8;*/ - if (p->nextpred <= p->lastsent) - p->nextpred = p->lastsent + 3; - } - ms = p->nextpred; - } else { - /* in this case, just use the actual - * time, since we're either way off - * (shouldn't happen), or we're ending a - * silent period -- and seed the next - * predicted time. Also, round ms to the - * next multiple of frame size (so our - * silent periods are multiples of - * frame size too) */ - - if (option_debug && iaxdebug && abs(ms - p->nextpred) > MAX_TIMESTAMP_SKEW ) - ast_log(LOG_DEBUG, "predicted timestamp skew (%u) > max (%u), using real ts instead.\n", - abs(ms - p->nextpred), MAX_TIMESTAMP_SKEW); - - if (f->samples >= 8) /* check to make sure we dont core dump */ - { - int diff = ms % (f->samples / 8); - if (diff) - ms += f->samples/8 - diff; - } - - p->nextpred = ms; - p->notsilenttx = 1; - } - } else if ( f->frametype == AST_FRAME_VIDEO ) { - /* - * IAX2 draft 03 says that timestamps MUST be in order. - * It does not say anything about several frames having the same timestamp - * When transporting video, we can have a frame that spans multiple iax packets - * (so called slices), so it would make sense to use the same timestamp for all of - * them - * We do want to make sure that frames don't go backwards though - */ - if ( (unsigned int)ms < p->lastsent ) - ms = p->lastsent; - } else { - /* On a dataframe, use last value + 3 (to accomodate jitter buffer shrinking) if appropriate unless - it's a genuine frame */ - if (genuine) { - /* genuine (IAX LAGRQ etc) must keep their clock-based stamps */ - if (ms <= p->lastsent) - ms = p->lastsent + 3; - } else if (abs(ms - p->lastsent) <= MAX_TIMESTAMP_SKEW) { - /* non-genuine frames (!?) (DTMF, CONTROL) should be pulled into the predicted stream stamps */ - ms = p->lastsent + 3; - } - } - } - p->lastsent = ms; - if (voice) - p->nextpred = p->nextpred + f->samples / 8; - return ms; -} - -static unsigned int calc_rxstamp(struct chan_iax2_pvt *p, unsigned int offset) -{ - /* Returns where in "receive time" we are. That is, how many ms - since we received (or would have received) the frame with timestamp 0 */ - int ms; -#ifdef IAXTESTS - int jit; -#endif /* IAXTESTS */ - /* Setup rxcore if necessary */ - if (ast_tvzero(p->rxcore)) { - p->rxcore = ast_tvnow(); - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "calc_rxstamp: call=%d: rxcore set to %d.%6.6d - %dms\n", - p->callno, (int)(p->rxcore.tv_sec), (int)(p->rxcore.tv_usec), offset); - p->rxcore = ast_tvsub(p->rxcore, ast_samp2tv(offset, 1000)); -#if 1 - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "calc_rxstamp: call=%d: works out as %d.%6.6d\n", - p->callno, (int)(p->rxcore.tv_sec),(int)( p->rxcore.tv_usec)); -#endif - } - - ms = ast_tvdiff_ms(ast_tvnow(), p->rxcore); -#ifdef IAXTESTS - if (test_jit) { - if (!test_jitpct || ((100.0 * ast_random() / (RAND_MAX + 1.0)) < test_jitpct)) { - jit = (int)((float)test_jit * ast_random() / (RAND_MAX + 1.0)); - if ((int)(2.0 * ast_random() / (RAND_MAX + 1.0))) - jit = -jit; - ms += jit; - } - } - if (test_late) { - ms += test_late; - test_late = 0; - } -#endif /* IAXTESTS */ - return ms; -} - -static struct iax2_trunk_peer *find_tpeer(struct sockaddr_in *sin, int fd) -{ - struct iax2_trunk_peer *tpeer; - - /* Finds and locks trunk peer */ - ast_mutex_lock(&tpeerlock); - for (tpeer = tpeers; tpeer; tpeer = tpeer->next) { - /* We don't lock here because tpeer->addr *never* changes */ - if (!inaddrcmp(&tpeer->addr, sin)) { - ast_mutex_lock(&tpeer->lock); - break; - } - } - if (!tpeer) { - if ((tpeer = ast_calloc(1, sizeof(*tpeer)))) { - ast_mutex_init(&tpeer->lock); - tpeer->lastsent = 9999; - memcpy(&tpeer->addr, sin, sizeof(tpeer->addr)); - tpeer->trunkact = ast_tvnow(); - ast_mutex_lock(&tpeer->lock); - tpeer->next = tpeers; - tpeer->sockfd = fd; - tpeers = tpeer; -#ifdef SO_NO_CHECK - setsockopt(tpeer->sockfd, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums)); -#endif - if (option_debug) - ast_log(LOG_DEBUG, "Created trunk peer for '%s:%d'\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port)); - } - } - ast_mutex_unlock(&tpeerlock); - return tpeer; -} - -static int iax2_trunk_queue(struct chan_iax2_pvt *pvt, struct iax_frame *fr) -{ - struct ast_frame *f; - struct iax2_trunk_peer *tpeer; - void *tmp, *ptr; - struct ast_iax2_meta_trunk_entry *met; - struct ast_iax2_meta_trunk_mini *mtm; - - f = &fr->af; - tpeer = find_tpeer(&pvt->addr, pvt->sockfd); - if (tpeer) { - if (tpeer->trunkdatalen + f->datalen + 4 >= tpeer->trunkdataalloc) { - /* Need to reallocate space */ - if (tpeer->trunkdataalloc < MAX_TRUNKDATA) { - if (!(tmp = ast_realloc(tpeer->trunkdata, tpeer->trunkdataalloc + DEFAULT_TRUNKDATA + IAX2_TRUNK_PREFACE))) { - ast_mutex_unlock(&tpeer->lock); - return -1; - } - - tpeer->trunkdataalloc += DEFAULT_TRUNKDATA; - tpeer->trunkdata = tmp; - if (option_debug) - ast_log(LOG_DEBUG, "Expanded trunk '%s:%d' to %d bytes\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), tpeer->trunkdataalloc); - } else { - ast_log(LOG_WARNING, "Maximum trunk data space exceeded to %s:%d\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port)); - ast_mutex_unlock(&tpeer->lock); - return -1; - } - } - - /* Append to meta frame */ - ptr = tpeer->trunkdata + IAX2_TRUNK_PREFACE + tpeer->trunkdatalen; - if (ast_test_flag(&globalflags, IAX_TRUNKTIMESTAMPS)) { - mtm = (struct ast_iax2_meta_trunk_mini *)ptr; - mtm->len = htons(f->datalen); - mtm->mini.callno = htons(pvt->callno); - mtm->mini.ts = htons(0xffff & fr->ts); - ptr += sizeof(struct ast_iax2_meta_trunk_mini); - tpeer->trunkdatalen += sizeof(struct ast_iax2_meta_trunk_mini); - } else { - met = (struct ast_iax2_meta_trunk_entry *)ptr; - /* Store call number and length in meta header */ - met->callno = htons(pvt->callno); - met->len = htons(f->datalen); - /* Advance pointers/decrease length past trunk entry header */ - ptr += sizeof(struct ast_iax2_meta_trunk_entry); - tpeer->trunkdatalen += sizeof(struct ast_iax2_meta_trunk_entry); - } - /* Copy actual trunk data */ - memcpy(ptr, f->data, f->datalen); - tpeer->trunkdatalen += f->datalen; - - tpeer->calls++; - ast_mutex_unlock(&tpeer->lock); - } - return 0; -} - -static void build_enc_keys(const unsigned char *digest, aes_encrypt_ctx *ecx, aes_decrypt_ctx *dcx) -{ - aes_encrypt_key128(digest, ecx); - aes_decrypt_key128(digest, dcx); -} - -static void memcpy_decrypt(unsigned char *dst, const unsigned char *src, int len, aes_decrypt_ctx *dcx) -{ -#if 0 - /* Debug with "fake encryption" */ - int x; - if (len % 16) - ast_log(LOG_WARNING, "len should be multiple of 16, not %d!\n", len); - for (x=0;x<len;x++) - dst[x] = src[x] ^ 0xff; -#else - unsigned char lastblock[16] = { 0 }; - int x; - while(len > 0) { - aes_decrypt(src, dst, dcx); - for (x=0;x<16;x++) - dst[x] ^= lastblock[x]; - memcpy(lastblock, src, sizeof(lastblock)); - dst += 16; - src += 16; - len -= 16; - } -#endif -} - -static void memcpy_encrypt(unsigned char *dst, const unsigned char *src, int len, aes_encrypt_ctx *ecx) -{ -#if 0 - /* Debug with "fake encryption" */ - int x; - if (len % 16) - ast_log(LOG_WARNING, "len should be multiple of 16, not %d!\n", len); - for (x=0;x<len;x++) - dst[x] = src[x] ^ 0xff; -#else - unsigned char curblock[16] = { 0 }; - int x; - while(len > 0) { - for (x=0;x<16;x++) - curblock[x] ^= src[x]; - aes_encrypt(curblock, dst, ecx); - memcpy(curblock, dst, sizeof(curblock)); - dst += 16; - src += 16; - len -= 16; - } -#endif -} - -static int decode_frame(aes_decrypt_ctx *dcx, struct ast_iax2_full_hdr *fh, struct ast_frame *f, int *datalen) -{ - int padding; - unsigned char *workspace; - - workspace = alloca(*datalen); - memset(f, 0, sizeof(*f)); - if (ntohs(fh->scallno) & IAX_FLAG_FULL) { - struct ast_iax2_full_enc_hdr *efh = (struct ast_iax2_full_enc_hdr *)fh; - if (*datalen < 16 + sizeof(struct ast_iax2_full_hdr)) - return -1; - /* Decrypt */ - memcpy_decrypt(workspace, efh->encdata, *datalen - sizeof(struct ast_iax2_full_enc_hdr), dcx); - - padding = 16 + (workspace[15] & 0xf); - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Decoding full frame with length %d (padding = %d) (15=%02x)\n", *datalen, padding, workspace[15]); - if (*datalen < padding + sizeof(struct ast_iax2_full_hdr)) - return -1; - - *datalen -= padding; - memcpy(efh->encdata, workspace + padding, *datalen - sizeof(struct ast_iax2_full_enc_hdr)); - f->frametype = fh->type; - if (f->frametype == AST_FRAME_VIDEO) { - f->subclass = uncompress_subclass(fh->csub & ~0x40) | ((fh->csub >> 6) & 0x1); - } else { - f->subclass = uncompress_subclass(fh->csub); - } - } else { - struct ast_iax2_mini_enc_hdr *efh = (struct ast_iax2_mini_enc_hdr *)fh; - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Decoding mini with length %d\n", *datalen); - if (*datalen < 16 + sizeof(struct ast_iax2_mini_hdr)) - return -1; - /* Decrypt */ - memcpy_decrypt(workspace, efh->encdata, *datalen - sizeof(struct ast_iax2_mini_enc_hdr), dcx); - padding = 16 + (workspace[15] & 0x0f); - if (*datalen < padding + sizeof(struct ast_iax2_mini_hdr)) - return -1; - *datalen -= padding; - memcpy(efh->encdata, workspace + padding, *datalen - sizeof(struct ast_iax2_mini_enc_hdr)); - } - return 0; -} - -static int encrypt_frame(aes_encrypt_ctx *ecx, struct ast_iax2_full_hdr *fh, unsigned char *poo, int *datalen) -{ - int padding; - unsigned char *workspace; - workspace = alloca(*datalen + 32); - if (!workspace) - return -1; - if (ntohs(fh->scallno) & IAX_FLAG_FULL) { - struct ast_iax2_full_enc_hdr *efh = (struct ast_iax2_full_enc_hdr *)fh; - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Encoding full frame %d/%d with length %d\n", fh->type, fh->csub, *datalen); - padding = 16 - ((*datalen - sizeof(struct ast_iax2_full_enc_hdr)) % 16); - padding = 16 + (padding & 0xf); - memcpy(workspace, poo, padding); - memcpy(workspace + padding, efh->encdata, *datalen - sizeof(struct ast_iax2_full_enc_hdr)); - workspace[15] &= 0xf0; - workspace[15] |= (padding & 0xf); - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Encoding full frame %d/%d with length %d + %d padding (15=%02x)\n", fh->type, fh->csub, *datalen, padding, workspace[15]); - *datalen += padding; - memcpy_encrypt(efh->encdata, workspace, *datalen - sizeof(struct ast_iax2_full_enc_hdr), ecx); - if (*datalen >= 32 + sizeof(struct ast_iax2_full_enc_hdr)) - memcpy(poo, workspace + *datalen - 32, 32); - } else { - struct ast_iax2_mini_enc_hdr *efh = (struct ast_iax2_mini_enc_hdr *)fh; - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Encoding mini frame with length %d\n", *datalen); - padding = 16 - ((*datalen - sizeof(struct ast_iax2_mini_enc_hdr)) % 16); - padding = 16 + (padding & 0xf); - memcpy(workspace, poo, padding); - memcpy(workspace + padding, efh->encdata, *datalen - sizeof(struct ast_iax2_mini_enc_hdr)); - workspace[15] &= 0xf0; - workspace[15] |= (padding & 0x0f); - *datalen += padding; - memcpy_encrypt(efh->encdata, workspace, *datalen - sizeof(struct ast_iax2_mini_enc_hdr), ecx); - if (*datalen >= 32 + sizeof(struct ast_iax2_mini_enc_hdr)) - memcpy(poo, workspace + *datalen - 32, 32); - } - return 0; -} - -static int decrypt_frame(int callno, struct ast_iax2_full_hdr *fh, struct ast_frame *f, int *datalen) -{ - int res=-1; - if (!ast_test_flag(iaxs[callno], IAX_KEYPOPULATED)) { - /* Search for possible keys, given secrets */ - struct MD5Context md5; - unsigned char digest[16]; - char *tmppw, *stringp; - - tmppw = ast_strdupa(iaxs[callno]->secret); - stringp = tmppw; - while ((tmppw = strsep(&stringp, ";"))) { - MD5Init(&md5); - MD5Update(&md5, (unsigned char *)iaxs[callno]->challenge, strlen(iaxs[callno]->challenge)); - MD5Update(&md5, (unsigned char *)tmppw, strlen(tmppw)); - MD5Final(digest, &md5); - build_enc_keys(digest, &iaxs[callno]->ecx, &iaxs[callno]->dcx); - res = decode_frame(&iaxs[callno]->dcx, fh, f, datalen); - if (!res) { - ast_set_flag(iaxs[callno], IAX_KEYPOPULATED); - break; - } - } - } else - res = decode_frame(&iaxs[callno]->dcx, fh, f, datalen); - return res; -} - -static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final) -{ - /* Queue a packet for delivery on a given private structure. Use "ts" for - timestamp, or calculate if ts is 0. Send immediately without retransmission - or delayed, with retransmission */ - struct ast_iax2_full_hdr *fh; - struct ast_iax2_mini_hdr *mh; - struct ast_iax2_video_hdr *vh; - struct { - struct iax_frame fr2; - unsigned char buffer[4096]; - } frb; - struct iax_frame *fr; - int res; - int sendmini=0; - unsigned int lastsent; - unsigned int fts; - - frb.fr2.afdatalen = sizeof(frb.buffer); - - if (!pvt) { - ast_log(LOG_WARNING, "No private structure for packet?\n"); - return -1; - } - - lastsent = pvt->lastsent; - - /* Calculate actual timestamp */ - fts = calc_timestamp(pvt, ts, f); - - /* Bail here if this is an "interp" frame; we don't want or need to send these placeholders out - * (the endpoint should detect the lost packet itself). But, we want to do this here, so that we - * increment the "predicted timestamps" for voice, if we're predecting */ - if(f->frametype == AST_FRAME_VOICE && f->datalen == 0) - return 0; - - - if ((ast_test_flag(pvt, IAX_TRUNK) || - (((fts & 0xFFFF0000L) == (lastsent & 0xFFFF0000L)) || - ((fts & 0xFFFF0000L) == ((lastsent + 0x10000) & 0xFFFF0000L)))) - /* High two bytes are the same on timestamp, or sending on a trunk */ && - (f->frametype == AST_FRAME_VOICE) - /* is a voice frame */ && - (f->subclass == pvt->svoiceformat) - /* is the same type */ ) { - /* Force immediate rather than delayed transmission */ - now = 1; - /* Mark that mini-style frame is appropriate */ - sendmini = 1; - } - if ( f->frametype == AST_FRAME_VIDEO ) { - /* - * If the lower 15 bits of the timestamp roll over, or if - * the video format changed then send a full frame. - * Otherwise send a mini video frame - */ - if (((fts & 0xFFFF8000L) == (pvt->lastvsent & 0xFFFF8000L)) && - ((f->subclass & ~0x1) == pvt->svideoformat) - ) { - now = 1; - sendmini = 1; - } else { - now = 0; - sendmini = 0; - } - pvt->lastvsent = fts; - } - /* Allocate an iax_frame */ - if (now) { - fr = &frb.fr2; - } else - fr = iax_frame_new(DIRECTION_OUTGRESS, ast_test_flag(pvt, IAX_ENCRYPTED) ? f->datalen + 32 : f->datalen, (f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_VIDEO)); - if (!fr) { - ast_log(LOG_WARNING, "Out of memory\n"); - return -1; - } - /* Copy our prospective frame into our immediate or retransmitted wrapper */ - iax_frame_wrap(fr, f); - - fr->ts = fts; - fr->callno = pvt->callno; - fr->transfer = transfer; - fr->final = final; - if (!sendmini) { - /* We need a full frame */ - if (seqno > -1) - fr->oseqno = seqno; - else - fr->oseqno = pvt->oseqno++; - fr->iseqno = pvt->iseqno; - fh = (struct ast_iax2_full_hdr *)(fr->af.data - sizeof(struct ast_iax2_full_hdr)); - fh->scallno = htons(fr->callno | IAX_FLAG_FULL); - fh->ts = htonl(fr->ts); - fh->oseqno = fr->oseqno; - if (transfer) { - fh->iseqno = 0; - } else - fh->iseqno = fr->iseqno; - /* Keep track of the last thing we've acknowledged */ - if (!transfer) - pvt->aseqno = fr->iseqno; - fh->type = fr->af.frametype & 0xFF; - if (fr->af.frametype == AST_FRAME_VIDEO) - fh->csub = compress_subclass(fr->af.subclass & ~0x1) | ((fr->af.subclass & 0x1) << 6); - else - fh->csub = compress_subclass(fr->af.subclass); - if (transfer) { - fr->dcallno = pvt->transfercallno; - } else - fr->dcallno = pvt->peercallno; - fh->dcallno = htons(fr->dcallno); - fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_full_hdr); - fr->data = fh; - fr->retries = 0; - /* Retry after 2x the ping time has passed */ - fr->retrytime = pvt->pingtime * 2; - if (fr->retrytime < MIN_RETRY_TIME) - fr->retrytime = MIN_RETRY_TIME; - if (fr->retrytime > MAX_RETRY_TIME) - fr->retrytime = MAX_RETRY_TIME; - /* Acks' don't get retried */ - if ((f->frametype == AST_FRAME_IAX) && (f->subclass == IAX_COMMAND_ACK)) - fr->retries = -1; - else if (f->frametype == AST_FRAME_VOICE) - pvt->svoiceformat = f->subclass; - else if (f->frametype == AST_FRAME_VIDEO) - pvt->svideoformat = f->subclass & ~0x1; - if (ast_test_flag(pvt, IAX_ENCRYPTED)) { - if (ast_test_flag(pvt, IAX_KEYPOPULATED)) { - if (iaxdebug) { - if (fr->transfer) - iax_showframe(fr, NULL, 2, &pvt->transfer, fr->datalen - sizeof(struct ast_iax2_full_hdr)); - else - iax_showframe(fr, NULL, 2, &pvt->addr, fr->datalen - sizeof(struct ast_iax2_full_hdr)); - } - encrypt_frame(&pvt->ecx, fh, pvt->semirand, &fr->datalen); - } else - ast_log(LOG_WARNING, "Supposed to send packet encrypted, but no key?\n"); - } - - if (now) { - res = send_packet(fr); - } else - res = iax2_transmit(fr); - } else { - if (ast_test_flag(pvt, IAX_TRUNK)) { - iax2_trunk_queue(pvt, fr); - res = 0; - } else if (fr->af.frametype == AST_FRAME_VIDEO) { - /* Video frame have no sequence number */ - fr->oseqno = -1; - fr->iseqno = -1; - vh = (struct ast_iax2_video_hdr *)(fr->af.data - sizeof(struct ast_iax2_video_hdr)); - vh->zeros = 0; - vh->callno = htons(0x8000 | fr->callno); - vh->ts = htons((fr->ts & 0x7FFF) | (fr->af.subclass & 0x1 ? 0x8000 : 0)); - fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_video_hdr); - fr->data = vh; - fr->retries = -1; - res = send_packet(fr); - } else { - /* Mini-frames have no sequence number */ - fr->oseqno = -1; - fr->iseqno = -1; - /* Mini frame will do */ - mh = (struct ast_iax2_mini_hdr *)(fr->af.data - sizeof(struct ast_iax2_mini_hdr)); - mh->callno = htons(fr->callno); - mh->ts = htons(fr->ts & 0xFFFF); - fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_mini_hdr); - fr->data = mh; - fr->retries = -1; - if (pvt->transferring == TRANSFER_MEDIAPASS) - fr->transfer = 1; - if (ast_test_flag(pvt, IAX_ENCRYPTED)) { - if (ast_test_flag(pvt, IAX_KEYPOPULATED)) { - encrypt_frame(&pvt->ecx, (struct ast_iax2_full_hdr *)mh, pvt->semirand, &fr->datalen); - } else - ast_log(LOG_WARNING, "Supposed to send packet encrypted, but no key?\n"); - } - res = send_packet(fr); - } - } - return res; -} - -static int iax2_show_users(int fd, int argc, char *argv[]) -{ - regex_t regexbuf; - int havepattern = 0; - -#define FORMAT "%-15.15s %-20.20s %-15.15s %-15.15s %-5.5s %-5.10s\n" -#define FORMAT2 "%-15.15s %-20.20s %-15.15d %-15.15s %-5.5s %-5.10s\n" - - struct iax2_user *user = NULL; - char auth[90]; - char *pstr = ""; - struct ao2_iterator i; - - switch (argc) { - case 5: - if (!strcasecmp(argv[3], "like")) { - if (regcomp(®exbuf, argv[4], REG_EXTENDED | REG_NOSUB)) - return RESULT_SHOWUSAGE; - havepattern = 1; - } else - return RESULT_SHOWUSAGE; - case 3: - break; - default: - return RESULT_SHOWUSAGE; - } - - ast_cli(fd, FORMAT, "Username", "Secret", "Authen", "Def.Context", "A/C","Codec Pref"); - i = ao2_iterator_init(users, 0); - for (user = ao2_iterator_next(&i); user; - user_unref(user), user = ao2_iterator_next(&i)) { - if (havepattern && regexec(®exbuf, user->name, 0, NULL, 0)) - continue; - - if (!ast_strlen_zero(user->secret)) { - ast_copy_string(auth,user->secret,sizeof(auth)); - } else if (!ast_strlen_zero(user->inkeys)) { - snprintf(auth, sizeof(auth), "Key: %-15.15s ", user->inkeys); - } else - ast_copy_string(auth, "-no secret-", sizeof(auth)); - - if(ast_test_flag(user,IAX_CODEC_NOCAP)) - pstr = "REQ Only"; - else if(ast_test_flag(user,IAX_CODEC_NOPREFS)) - pstr = "Disabled"; - else - pstr = ast_test_flag(user,IAX_CODEC_USER_FIRST) ? "Caller" : "Host"; - - ast_cli(fd, FORMAT2, user->name, auth, user->authmethods, - user->contexts ? user->contexts->context : context, - user->ha ? "Yes" : "No", pstr); - } - - if (havepattern) - regfree(®exbuf); - - return RESULT_SUCCESS; -#undef FORMAT -#undef FORMAT2 -} - -static int __iax2_show_peers(int manager, int fd, struct mansession *s, int argc, char *argv[]) -{ - regex_t regexbuf; - int havepattern = 0; - int total_peers = 0; - int online_peers = 0; - int offline_peers = 0; - int unmonitored_peers = 0; - struct ao2_iterator i; - -#define FORMAT2 "%-15.15s %-15.15s %s %-15.15s %-8s %s %-10s%s" -#define FORMAT "%-15.15s %-15.15s %s %-15.15s %-5d%s %s %-10s%s" - - struct iax2_peer *peer = NULL; - char name[256]; - int registeredonly=0; - char *term = manager ? "\r\n" : "\n"; - - switch (argc) { - case 6: - if (!strcasecmp(argv[3], "registered")) - registeredonly = 1; - else - return RESULT_SHOWUSAGE; - if (!strcasecmp(argv[4], "like")) { - if (regcomp(®exbuf, argv[5], REG_EXTENDED | REG_NOSUB)) - return RESULT_SHOWUSAGE; - havepattern = 1; - } else - return RESULT_SHOWUSAGE; - break; - case 5: - if (!strcasecmp(argv[3], "like")) { - if (regcomp(®exbuf, argv[4], REG_EXTENDED | REG_NOSUB)) - return RESULT_SHOWUSAGE; - havepattern = 1; - } else - return RESULT_SHOWUSAGE; - break; - case 4: - if (!strcasecmp(argv[3], "registered")) - registeredonly = 1; - else - return RESULT_SHOWUSAGE; - break; - case 3: - break; - default: - return RESULT_SHOWUSAGE; - } - - - if (s) - astman_append(s, FORMAT2, "Name/Username", "Host", " ", "Mask", "Port", " ", "Status", term); - else - ast_cli(fd, FORMAT2, "Name/Username", "Host", " ", "Mask", "Port", " ", "Status", term); - - i = ao2_iterator_init(peers, 0); - for (peer = ao2_iterator_next(&i); peer; - peer_unref(peer), peer = ao2_iterator_next(&i)) { - char nm[20]; - char status[20]; - char srch[2000]; - int retstatus; - - if (registeredonly && !peer->addr.sin_addr.s_addr) - continue; - if (havepattern && regexec(®exbuf, peer->name, 0, NULL, 0)) - continue; - - if (!ast_strlen_zero(peer->username)) - snprintf(name, sizeof(name), "%s/%s", peer->name, peer->username); - else - ast_copy_string(name, peer->name, sizeof(name)); - - retstatus = peer_status(peer, status, sizeof(status)); - if (retstatus > 0) - online_peers++; - else if (!retstatus) - offline_peers++; - else - unmonitored_peers++; - - ast_copy_string(nm, ast_inet_ntoa(peer->mask), sizeof(nm)); - - snprintf(srch, sizeof(srch), FORMAT, name, - peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", - ast_test_flag(peer, IAX_DYNAMIC) ? "(D)" : "(S)", - nm, - ntohs(peer->addr.sin_port), ast_test_flag(peer, IAX_TRUNK) ? "(T)" : " ", - peer->encmethods ? "(E)" : " ", status, term); - - if (s) - astman_append(s, FORMAT, name, - peer->addr.sin_addr.s_addr ? ast_inet_ntoa( peer->addr.sin_addr) : "(Unspecified)", - ast_test_flag(peer, IAX_DYNAMIC) ? "(D)" : "(S)", - nm, - ntohs(peer->addr.sin_port), ast_test_flag(peer, IAX_TRUNK) ? "(T)" : " ", - peer->encmethods ? "(E)" : " ", status, term); - else - ast_cli(fd, FORMAT, name, - peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", - ast_test_flag(peer, IAX_DYNAMIC) ? "(D)" : "(S)", - nm, - ntohs(peer->addr.sin_port), ast_test_flag(peer, IAX_TRUNK) ? "(T)" : " ", - peer->encmethods ? "(E)" : " ", status, term); - total_peers++; - } - - if (s) - astman_append(s,"%d iax2 peers [%d online, %d offline, %d unmonitored]%s", total_peers, online_peers, offline_peers, unmonitored_peers, term); - else - ast_cli(fd,"%d iax2 peers [%d online, %d offline, %d unmonitored]%s", total_peers, online_peers, offline_peers, unmonitored_peers, term); - - if (havepattern) - regfree(®exbuf); - - return RESULT_SUCCESS; -#undef FORMAT -#undef FORMAT2 -} - -static int iax2_show_threads(int fd, int argc, char *argv[]) -{ - struct iax2_thread *thread = NULL; - time_t t; - int threadcount = 0, dynamiccount = 0; - char type; - - if (argc != 3) - return RESULT_SHOWUSAGE; - - ast_cli(fd, "IAX2 Thread Information\n"); - time(&t); - ast_cli(fd, "Idle Threads:\n"); - AST_LIST_LOCK(&idle_list); - AST_LIST_TRAVERSE(&idle_list, thread, list) { -#ifdef DEBUG_SCHED_MULTITHREAD - ast_cli(fd, "Thread %d: state=%d, update=%d, actions=%d, func ='%s'\n", - thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions, thread->curfunc); -#else - ast_cli(fd, "Thread %d: state=%d, update=%d, actions=%d\n", - thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions); -#endif - threadcount++; - } - AST_LIST_UNLOCK(&idle_list); - ast_cli(fd, "Active Threads:\n"); - AST_LIST_LOCK(&active_list); - AST_LIST_TRAVERSE(&active_list, thread, list) { - if (thread->type == IAX_TYPE_DYNAMIC) - type = 'D'; - else - type = 'P'; -#ifdef DEBUG_SCHED_MULTITHREAD - ast_cli(fd, "Thread %c%d: state=%d, update=%d, actions=%d, func ='%s'\n", - type, thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions, thread->curfunc); -#else - ast_cli(fd, "Thread %c%d: state=%d, update=%d, actions=%d\n", - type, thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions); -#endif - threadcount++; - } - AST_LIST_UNLOCK(&active_list); - ast_cli(fd, "Dynamic Threads:\n"); - AST_LIST_LOCK(&dynamic_list); - AST_LIST_TRAVERSE(&dynamic_list, thread, list) { -#ifdef DEBUG_SCHED_MULTITHREAD - ast_cli(fd, "Thread %d: state=%d, update=%d, actions=%d, func ='%s'\n", - thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions, thread->curfunc); -#else - ast_cli(fd, "Thread %d: state=%d, update=%d, actions=%d\n", - thread->threadnum, thread->iostate, (int)(t - thread->checktime), thread->actions); -#endif - dynamiccount++; - } - AST_LIST_UNLOCK(&dynamic_list); - ast_cli(fd, "%d of %d threads accounted for with %d dynamic threads\n", threadcount, iaxthreadcount, dynamiccount); - return RESULT_SUCCESS; -} - -static int iax2_show_peers(int fd, int argc, char *argv[]) -{ - return __iax2_show_peers(0, fd, NULL, argc, argv); -} -static int manager_iax2_show_netstats(struct mansession *s, const struct message *m) -{ - ast_cli_netstats(s, -1, 0); - astman_append(s, "\r\n"); - return RESULT_SUCCESS; -} - -static int iax2_show_firmware(int fd, int argc, char *argv[]) -{ -#define FORMAT2 "%-15.15s %-15.15s %-15.15s\n" -#if !defined(__FreeBSD__) -#define FORMAT "%-15.15s %-15d %-15d\n" -#else /* __FreeBSD__ */ -#define FORMAT "%-15.15s %-15d %-15d\n" /* XXX 2.95 ? */ -#endif /* __FreeBSD__ */ - struct iax_firmware *cur; - if ((argc != 3) && (argc != 4)) - return RESULT_SHOWUSAGE; - ast_mutex_lock(&waresl.lock); - - ast_cli(fd, FORMAT2, "Device", "Version", "Size"); - for (cur = waresl.wares;cur;cur = cur->next) { - if ((argc == 3) || (!strcasecmp(argv[3], (char *)cur->fwh->devname))) - ast_cli(fd, FORMAT, cur->fwh->devname, ntohs(cur->fwh->version), - (int)ntohl(cur->fwh->datalen)); - } - ast_mutex_unlock(&waresl.lock); - return RESULT_SUCCESS; -#undef FORMAT -#undef FORMAT2 -} - -/* JDG: callback to display iax peers in manager */ -static int manager_iax2_show_peers(struct mansession *s, const struct message *m) -{ - char *a[] = { "iax2", "show", "users" }; - int ret; - const char *id = astman_get_header(m,"ActionID"); - - if (!ast_strlen_zero(id)) - astman_append(s, "ActionID: %s\r\n",id); - ret = __iax2_show_peers(1, -1, s, 3, a ); - astman_append(s, "\r\n\r\n" ); - return ret; -} /* /JDG */ - -static char *regstate2str(int regstate) -{ - switch(regstate) { - case REG_STATE_UNREGISTERED: - return "Unregistered"; - case REG_STATE_REGSENT: - return "Request Sent"; - case REG_STATE_AUTHSENT: - return "Auth. Sent"; - case REG_STATE_REGISTERED: - return "Registered"; - case REG_STATE_REJECTED: - return "Rejected"; - case REG_STATE_TIMEOUT: - return "Timeout"; - case REG_STATE_NOAUTH: - return "No Authentication"; - default: - return "Unknown"; - } -} - -static int iax2_show_registry(int fd, int argc, char *argv[]) -{ -#define FORMAT2 "%-20.20s %-6.6s %-10.10s %-20.20s %8.8s %s\n" -#define FORMAT "%-20.20s %-6.6s %-10.10s %-20.20s %8d %s\n" - struct iax2_registry *reg = NULL; - - char host[80]; - char perceived[80]; - if (argc != 3) - return RESULT_SHOWUSAGE; - ast_cli(fd, FORMAT2, "Host", "dnsmgr", "Username", "Perceived", "Refresh", "State"); - AST_LIST_LOCK(®istrations); - AST_LIST_TRAVERSE(®istrations, reg, entry) { - snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(reg->addr.sin_addr), ntohs(reg->addr.sin_port)); - if (reg->us.sin_addr.s_addr) - snprintf(perceived, sizeof(perceived), "%s:%d", ast_inet_ntoa(reg->us.sin_addr), ntohs(reg->us.sin_port)); - else - ast_copy_string(perceived, "<Unregistered>", sizeof(perceived)); - ast_cli(fd, FORMAT, host, - (reg->dnsmgr) ? "Y" : "N", - reg->username, perceived, reg->refresh, regstate2str(reg->regstate)); - } - AST_LIST_UNLOCK(®istrations); - return RESULT_SUCCESS; -#undef FORMAT -#undef FORMAT2 -} - -static int iax2_show_channels(int fd, int argc, char *argv[]) -{ -#define FORMAT2 "%-20.20s %-15.15s %-10.10s %-11.11s %-11.11s %-7.7s %-6.6s %-6.6s %s\n" -#define FORMAT "%-20.20s %-15.15s %-10.10s %5.5d/%5.5d %5.5d/%5.5d %-5.5dms %-4.4dms %-4.4dms %-6.6s\n" -#define FORMATB "%-20.20s %-15.15s %-10.10s %5.5d/%5.5d %5.5d/%5.5d [Native Bridged to ID=%5.5d]\n" - int x; - int numchans = 0; - - if (argc != 3) - return RESULT_SHOWUSAGE; - ast_cli(fd, FORMAT2, "Channel", "Peer", "Username", "ID (Lo/Rem)", "Seq (Tx/Rx)", "Lag", "Jitter", "JitBuf", "Format"); - for (x = 0; x < ARRAY_LEN(iaxs); x++) { - ast_mutex_lock(&iaxsl[x]); - if (iaxs[x]) { - int lag, jitter, localdelay; - jb_info jbinfo; - - if(ast_test_flag(iaxs[x], IAX_USEJITTERBUF)) { - jb_getinfo(iaxs[x]->jb, &jbinfo); - jitter = jbinfo.jitter; - localdelay = jbinfo.current - jbinfo.min; - } else { - jitter = -1; - localdelay = 0; - } - lag = iaxs[x]->remote_rr.delay; - ast_cli(fd, FORMAT, - iaxs[x]->owner ? iaxs[x]->owner->name : "(None)", - ast_inet_ntoa(iaxs[x]->addr.sin_addr), - S_OR(iaxs[x]->username, "(None)"), - iaxs[x]->callno, iaxs[x]->peercallno, - iaxs[x]->oseqno, iaxs[x]->iseqno, - lag, - jitter, - localdelay, - ast_getformatname(iaxs[x]->voiceformat) ); - numchans++; - } - ast_mutex_unlock(&iaxsl[x]); - } - ast_cli(fd, "%d active IAX channel%s\n", numchans, (numchans != 1) ? "s" : ""); - return RESULT_SUCCESS; -#undef FORMAT -#undef FORMAT2 -#undef FORMATB -} - -static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt) -{ - int x; - int numchans = 0; - for (x = 0; x < ARRAY_LEN(iaxs); x++) { - ast_mutex_lock(&iaxsl[x]); - if (iaxs[x]) { - int localjitter, localdelay, locallost, locallosspct, localdropped, localooo; - char *fmt; - jb_info jbinfo; - - if(ast_test_flag(iaxs[x], IAX_USEJITTERBUF)) { - jb_getinfo(iaxs[x]->jb, &jbinfo); - localjitter = jbinfo.jitter; - localdelay = jbinfo.current - jbinfo.min; - locallost = jbinfo.frames_lost; - locallosspct = jbinfo.losspct/1000; - localdropped = jbinfo.frames_dropped; - localooo = jbinfo.frames_ooo; - } else { - localjitter = -1; - localdelay = 0; - locallost = -1; - locallosspct = -1; - localdropped = 0; - localooo = -1; - } - if (limit_fmt) - fmt = "%-25.25s %4d %4d %4d %5d %3d %5d %4d %6d %4d %4d %5d %3d %5d %4d %6d\n"; - else - fmt = "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n"; - if (s) - - astman_append(s, fmt, - iaxs[x]->owner ? iaxs[x]->owner->name : "(None)", - iaxs[x]->pingtime, - localjitter, - localdelay, - locallost, - locallosspct, - localdropped, - localooo, - iaxs[x]->frames_received/1000, - iaxs[x]->remote_rr.jitter, - iaxs[x]->remote_rr.delay, - iaxs[x]->remote_rr.losscnt, - iaxs[x]->remote_rr.losspct, - iaxs[x]->remote_rr.dropped, - iaxs[x]->remote_rr.ooo, - iaxs[x]->remote_rr.packets/1000); - else - ast_cli(fd, fmt, - iaxs[x]->owner ? iaxs[x]->owner->name : "(None)", - iaxs[x]->pingtime, - localjitter, - localdelay, - locallost, - locallosspct, - localdropped, - localooo, - iaxs[x]->frames_received/1000, - iaxs[x]->remote_rr.jitter, - iaxs[x]->remote_rr.delay, - iaxs[x]->remote_rr.losscnt, - iaxs[x]->remote_rr.losspct, - iaxs[x]->remote_rr.dropped, - iaxs[x]->remote_rr.ooo, - iaxs[x]->remote_rr.packets/1000 - ); - numchans++; - } - ast_mutex_unlock(&iaxsl[x]); - } - return numchans; -} - -static int iax2_show_netstats(int fd, int argc, char *argv[]) -{ - int numchans = 0; - if (argc != 3) - return RESULT_SHOWUSAGE; - ast_cli(fd, " -------- LOCAL --------------------- -------- REMOTE --------------------\n"); - ast_cli(fd, "Channel RTT Jit Del Lost %% Drop OOO Kpkts Jit Del Lost %% Drop OOO Kpkts\n"); - numchans = ast_cli_netstats(NULL, fd, 1); - ast_cli(fd, "%d active IAX channel%s\n", numchans, (numchans != 1) ? "s" : ""); - return RESULT_SUCCESS; -} - -static int iax2_do_debug(int fd, int argc, char *argv[]) -{ - if (argc < 2 || argc > 3) - return RESULT_SHOWUSAGE; - iaxdebug = 1; - ast_cli(fd, "IAX2 Debugging Enabled\n"); - return RESULT_SUCCESS; -} - -static int iax2_do_trunk_debug(int fd, int argc, char *argv[]) -{ - if (argc < 3 || argc > 4) - return RESULT_SHOWUSAGE; - iaxtrunkdebug = 1; - ast_cli(fd, "IAX2 Trunk Debug Requested\n"); - return RESULT_SUCCESS; -} - -static int iax2_do_jb_debug(int fd, int argc, char *argv[]) -{ - if (argc < 3 || argc > 4) - return RESULT_SHOWUSAGE; - jb_setoutput(jb_error_output, jb_warning_output, jb_debug_output); - ast_cli(fd, "IAX2 Jitterbuffer Debugging Enabled\n"); - return RESULT_SUCCESS; -} - -static int iax2_no_debug(int fd, int argc, char *argv[]) -{ - if (argc < 3 || argc > 4) - return RESULT_SHOWUSAGE; - iaxdebug = 0; - ast_cli(fd, "IAX2 Debugging Disabled\n"); - return RESULT_SUCCESS; -} - -static int iax2_no_trunk_debug(int fd, int argc, char *argv[]) -{ - if (argc < 4 || argc > 5) - return RESULT_SHOWUSAGE; - iaxtrunkdebug = 0; - ast_cli(fd, "IAX2 Trunk Debugging Disabled\n"); - return RESULT_SUCCESS; -} - -static int iax2_no_jb_debug(int fd, int argc, char *argv[]) -{ - if (argc < 4 || argc > 5) - return RESULT_SHOWUSAGE; - jb_setoutput(jb_error_output, jb_warning_output, NULL); - jb_debug_output("\n"); - ast_cli(fd, "IAX2 Jitterbuffer Debugging Disabled\n"); - return RESULT_SUCCESS; -} - -static int iax2_write(struct ast_channel *c, struct ast_frame *f) -{ - unsigned short callno = PTR_TO_CALLNO(c->tech_pvt); - int res = -1; - ast_mutex_lock(&iaxsl[callno]); - if (iaxs[callno]) { - /* If there's an outstanding error, return failure now */ - if (!iaxs[callno]->error) { - if (ast_test_flag(iaxs[callno], IAX_ALREADYGONE)) - res = 0; - /* Don't waste bandwidth sending null frames */ - else if (f->frametype == AST_FRAME_NULL) - res = 0; - else if ((f->frametype == AST_FRAME_VOICE) && ast_test_flag(iaxs[callno], IAX_QUELCH)) - res = 0; - else if (!ast_test_flag(&iaxs[callno]->state, IAX_STATE_STARTED)) - res = 0; - else - /* Simple, just queue for transmission */ - res = iax2_send(iaxs[callno], f, 0, -1, 0, 0, 0); - } else { - if (option_debug) - ast_log(LOG_DEBUG, "Write error: %s\n", strerror(errno)); - } - } - /* If it's already gone, just return */ - ast_mutex_unlock(&iaxsl[callno]); - return res; -} - -static int __send_command(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno, - int now, int transfer, int final) -{ - struct ast_frame f = { 0, }; - - f.frametype = type; - f.subclass = command; - f.datalen = datalen; - f.src = __FUNCTION__; - f.data = (void *) data; - - return iax2_send(i, &f, ts, seqno, now, transfer, final); -} - -static int send_command(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno) -{ - return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0); -} - -static int send_command_locked(unsigned short callno, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno) -{ - int res; - ast_mutex_lock(&iaxsl[callno]); - res = send_command(iaxs[callno], type, command, ts, data, datalen, seqno); - ast_mutex_unlock(&iaxsl[callno]); - return res; -} - -/*! - * \note Since this function calls iax2_predestroy() -> iax2_queue_hangup(), - * the pvt struct for the given call number may disappear during its - * execution. - */ -static int send_command_final(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno) -{ - int call_num = i->callno; - /* It is assumed that the callno has already been locked */ - iax2_predestroy(i->callno); - if (!iaxs[call_num]) - return -1; - return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 1); -} - -static int send_command_immediate(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen, int seqno) -{ - return __send_command(i, type, command, ts, data, datalen, seqno, 1, 0, 0); -} - -static int send_command_transfer(struct chan_iax2_pvt *i, char type, int command, unsigned int ts, const unsigned char *data, int datalen) -{ - return __send_command(i, type, command, ts, data, datalen, 0, 0, 1, 0); -} - -static int apply_context(struct iax2_context *con, const char *context) -{ - while(con) { - if (!strcmp(con->context, context) || !strcmp(con->context, "*")) - return -1; - con = con->next; - } - return 0; -} - - -static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies) -{ - /* Start pessimistic */ - int res = -1; - int version = 2; - struct iax2_user *user = NULL, *best = NULL; - int bestscore = 0; - int gotcapability = 0; - struct ast_variable *v = NULL, *tmpvar = NULL; - struct ao2_iterator i; - - if (!iaxs[callno]) - return res; - if (ies->called_number) - ast_string_field_set(iaxs[callno], exten, ies->called_number); - if (ies->calling_number) { - ast_shrink_phone_number(ies->calling_number); - ast_string_field_set(iaxs[callno], cid_num, ies->calling_number); - } - if (ies->calling_name) - ast_string_field_set(iaxs[callno], cid_name, ies->calling_name); - if (ies->calling_ani) - ast_string_field_set(iaxs[callno], ani, ies->calling_ani); - if (ies->dnid) - ast_string_field_set(iaxs[callno], dnid, ies->dnid); - if (ies->rdnis) - ast_string_field_set(iaxs[callno], rdnis, ies->rdnis); - if (ies->called_context) - ast_string_field_set(iaxs[callno], context, ies->called_context); - if (ies->language) - ast_string_field_set(iaxs[callno], language, ies->language); - if (ies->username) - ast_string_field_set(iaxs[callno], username, ies->username); - if (ies->calling_ton > -1) - iaxs[callno]->calling_ton = ies->calling_ton; - if (ies->calling_tns > -1) - iaxs[callno]->calling_tns = ies->calling_tns; - if (ies->calling_pres > -1) - iaxs[callno]->calling_pres = ies->calling_pres; - if (ies->format) - iaxs[callno]->peerformat = ies->format; - if (ies->adsicpe) - iaxs[callno]->peeradsicpe = ies->adsicpe; - if (ies->capability) { - gotcapability = 1; - iaxs[callno]->peercapability = ies->capability; - } - if (ies->version) - version = ies->version; - - /* Use provided preferences until told otherwise for actual preferences */ - if(ies->codec_prefs) { - ast_codec_pref_convert(&iaxs[callno]->rprefs, ies->codec_prefs, 32, 0); - ast_codec_pref_convert(&iaxs[callno]->prefs, ies->codec_prefs, 32, 0); - } - - if (!gotcapability) - iaxs[callno]->peercapability = iaxs[callno]->peerformat; - if (version > IAX_PROTO_VERSION) { - ast_log(LOG_WARNING, "Peer '%s' has too new a protocol version (%d) for me\n", - ast_inet_ntoa(sin->sin_addr), version); - return res; - } - /* Search the userlist for a compatible entry, and fill in the rest */ - i = ao2_iterator_init(users, 0); - while ((user = ao2_iterator_next(&i))) { - if ((ast_strlen_zero(iaxs[callno]->username) || /* No username specified */ - !strcmp(iaxs[callno]->username, user->name)) /* Or this username specified */ - && ast_apply_ha(user->ha, sin) /* Access is permitted from this IP */ - && (ast_strlen_zero(iaxs[callno]->context) || /* No context specified */ - apply_context(user->contexts, iaxs[callno]->context))) { /* Context is permitted */ - if (!ast_strlen_zero(iaxs[callno]->username)) { - /* Exact match, stop right now. */ - if (best) - user_unref(best); - best = user; - break; - } else if (ast_strlen_zero(user->secret) && ast_strlen_zero(user->dbsecret) && ast_strlen_zero(user->inkeys)) { - /* No required authentication */ - if (user->ha) { - /* There was host authentication and we passed, bonus! */ - if (bestscore < 4) { - bestscore = 4; - if (best) - user_unref(best); - best = user; - continue; - } - } else { - /* No host access, but no secret, either, not bad */ - if (bestscore < 3) { - bestscore = 3; - if (best) - user_unref(best); - best = user; - continue; - } - } - } else { - if (user->ha) { - /* Authentication, but host access too, eh, it's something.. */ - if (bestscore < 2) { - bestscore = 2; - if (best) - user_unref(best); - best = user; - continue; - } - } else { - /* Authentication and no host access... This is our baseline */ - if (bestscore < 1) { - bestscore = 1; - if (best) - user_unref(best); - best = user; - continue; - } - } - } - } - user_unref(user); - } - user = best; - if (!user && !ast_strlen_zero(iaxs[callno]->username)) { - user = realtime_user(iaxs[callno]->username, sin); - if (user && !ast_strlen_zero(iaxs[callno]->context) && /* No context specified */ - !apply_context(user->contexts, iaxs[callno]->context)) { /* Context is permitted */ - user = user_unref(user); - } - } - if (user) { - /* We found our match (use the first) */ - /* copy vars */ - for (v = user->vars ; v ; v = v->next) { - if((tmpvar = ast_variable_new(v->name, v->value))) { - tmpvar->next = iaxs[callno]->vars; - iaxs[callno]->vars = tmpvar; - } - } - /* If a max AUTHREQ restriction is in place, activate it */ - if (user->maxauthreq > 0) - ast_set_flag(iaxs[callno], IAX_MAXAUTHREQ); - iaxs[callno]->prefs = user->prefs; - ast_copy_flags(iaxs[callno], user, IAX_CODEC_USER_FIRST); - ast_copy_flags(iaxs[callno], user, IAX_CODEC_NOPREFS); - ast_copy_flags(iaxs[callno], user, IAX_CODEC_NOCAP); - iaxs[callno]->encmethods = user->encmethods; - /* Store the requested username if not specified */ - if (ast_strlen_zero(iaxs[callno]->username)) - ast_string_field_set(iaxs[callno], username, user->name); - /* Store whether this is a trunked call, too, of course, and move if appropriate */ - ast_copy_flags(iaxs[callno], user, IAX_TRUNK); - iaxs[callno]->capability = user->capability; - /* And use the default context */ - if (ast_strlen_zero(iaxs[callno]->context)) { - if (user->contexts) - ast_string_field_set(iaxs[callno], context, user->contexts->context); - else - ast_string_field_set(iaxs[callno], context, context); - } - /* And any input keys */ - ast_string_field_set(iaxs[callno], inkeys, user->inkeys); - /* And the permitted authentication methods */ - iaxs[callno]->authmethods = user->authmethods; - iaxs[callno]->adsi = user->adsi; - /* If the user has callerid, override the remote caller id. */ - if (ast_test_flag(user, IAX_HASCALLERID)) { - iaxs[callno]->calling_tns = 0; - iaxs[callno]->calling_ton = 0; - ast_string_field_set(iaxs[callno], cid_num, user->cid_num); - ast_string_field_set(iaxs[callno], cid_name, user->cid_name); - ast_string_field_set(iaxs[callno], ani, user->cid_num); - iaxs[callno]->calling_pres = AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN; - } else if (ast_strlen_zero(iaxs[callno]->cid_num) && ast_strlen_zero(iaxs[callno]->cid_name)) { - iaxs[callno]->calling_pres = AST_PRES_NUMBER_NOT_AVAILABLE; - } /* else user is allowed to set their own CID settings */ - if (!ast_strlen_zero(user->accountcode)) - ast_string_field_set(iaxs[callno], accountcode, user->accountcode); - if (!ast_strlen_zero(user->mohinterpret)) - ast_string_field_set(iaxs[callno], mohinterpret, user->mohinterpret); - if (!ast_strlen_zero(user->mohsuggest)) - ast_string_field_set(iaxs[callno], mohsuggest, user->mohsuggest); - if (user->amaflags) - iaxs[callno]->amaflags = user->amaflags; - if (!ast_strlen_zero(user->language)) - ast_string_field_set(iaxs[callno], language, user->language); - ast_copy_flags(iaxs[callno], user, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); - /* Keep this check last */ - if (!ast_strlen_zero(user->dbsecret)) { - char *family, *key=NULL; - char buf[80]; - family = ast_strdupa(user->dbsecret); - key = strchr(family, '/'); - if (key) { - *key = '\0'; - key++; - } - if (!key || ast_db_get(family, key, buf, sizeof(buf))) - ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", user->dbsecret); - else - ast_string_field_set(iaxs[callno], secret, buf); - } else - ast_string_field_set(iaxs[callno], secret, user->secret); - res = 0; - user = user_unref(user); - } - ast_set2_flag(iaxs[callno], iax2_getpeertrunk(*sin), IAX_TRUNK); - return res; -} - -static int raw_hangup(struct sockaddr_in *sin, unsigned short src, unsigned short dst, int sockfd) -{ - struct ast_iax2_full_hdr fh; - fh.scallno = htons(src | IAX_FLAG_FULL); - fh.dcallno = htons(dst); - fh.ts = 0; - fh.oseqno = 0; - fh.iseqno = 0; - fh.type = AST_FRAME_IAX; - fh.csub = compress_subclass(IAX_COMMAND_INVAL); - if (iaxdebug) - iax_showframe(NULL, &fh, 0, sin, 0); - if (option_debug) - ast_log(LOG_DEBUG, "Raw Hangup %s:%d, src=%d, dst=%d\n", - ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), src, dst); - return sendto(sockfd, &fh, sizeof(fh), 0, (struct sockaddr *)sin, sizeof(*sin)); -} - -static void merge_encryption(struct chan_iax2_pvt *p, unsigned int enc) -{ - /* Select exactly one common encryption if there are any */ - p->encmethods &= enc; - if (p->encmethods) { - if (p->encmethods & IAX_ENCRYPT_AES128) - p->encmethods = IAX_ENCRYPT_AES128; - else - p->encmethods = 0; - } -} - -/*! - * \pre iaxsl[call_num] is locked - * - * \note Since this function calls send_command_final(), the pvt struct for the given - * call number may disappear while executing this function. - */ -static int authenticate_request(int call_num) -{ - struct iax_ie_data ied; - int res = -1, authreq_restrict = 0; - char challenge[10]; - struct chan_iax2_pvt *p = iaxs[call_num]; - - memset(&ied, 0, sizeof(ied)); - - /* If an AUTHREQ restriction is in place, make sure we can send an AUTHREQ back */ - if (ast_test_flag(p, IAX_MAXAUTHREQ)) { - struct iax2_user *user, tmp_user = { - .name = p->username, - }; - - user = ao2_find(users, &tmp_user, OBJ_POINTER); - if (user) { - if (user->curauthreq == user->maxauthreq) - authreq_restrict = 1; - else - user->curauthreq++; - user = user_unref(user); - } - } - - /* If the AUTHREQ limit test failed, send back an error */ - if (authreq_restrict) { - iax_ie_append_str(&ied, IAX_IE_CAUSE, "Unauthenticated call limit reached"); - iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_CALL_REJECTED); - send_command_final(p, AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied.buf, ied.pos, -1); - return 0; - } - - iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods); - if (p->authmethods & (IAX_AUTH_MD5 | IAX_AUTH_RSA)) { - snprintf(challenge, sizeof(challenge), "%d", (int)ast_random()); - ast_string_field_set(p, challenge, challenge); - /* snprintf(p->challenge, sizeof(p->challenge), "%d", (int)ast_random()); */ - iax_ie_append_str(&ied, IAX_IE_CHALLENGE, p->challenge); - } - if (p->encmethods) - iax_ie_append_short(&ied, IAX_IE_ENCRYPTION, p->encmethods); - - iax_ie_append_str(&ied,IAX_IE_USERNAME, p->username); - - res = send_command(p, AST_FRAME_IAX, IAX_COMMAND_AUTHREQ, 0, ied.buf, ied.pos, -1); - - if (p->encmethods) - ast_set_flag(p, IAX_ENCRYPTED); - - return res; -} - -static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies) -{ - char requeststr[256]; - char md5secret[256] = ""; - char secret[256] = ""; - char rsasecret[256] = ""; - int res = -1; - int x; - struct iax2_user *user, tmp_user = { - .name = p->username, - }; - - user = ao2_find(users, &tmp_user, OBJ_POINTER); - if (user) { - if (ast_test_flag(p, IAX_MAXAUTHREQ)) { - ast_atomic_fetchadd_int(&user->curauthreq, -1); - ast_clear_flag(p, IAX_MAXAUTHREQ); - } - ast_string_field_set(p, host, user->name); - user = user_unref(user); - } - - if (!ast_test_flag(&p->state, IAX_STATE_AUTHENTICATED)) - return res; - if (ies->password) - ast_copy_string(secret, ies->password, sizeof(secret)); - if (ies->md5_result) - ast_copy_string(md5secret, ies->md5_result, sizeof(md5secret)); - if (ies->rsa_result) - ast_copy_string(rsasecret, ies->rsa_result, sizeof(rsasecret)); - if ((p->authmethods & IAX_AUTH_RSA) && !ast_strlen_zero(rsasecret) && !ast_strlen_zero(p->inkeys)) { - struct ast_key *key; - char *keyn; - char tmpkey[256]; - char *stringp=NULL; - ast_copy_string(tmpkey, p->inkeys, sizeof(tmpkey)); - stringp=tmpkey; - keyn = strsep(&stringp, ":"); - while(keyn) { - key = ast_key_get(keyn, AST_KEY_PUBLIC); - if (key && !ast_check_signature(key, p->challenge, rsasecret)) { - res = 0; - break; - } else if (!key) - ast_log(LOG_WARNING, "requested inkey '%s' for RSA authentication does not exist\n", keyn); - keyn = strsep(&stringp, ":"); - } - } else if (p->authmethods & IAX_AUTH_MD5) { - struct MD5Context md5; - unsigned char digest[16]; - char *tmppw, *stringp; - - tmppw = ast_strdupa(p->secret); - stringp = tmppw; - while((tmppw = strsep(&stringp, ";"))) { - MD5Init(&md5); - MD5Update(&md5, (unsigned char *)p->challenge, strlen(p->challenge)); - MD5Update(&md5, (unsigned char *)tmppw, strlen(tmppw)); - MD5Final(digest, &md5); - /* If they support md5, authenticate with it. */ - for (x=0;x<16;x++) - sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */ - if (!strcasecmp(requeststr, md5secret)) { - res = 0; - break; - } - } - } else if (p->authmethods & IAX_AUTH_PLAINTEXT) { - if (!strcmp(secret, p->secret)) - res = 0; - } - return res; -} - -/*! \brief Verify inbound registration */ -static int register_verify(int callno, struct sockaddr_in *sin, struct iax_ies *ies) -{ - char requeststr[256] = ""; - char peer[256] = ""; - char md5secret[256] = ""; - char rsasecret[256] = ""; - char secret[256] = ""; - struct iax2_peer *p = NULL; - struct ast_key *key; - char *keyn; - int x; - int expire = 0; - int res = -1; - - ast_clear_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED | IAX_STATE_UNCHANGED); - /* iaxs[callno]->peer[0] = '\0'; not necc. any more-- stringfield is pre-inited to null string */ - if (ies->username) - ast_copy_string(peer, ies->username, sizeof(peer)); - if (ies->password) - ast_copy_string(secret, ies->password, sizeof(secret)); - if (ies->md5_result) - ast_copy_string(md5secret, ies->md5_result, sizeof(md5secret)); - if (ies->rsa_result) - ast_copy_string(rsasecret, ies->rsa_result, sizeof(rsasecret)); - if (ies->refresh) - expire = ies->refresh; - - if (ast_strlen_zero(peer)) { - ast_log(LOG_NOTICE, "Empty registration from %s\n", ast_inet_ntoa(sin->sin_addr)); - return -1; - } - - /* SLD: first call to lookup peer during registration */ - ast_mutex_unlock(&iaxsl[callno]); - p = find_peer(peer, 1); - ast_mutex_lock(&iaxsl[callno]); - if (!p || !iaxs[callno]) { - if (authdebug && !p) - ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr)); - goto return_unref; - } - - if (!ast_test_flag(p, IAX_DYNAMIC)) { - if (authdebug) - ast_log(LOG_NOTICE, "Peer '%s' is not dynamic (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr)); - goto return_unref; - } - - if (!ast_apply_ha(p->ha, sin)) { - if (authdebug) - ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name); - goto return_unref; - } - if (!inaddrcmp(&p->addr, sin)) - ast_set_flag(&iaxs[callno]->state, IAX_STATE_UNCHANGED); - ast_string_field_set(iaxs[callno], secret, p->secret); - ast_string_field_set(iaxs[callno], inkeys, p->inkeys); - /* Check secret against what we have on file */ - if (!ast_strlen_zero(rsasecret) && (p->authmethods & IAX_AUTH_RSA) && !ast_strlen_zero(iaxs[callno]->challenge)) { - if (!ast_strlen_zero(p->inkeys)) { - char tmpkeys[256]; - char *stringp=NULL; - ast_copy_string(tmpkeys, p->inkeys, sizeof(tmpkeys)); - stringp=tmpkeys; - keyn = strsep(&stringp, ":"); - while(keyn) { - key = ast_key_get(keyn, AST_KEY_PUBLIC); - if (key && !ast_check_signature(key, iaxs[callno]->challenge, rsasecret)) { - ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED); - break; - } else if (!key) - ast_log(LOG_WARNING, "requested inkey '%s' does not exist\n", keyn); - keyn = strsep(&stringp, ":"); - } - if (!keyn) { - if (authdebug) - ast_log(LOG_NOTICE, "Host %s failed RSA authentication with inkeys '%s'\n", peer, p->inkeys); - goto return_unref; - } - } else { - if (authdebug) - ast_log(LOG_NOTICE, "Host '%s' trying to do RSA authentication, but we have no inkeys\n", peer); - goto return_unref; - } - } else if (!ast_strlen_zero(md5secret) && (p->authmethods & IAX_AUTH_MD5) && !ast_strlen_zero(iaxs[callno]->challenge)) { - struct MD5Context md5; - unsigned char digest[16]; - char *tmppw, *stringp; - - tmppw = ast_strdupa(p->secret); - stringp = tmppw; - while((tmppw = strsep(&stringp, ";"))) { - MD5Init(&md5); - MD5Update(&md5, (unsigned char *)iaxs[callno]->challenge, strlen(iaxs[callno]->challenge)); - MD5Update(&md5, (unsigned char *)tmppw, strlen(tmppw)); - MD5Final(digest, &md5); - for (x=0;x<16;x++) - sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */ - if (!strcasecmp(requeststr, md5secret)) - break; - } - if (tmppw) { - ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED); - } else { - if (authdebug) - ast_log(LOG_NOTICE, "Host %s failed MD5 authentication for '%s' (%s != %s)\n", ast_inet_ntoa(sin->sin_addr), p->name, requeststr, md5secret); - goto return_unref; - } - } else if (!ast_strlen_zero(secret) && (p->authmethods & IAX_AUTH_PLAINTEXT)) { - /* They've provided a plain text password and we support that */ - if (strcmp(secret, p->secret)) { - if (authdebug) - ast_log(LOG_NOTICE, "Host %s did not provide proper plaintext password for '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name); - goto return_unref; - } else - ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED); - } else if (!ast_strlen_zero(md5secret) || !ast_strlen_zero(secret)) { - if (authdebug) - ast_log(LOG_NOTICE, "Inappropriate authentication received\n"); - goto return_unref; - } - ast_string_field_set(iaxs[callno], peer, peer); - /* Choose lowest expiry number */ - if (expire && (expire < iaxs[callno]->expiry)) - iaxs[callno]->expiry = expire; - - ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */ - - res = 0; - -return_unref: - if (p) - peer_unref(p); - - return res; -} - -static int authenticate(const char *challenge, const char *secret, const char *keyn, int authmethods, struct iax_ie_data *ied, struct sockaddr_in *sin, aes_encrypt_ctx *ecx, aes_decrypt_ctx *dcx) -{ - int res = -1; - int x; - if (!ast_strlen_zero(keyn)) { - if (!(authmethods & IAX_AUTH_RSA)) { - if (ast_strlen_zero(secret)) - ast_log(LOG_NOTICE, "Asked to authenticate to %s with an RSA key, but they don't allow RSA authentication\n", ast_inet_ntoa(sin->sin_addr)); - } else if (ast_strlen_zero(challenge)) { - ast_log(LOG_NOTICE, "No challenge provided for RSA authentication to %s\n", ast_inet_ntoa(sin->sin_addr)); - } else { - char sig[256]; - struct ast_key *key; - key = ast_key_get(keyn, AST_KEY_PRIVATE); - if (!key) { - ast_log(LOG_NOTICE, "Unable to find private key '%s'\n", keyn); - } else { - if (ast_sign(key, (char*)challenge, sig)) { - ast_log(LOG_NOTICE, "Unable to sign challenge with key\n"); - res = -1; - } else { - iax_ie_append_str(ied, IAX_IE_RSA_RESULT, sig); - res = 0; - } - } - } - } - /* Fall back */ - if (res && !ast_strlen_zero(secret)) { - if ((authmethods & IAX_AUTH_MD5) && !ast_strlen_zero(challenge)) { - struct MD5Context md5; - unsigned char digest[16]; - char digres[128]; - MD5Init(&md5); - MD5Update(&md5, (unsigned char *)challenge, strlen(challenge)); - MD5Update(&md5, (unsigned char *)secret, strlen(secret)); - MD5Final(digest, &md5); - /* If they support md5, authenticate with it. */ - for (x=0;x<16;x++) - sprintf(digres + (x << 1), "%2.2x", digest[x]); /* safe */ - if (ecx && dcx) - build_enc_keys(digest, ecx, dcx); - iax_ie_append_str(ied, IAX_IE_MD5_RESULT, digres); - res = 0; - } else if (authmethods & IAX_AUTH_PLAINTEXT) { - iax_ie_append_str(ied, IAX_IE_PASSWORD, secret); - res = 0; - } else - ast_log(LOG_NOTICE, "No way to send secret to peer '%s' (their methods: %d)\n", ast_inet_ntoa(sin->sin_addr), authmethods); - } - return res; -} - -/*! - * \note This function calls realtime_peer -> reg_source_db -> iax2_poke_peer -> find_callno, - * so do not call this function with a pvt lock held. - */ -static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin, struct iax_ies *ies, const char *override, const char *okey) -{ - struct iax2_peer *peer = NULL; - /* Start pessimistic */ - int res = -1; - int authmethods = 0; - struct iax_ie_data ied; - uint16_t callno = p->callno; - - memset(&ied, 0, sizeof(ied)); - - if (ies->username) - ast_string_field_set(p, username, ies->username); - if (ies->challenge) - ast_string_field_set(p, challenge, ies->challenge); - if (ies->authmethods) - authmethods = ies->authmethods; - if (authmethods & IAX_AUTH_MD5) - merge_encryption(p, ies->encmethods); - else - p->encmethods = 0; - - /* Check for override RSA authentication first */ - if (!ast_strlen_zero(override) || !ast_strlen_zero(okey)) { - /* Normal password authentication */ - res = authenticate(p->challenge, override, okey, authmethods, &ied, sin, &p->ecx, &p->dcx); - } else { - struct ao2_iterator i = ao2_iterator_init(peers, 0); - while ((peer = ao2_iterator_next(&i))) { - if ((ast_strlen_zero(p->peer) || !strcmp(p->peer, peer->name)) - /* No peer specified at our end, or this is the peer */ - && (ast_strlen_zero(peer->username) || (!strcmp(peer->username, p->username))) - /* No username specified in peer rule, or this is the right username */ - && (!peer->addr.sin_addr.s_addr || ((sin->sin_addr.s_addr & peer->mask.s_addr) == (peer->addr.sin_addr.s_addr & peer->mask.s_addr))) - /* No specified host, or this is our host */ - ) { - res = authenticate(p->challenge, peer->secret, peer->outkey, authmethods, &ied, sin, &p->ecx, &p->dcx); - if (!res) { - peer_unref(peer); - break; - } - } - peer_unref(peer); - } - if (!peer) { - /* We checked our list and didn't find one. It's unlikely, but possible, - that we're trying to authenticate *to* a realtime peer */ - const char *peer_name = ast_strdupa(p->peer); - ast_mutex_unlock(&iaxsl[callno]); - if ((peer = realtime_peer(peer_name, NULL))) { - ast_mutex_lock(&iaxsl[callno]); - if (!(p = iaxs[callno])) { - peer_unref(peer); - return -1; - } - res = authenticate(p->challenge, peer->secret,peer->outkey, authmethods, &ied, sin, &p->ecx, &p->dcx); - peer_unref(peer); - } - if (!peer) { - ast_mutex_lock(&iaxsl[callno]); - if (!(p = iaxs[callno])) - return -1; - } - } - } - if (ies->encmethods) - ast_set_flag(p, IAX_ENCRYPTED | IAX_KEYPOPULATED); - if (!res) - res = send_command(p, AST_FRAME_IAX, IAX_COMMAND_AUTHREP, 0, ied.buf, ied.pos, -1); - return res; -} - -static int iax2_do_register(struct iax2_registry *reg); - -static void __iax2_do_register_s(const void *data) -{ - struct iax2_registry *reg = (struct iax2_registry *)data; - reg->expire = -1; - iax2_do_register(reg); -} - -static int iax2_do_register_s(const void *data) -{ -#ifdef SCHED_MULTITHREADED - if (schedule_action(__iax2_do_register_s, data)) -#endif - __iax2_do_register_s(data); - return 0; -} - -static int try_transfer(struct chan_iax2_pvt *pvt, struct iax_ies *ies) -{ - int newcall = 0; - char newip[256]; - struct iax_ie_data ied; - struct sockaddr_in new; - - - memset(&ied, 0, sizeof(ied)); - if (ies->apparent_addr) - bcopy(ies->apparent_addr, &new, sizeof(new)); - if (ies->callno) - newcall = ies->callno; - if (!newcall || !new.sin_addr.s_addr || !new.sin_port) { - ast_log(LOG_WARNING, "Invalid transfer request\n"); - return -1; - } - pvt->transfercallno = newcall; - memcpy(&pvt->transfer, &new, sizeof(pvt->transfer)); - inet_aton(newip, &pvt->transfer.sin_addr); - pvt->transfer.sin_family = AF_INET; - pvt->transferring = TRANSFER_BEGIN; - pvt->transferid = ies->transferid; - if (ies->transferid) - iax_ie_append_int(&ied, IAX_IE_TRANSFERID, ies->transferid); - send_command_transfer(pvt, AST_FRAME_IAX, IAX_COMMAND_TXCNT, 0, ied.buf, ied.pos); - return 0; -} - -static int complete_dpreply(struct chan_iax2_pvt *pvt, struct iax_ies *ies) -{ - char exten[256] = ""; - int status = CACHE_FLAG_UNKNOWN; - int expiry = iaxdefaultdpcache; - int x; - int matchmore = 0; - struct iax2_dpcache *dp, *prev; - - if (ies->called_number) - ast_copy_string(exten, ies->called_number, sizeof(exten)); - - if (ies->dpstatus & IAX_DPSTATUS_EXISTS) - status = CACHE_FLAG_EXISTS; - else if (ies->dpstatus & IAX_DPSTATUS_CANEXIST) - status = CACHE_FLAG_CANEXIST; - else if (ies->dpstatus & IAX_DPSTATUS_NONEXISTENT) - status = CACHE_FLAG_NONEXISTENT; - - if (ies->dpstatus & IAX_DPSTATUS_IGNOREPAT) { - /* Don't really do anything with this */ - } - if (ies->refresh) - expiry = ies->refresh; - if (ies->dpstatus & IAX_DPSTATUS_MATCHMORE) - matchmore = CACHE_FLAG_MATCHMORE; - ast_mutex_lock(&dpcache_lock); - prev = NULL; - dp = pvt->dpentries; - while(dp) { - if (!strcmp(dp->exten, exten)) { - /* Let them go */ - if (prev) - prev->peer = dp->peer; - else - pvt->dpentries = dp->peer; - dp->peer = NULL; - dp->callno = 0; - dp->expiry.tv_sec = dp->orig.tv_sec + expiry; - if (dp->flags & CACHE_FLAG_PENDING) { - dp->flags &= ~CACHE_FLAG_PENDING; - dp->flags |= status; - dp->flags |= matchmore; - } - /* Wake up waiters */ - for (x = 0; x < ARRAY_LEN(dp->waiters); x++) { - if (dp->waiters[x] > -1) { - if (write(dp->waiters[x], "asdf", 4) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } - } - } - } - prev = dp; - dp = dp->peer; - } - ast_mutex_unlock(&dpcache_lock); - return 0; -} - -static int complete_transfer(int callno, struct iax_ies *ies) -{ - int peercallno = 0; - struct chan_iax2_pvt *pvt = iaxs[callno]; - struct iax_frame *cur; - jb_frame frame; - - if (ies->callno) - peercallno = ies->callno; - - if (peercallno < 1) { - ast_log(LOG_WARNING, "Invalid transfer request\n"); - return -1; - } - memcpy(&pvt->addr, &pvt->transfer, sizeof(pvt->addr)); - memset(&pvt->transfer, 0, sizeof(pvt->transfer)); - /* Reset sequence numbers */ - pvt->oseqno = 0; - pvt->rseqno = 0; - pvt->iseqno = 0; - pvt->aseqno = 0; - - if (pvt->peercallno) { - remove_by_peercallno(pvt); - } - pvt->peercallno = peercallno; - store_by_peercallno(pvt); - - pvt->transferring = TRANSFER_NONE; - pvt->svoiceformat = -1; - pvt->voiceformat = 0; - pvt->svideoformat = -1; - pvt->videoformat = 0; - pvt->transfercallno = -1; - memset(&pvt->rxcore, 0, sizeof(pvt->rxcore)); - memset(&pvt->offset, 0, sizeof(pvt->offset)); - /* reset jitterbuffer */ - while(jb_getall(pvt->jb,&frame) == JB_OK) - iax2_frame_free(frame.data); - jb_reset(pvt->jb); - pvt->lag = 0; - pvt->last = 0; - pvt->lastsent = 0; - pvt->nextpred = 0; - pvt->pingtime = DEFAULT_RETRY_TIME; - AST_LIST_LOCK(&iaxq.queue); - AST_LIST_TRAVERSE(&iaxq.queue, cur, list) { - /* We must cancel any packets that would have been transmitted - because now we're talking to someone new. It's okay, they - were transmitted to someone that didn't care anyway. */ - if (callno == cur->callno) - cur->retries = -1; - } - AST_LIST_UNLOCK(&iaxq.queue); - return 0; -} - -/*! \brief Acknowledgment received for OUR registration */ -static int iax2_ack_registry(struct iax_ies *ies, struct sockaddr_in *sin, int callno) -{ - struct iax2_registry *reg; - /* Start pessimistic */ - char peer[256] = ""; - char msgstatus[60]; - int refresh = 60; - char ourip[256] = "<Unspecified>"; - struct sockaddr_in oldus; - struct sockaddr_in us; - int oldmsgs; - - memset(&us, 0, sizeof(us)); - if (ies->apparent_addr) - bcopy(ies->apparent_addr, &us, sizeof(us)); - if (ies->username) - ast_copy_string(peer, ies->username, sizeof(peer)); - if (ies->refresh) - refresh = ies->refresh; - if (ies->calling_number) { - /* We don't do anything with it really, but maybe we should */ - } - reg = iaxs[callno]->reg; - if (!reg) { - ast_log(LOG_WARNING, "Registry acknowledge on unknown registry '%s'\n", peer); - return -1; - } - memcpy(&oldus, ®->us, sizeof(oldus)); - oldmsgs = reg->messages; - if (inaddrcmp(®->addr, sin)) { - ast_log(LOG_WARNING, "Received unsolicited registry ack from '%s'\n", ast_inet_ntoa(sin->sin_addr)); - return -1; - } - memcpy(®->us, &us, sizeof(reg->us)); - if (ies->msgcount >= 0) - reg->messages = ies->msgcount & 0xffff; /* only low 16 bits are used in the transmission of the IE */ - /* always refresh the registration at the interval requested by the server - we are registering to - */ - reg->refresh = refresh; - AST_SCHED_DEL(sched, reg->expire); - reg->expire = iax2_sched_add(sched, (5 * reg->refresh / 6) * 1000, iax2_do_register_s, reg); - if (inaddrcmp(&oldus, ®->us) || (reg->messages != oldmsgs)) { - if (option_verbose > 2) { - if (reg->messages > 255) - snprintf(msgstatus, sizeof(msgstatus), " with %d new and %d old messages waiting", reg->messages & 0xff, reg->messages >> 8); - else if (reg->messages > 1) - snprintf(msgstatus, sizeof(msgstatus), " with %d new messages waiting\n", reg->messages); - else if (reg->messages > 0) - snprintf(msgstatus, sizeof(msgstatus), " with 1 new message waiting\n"); - else - snprintf(msgstatus, sizeof(msgstatus), " with no messages waiting\n"); - snprintf(ourip, sizeof(ourip), "%s:%d", ast_inet_ntoa(reg->us.sin_addr), ntohs(reg->us.sin_port)); - ast_verbose(VERBOSE_PREFIX_3 "Registered IAX2 to '%s', who sees us as %s%s\n", ast_inet_ntoa(sin->sin_addr), ourip, msgstatus); - } - manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelDriver: IAX2\r\nDomain: %s\r\nStatus: Registered\r\n", ast_inet_ntoa(sin->sin_addr)); - } - reg->regstate = REG_STATE_REGISTERED; - return 0; -} - -static int iax2_register(char *value, int lineno) -{ - struct iax2_registry *reg; - char copy[256]; - char *username, *hostname, *secret; - char *porta; - char *stringp=NULL; - - if (!value) - return -1; - ast_copy_string(copy, value, sizeof(copy)); - stringp=copy; - username = strsep(&stringp, "@"); - hostname = strsep(&stringp, "@"); - if (!hostname) { - ast_log(LOG_WARNING, "Format for registration is user[:secret]@host[:port] at line %d\n", lineno); - return -1; - } - stringp=username; - username = strsep(&stringp, ":"); - secret = strsep(&stringp, ":"); - stringp=hostname; - hostname = strsep(&stringp, ":"); - porta = strsep(&stringp, ":"); - - if (porta && !atoi(porta)) { - ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno); - return -1; - } - if (!(reg = ast_calloc(1, sizeof(*reg)))) - return -1; - if (ast_dnsmgr_lookup(hostname, ®->addr.sin_addr, ®->dnsmgr) < 0) { - free(reg); - return -1; - } - ast_copy_string(reg->username, username, sizeof(reg->username)); - if (secret) - ast_copy_string(reg->secret, secret, sizeof(reg->secret)); - reg->expire = -1; - reg->refresh = IAX_DEFAULT_REG_EXPIRE; - reg->addr.sin_family = AF_INET; - reg->addr.sin_port = porta ? htons(atoi(porta)) : htons(IAX_DEFAULT_PORTNO); - AST_LIST_LOCK(®istrations); - AST_LIST_INSERT_HEAD(®istrations, reg, entry); - AST_LIST_UNLOCK(®istrations); - - return 0; -} - -static void register_peer_exten(struct iax2_peer *peer, int onoff) -{ - char multi[256]; - char *stringp, *ext; - if (!ast_strlen_zero(regcontext)) { - ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi)); - stringp = multi; - while((ext = strsep(&stringp, "&"))) { - if (onoff) { - if (!ast_exists_extension(NULL, regcontext, ext, 1, NULL)) - ast_add_extension(regcontext, 1, ext, 1, NULL, NULL, - "Noop", ast_strdup(peer->name), ast_free, "IAX2"); - } else - ast_context_remove_extension(regcontext, ext, 1, NULL); - } - } -} -static void prune_peers(void); - -static void unlink_peer(struct iax2_peer *peer) -{ - if (peer->expire > -1) { - if (!ast_sched_del(sched, peer->expire)) { - peer->expire = -1; - peer_unref(peer); - } - } - - if (peer->pokeexpire > -1) { - if (!ast_sched_del(sched, peer->pokeexpire)) { - peer->pokeexpire = -1; - peer_unref(peer); - } - } - - ao2_unlink(peers, peer); -} - -static void __expire_registry(const void *data) -{ - struct iax2_peer *peer = (struct iax2_peer *) data; - - if (!peer) - return; - - peer->expire = -1; - - if (option_debug) - ast_log(LOG_DEBUG, "Expiring registration for peer '%s'\n", peer->name); - if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(peer, IAX_TEMPONLY|IAX_RTCACHEFRIENDS))) - realtime_update_peer(peer->name, &peer->addr, 0); - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name); - /* Reset the address */ - memset(&peer->addr, 0, sizeof(peer->addr)); - /* Reset expiry value */ - peer->expiry = min_reg_expire; - if (!ast_test_flag(peer, IAX_TEMPONLY)) - ast_db_del("IAX/Registry", peer->name); - register_peer_exten(peer, 0); - ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */ - if (iax2_regfunk) - iax2_regfunk(peer->name, 0); - - if (ast_test_flag(peer, IAX_RTAUTOCLEAR)) - unlink_peer(peer); - - peer_unref(peer); -} - -static int expire_registry(const void *data) -{ -#ifdef SCHED_MULTITHREADED - if (schedule_action(__expire_registry, data)) -#endif - __expire_registry(data); - return 0; -} - -static int iax2_poke_peer(struct iax2_peer *peer, int heldcall); - -static void reg_source_db(struct iax2_peer *p) -{ - char data[80]; - struct in_addr in; - char *c, *d; - if (!ast_test_flag(p, IAX_TEMPONLY) && (!ast_db_get("IAX/Registry", p->name, data, sizeof(data)))) { - c = strchr(data, ':'); - if (c) { - *c = '\0'; - c++; - if (inet_aton(data, &in)) { - d = strchr(c, ':'); - if (d) { - *d = '\0'; - d++; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Seeding '%s' at %s:%d for %d\n", p->name, - ast_inet_ntoa(in), atoi(c), atoi(d)); - iax2_poke_peer(p, 0); - p->expiry = atoi(d); - memset(&p->addr, 0, sizeof(p->addr)); - p->addr.sin_family = AF_INET; - p->addr.sin_addr = in; - p->addr.sin_port = htons(atoi(c)); - if (p->expire > -1) { - if (!ast_sched_del(sched, p->expire)) { - p->expire = -1; - peer_unref(p); - } - } - ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */ - p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, peer_ref(p)); - if (p->expire == -1) - peer_unref(p); - if (iax2_regfunk) - iax2_regfunk(p->name, 1); - register_peer_exten(p, 1); - } - - } - } - } -} - -/*! - * \pre iaxsl[callno] is locked - * - * \note Since this function calls send_command_final(), the pvt struct for - * the given call number may disappear while executing this function. - */ -static int update_registry(struct sockaddr_in *sin, int callno, char *devtype, int fd, unsigned short refresh) -{ - /* Called from IAX thread only, with proper iaxsl lock */ - struct iax_ie_data ied; - struct iax2_peer *p; - int msgcount; - char data[80]; - int version; - const char *peer_name; - int res = -1; - - memset(&ied, 0, sizeof(ied)); - - peer_name = ast_strdupa(iaxs[callno]->peer); - - /* SLD: Another find_peer call during registration - this time when we are really updating our registration */ - ast_mutex_unlock(&iaxsl[callno]); - if (!(p = find_peer(peer_name, 1))) { - ast_mutex_lock(&iaxsl[callno]); - ast_log(LOG_WARNING, "No such peer '%s'\n", peer_name); - return -1; - } - ast_mutex_lock(&iaxsl[callno]); - if (!iaxs[callno]) - goto return_unref; - - if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(p, IAX_TEMPONLY|IAX_RTCACHEFRIENDS))) { - if (sin->sin_addr.s_addr) { - time_t nowtime; - time(&nowtime); - realtime_update_peer(peer_name, sin, nowtime); - } else { - realtime_update_peer(peer_name, sin, 0); - } - } - if (inaddrcmp(&p->addr, sin)) { - if (iax2_regfunk) - iax2_regfunk(p->name, 1); - /* Stash the IP address from which they registered */ - memcpy(&p->addr, sin, sizeof(p->addr)); - snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p->expiry); - if (!ast_test_flag(p, IAX_TEMPONLY) && sin->sin_addr.s_addr) { - ast_db_put("IAX/Registry", p->name, data); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Registered IAX2 '%s' (%s) at %s:%d\n", p->name, - ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Registered\r\n", p->name); - register_peer_exten(p, 1); - ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */ - } else if (!ast_test_flag(p, IAX_TEMPONLY)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Unregistered IAX2 '%s' (%s)\n", p->name, - ast_test_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED) ? "AUTHENTICATED" : "UNAUTHENTICATED"); - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Unregistered\r\n", p->name); - register_peer_exten(p, 0); - ast_db_del("IAX/Registry", p->name); - ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */ - } - /* Update the host */ - /* Verify that the host is really there */ - iax2_poke_peer(p, callno); - } - - /* Make sure our call still exists, an INVAL at the right point may make it go away */ - if (!iaxs[callno]) { - res = -1; - goto return_unref; - } - - /* Store socket fd */ - p->sockfd = fd; - /* Setup the expiry */ - if (p->expire > -1) { - if (!ast_sched_del(sched, p->expire)) { - p->expire = -1; - peer_unref(p); - } - } - /* treat an unspecified refresh interval as the minimum */ - if (!refresh) - refresh = min_reg_expire; - if (refresh > max_reg_expire) { - ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n", - p->name, max_reg_expire, refresh); - p->expiry = max_reg_expire; - } else if (refresh < min_reg_expire) { - ast_log(LOG_NOTICE, "Restricting registration for peer '%s' to %d seconds (requested %d)\n", - p->name, min_reg_expire, refresh); - p->expiry = min_reg_expire; - } else { - p->expiry = refresh; - } - if (p->expiry && sin->sin_addr.s_addr) { - p->expire = iax2_sched_add(sched, (p->expiry + 10) * 1000, expire_registry, peer_ref(p)); - if (p->expire == -1) - peer_unref(p); - } - iax_ie_append_str(&ied, IAX_IE_USERNAME, p->name); - iax_ie_append_int(&ied, IAX_IE_DATETIME, iax2_datetime(p->zonetag)); - if (sin->sin_addr.s_addr) { - iax_ie_append_short(&ied, IAX_IE_REFRESH, p->expiry); - iax_ie_append_addr(&ied, IAX_IE_APPARENT_ADDR, &p->addr); - if (!ast_strlen_zero(p->mailbox)) { - int new, old; - ast_app_inboxcount(p->mailbox, &new, &old); - if (new > 255) - new = 255; - if (old > 255) - old = 255; - msgcount = (old << 8) | new; - iax_ie_append_short(&ied, IAX_IE_MSGCOUNT, msgcount); - } - if (ast_test_flag(p, IAX_HASCALLERID)) { - iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, p->cid_num); - iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, p->cid_name); - } - } - version = iax_check_version(devtype); - if (version) - iax_ie_append_short(&ied, IAX_IE_FIRMWAREVER, version); - - res = 0; - -return_unref: - peer_unref(p); - - return res ? res : send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1); -} - -static int registry_authrequest(int callno) -{ - struct iax_ie_data ied; - struct iax2_peer *p; - char challenge[10]; - const char *peer_name; - int res = -1; - int sentauthmethod; - - peer_name = ast_strdupa(iaxs[callno]->peer); - - /* SLD: third call to find_peer in registration */ - ast_mutex_unlock(&iaxsl[callno]); - if ((p = find_peer(peer_name, 1))) { - last_authmethod = p->authmethods; - } - - ast_mutex_lock(&iaxsl[callno]); - if (!iaxs[callno]) - goto return_unref; - if (!p && !delayreject) { - ast_log(LOG_WARNING, "No such peer '%s'\n", peer_name); - goto return_unref; - } - - memset(&ied, 0, sizeof(ied)); - /* The selection of which delayed reject is sent may leak information, - * if it sets a static response. For example, if a host is known to only - * use MD5 authentication, then an RSA response would indicate that the - * peer does not exist, and vice-versa. - * Therefore, we use whatever the last peer used (which may vary over the - * course of a server, which should leak minimal information). */ - sentauthmethod = p ? p->authmethods : last_authmethod ? last_authmethod : (IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT); - iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, sentauthmethod); - if (sentauthmethod & (IAX_AUTH_RSA | IAX_AUTH_MD5)) { - /* Build the challenge */ - snprintf(challenge, sizeof(challenge), "%d", (int)ast_random()); - ast_string_field_set(iaxs[callno], challenge, challenge); - iax_ie_append_str(&ied, IAX_IE_CHALLENGE, iaxs[callno]->challenge); - } - iax_ie_append_str(&ied, IAX_IE_USERNAME, peer_name); - - res = 0; - -return_unref: - peer_unref(p); - - return res ? res : send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);; -} - -static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_in *sin) -{ - struct iax2_registry *reg; - /* Start pessimistic */ - struct iax_ie_data ied; - char peer[256] = ""; - char challenge[256] = ""; - int res; - int authmethods = 0; - if (ies->authmethods) - authmethods = ies->authmethods; - if (ies->username) - ast_copy_string(peer, ies->username, sizeof(peer)); - if (ies->challenge) - ast_copy_string(challenge, ies->challenge, sizeof(challenge)); - memset(&ied, 0, sizeof(ied)); - reg = iaxs[callno]->reg; - if (reg) { - if (inaddrcmp(®->addr, sin)) { - ast_log(LOG_WARNING, "Received unsolicited registry authenticate request from '%s'\n", ast_inet_ntoa(sin->sin_addr)); - return -1; - } - if (ast_strlen_zero(reg->secret)) { - ast_log(LOG_NOTICE, "No secret associated with peer '%s'\n", reg->username); - reg->regstate = REG_STATE_NOAUTH; - return -1; - } - iax_ie_append_str(&ied, IAX_IE_USERNAME, reg->username); - iax_ie_append_short(&ied, IAX_IE_REFRESH, reg->refresh); - if (reg->secret[0] == '[') { - char tmpkey[256]; - ast_copy_string(tmpkey, reg->secret + 1, sizeof(tmpkey)); - tmpkey[strlen(tmpkey) - 1] = '\0'; - res = authenticate(challenge, NULL, tmpkey, authmethods, &ied, sin, NULL, NULL); - } else - res = authenticate(challenge, reg->secret, NULL, authmethods, &ied, sin, NULL, NULL); - if (!res) { - reg->regstate = REG_STATE_AUTHSENT; - return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1); - } else - return -1; - ast_log(LOG_WARNING, "Registry acknowledge on unknown registery '%s'\n", peer); - } else - ast_log(LOG_NOTICE, "Can't reregister without a reg\n"); - return -1; -} - -static void stop_stuff(int callno) -{ - iax2_destroy_helper(iaxs[callno]); -} - -static void __auth_reject(const void *nothing) -{ - /* Called from IAX thread only, without iaxs lock */ - int callno = (int)(long)(nothing); - struct iax_ie_data ied; - ast_mutex_lock(&iaxsl[callno]); - if (iaxs[callno]) { - memset(&ied, 0, sizeof(ied)); - if (iaxs[callno]->authfail == IAX_COMMAND_REGREJ) { - iax_ie_append_str(&ied, IAX_IE_CAUSE, "Registration Refused"); - iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_FACILITY_REJECTED); - } else if (iaxs[callno]->authfail == IAX_COMMAND_REJECT) { - iax_ie_append_str(&ied, IAX_IE_CAUSE, "No authority found"); - iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_FACILITY_NOT_SUBSCRIBED); - } - send_command_final(iaxs[callno], AST_FRAME_IAX, iaxs[callno]->authfail, 0, ied.buf, ied.pos, -1); - } - ast_mutex_unlock(&iaxsl[callno]); -} - -static int auth_reject(const void *data) -{ - int callno = (int)(long)(data); - ast_mutex_lock(&iaxsl[callno]); - if (iaxs[callno]) - iaxs[callno]->authid = -1; - ast_mutex_unlock(&iaxsl[callno]); -#ifdef SCHED_MULTITHREADED - if (schedule_action(__auth_reject, data)) -#endif - __auth_reject(data); - return 0; -} - -static int auth_fail(int callno, int failcode) -{ - /* Schedule sending the authentication failure in one second, to prevent - guessing */ - if (iaxs[callno]) { - iaxs[callno]->authfail = failcode; - if (delayreject) { - AST_SCHED_DEL(sched, iaxs[callno]->authid); - iaxs[callno]->authid = iax2_sched_add(sched, 1000, auth_reject, (void *)(long)callno); - } else - auth_reject((void *)(long)callno); - } - return 0; -} - -static void __auto_hangup(const void *nothing) -{ - /* Called from IAX thread only, without iaxs lock */ - int callno = (int)(long)(nothing); - struct iax_ie_data ied; - ast_mutex_lock(&iaxsl[callno]); - if (iaxs[callno]) { - memset(&ied, 0, sizeof(ied)); - iax_ie_append_str(&ied, IAX_IE_CAUSE, "Timeout"); - iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, AST_CAUSE_NO_USER_RESPONSE); - send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_HANGUP, 0, ied.buf, ied.pos, -1); - } - ast_mutex_unlock(&iaxsl[callno]); -} - -static int auto_hangup(const void *data) -{ - int callno = (int)(long)(data); - ast_mutex_lock(&iaxsl[callno]); - if (iaxs[callno]) { - iaxs[callno]->autoid = -1; - } - ast_mutex_unlock(&iaxsl[callno]); -#ifdef SCHED_MULTITHREADED - if (schedule_action(__auto_hangup, data)) -#endif - __auto_hangup(data); - return 0; -} - -static void iax2_dprequest(struct iax2_dpcache *dp, int callno) -{ - struct iax_ie_data ied; - /* Auto-hangup with 30 seconds of inactivity */ - AST_SCHED_DEL(sched, iaxs[callno]->autoid); - iaxs[callno]->autoid = iax2_sched_add(sched, 30000, auto_hangup, (void *)(long)callno); - memset(&ied, 0, sizeof(ied)); - iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, dp->exten); - send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_DPREQ, 0, ied.buf, ied.pos, -1); - dp->flags |= CACHE_FLAG_TRANSMITTED; -} - -static int iax2_vnak(int callno) -{ - return send_command_immediate(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_VNAK, 0, NULL, 0, iaxs[callno]->iseqno); -} - -static void vnak_retransmit(int callno, int last) -{ - struct iax_frame *f; - - AST_LIST_LOCK(&iaxq.queue); - AST_LIST_TRAVERSE(&iaxq.queue, f, list) { - /* Send a copy immediately */ - if ((f->callno == callno) && iaxs[f->callno] && - ((unsigned char ) (f->oseqno - last) < 128) && - (f->retries >= 0)) { - send_packet(f); - } - } - AST_LIST_UNLOCK(&iaxq.queue); -} - -static void __iax2_poke_peer_s(const void *data) -{ - struct iax2_peer *peer = (struct iax2_peer *)data; - iax2_poke_peer(peer, 0); - peer_unref(peer); -} - -static int iax2_poke_peer_s(const void *data) -{ - struct iax2_peer *peer = (struct iax2_peer *)data; - peer->pokeexpire = -1; -#ifdef SCHED_MULTITHREADED - if (schedule_action(__iax2_poke_peer_s, data)) -#endif - __iax2_poke_peer_s(data); - return 0; -} - -static int send_trunk(struct iax2_trunk_peer *tpeer, struct timeval *now) -{ - int res = 0; - struct iax_frame *fr; - struct ast_iax2_meta_hdr *meta; - struct ast_iax2_meta_trunk_hdr *mth; - int calls = 0; - - /* Point to frame */ - fr = (struct iax_frame *)tpeer->trunkdata; - /* Point to meta data */ - meta = (struct ast_iax2_meta_hdr *)fr->afdata; - mth = (struct ast_iax2_meta_trunk_hdr *)meta->data; - if (tpeer->trunkdatalen) { - /* We're actually sending a frame, so fill the meta trunk header and meta header */ - meta->zeros = 0; - meta->metacmd = IAX_META_TRUNK; - if (ast_test_flag(&globalflags, IAX_TRUNKTIMESTAMPS)) - meta->cmddata = IAX_META_TRUNK_MINI; - else - meta->cmddata = IAX_META_TRUNK_SUPERMINI; - mth->ts = htonl(calc_txpeerstamp(tpeer, trunkfreq, now)); - /* And the rest of the ast_iax2 header */ - fr->direction = DIRECTION_OUTGRESS; - fr->retrans = -1; - fr->transfer = 0; - /* Any appropriate call will do */ - fr->data = fr->afdata; - fr->datalen = tpeer->trunkdatalen + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr); - res = transmit_trunk(fr, &tpeer->addr, tpeer->sockfd); - calls = tpeer->calls; -#if 0 - if (option_debug) - ast_log(LOG_DEBUG, "Trunking %d call chunks in %d bytes to %s:%d, ts=%d\n", calls, fr->datalen, ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), ntohl(mth->ts)); -#endif - /* Reset transmit trunk side data */ - tpeer->trunkdatalen = 0; - tpeer->calls = 0; - } - if (res < 0) - return res; - return calls; -} - -static inline int iax2_trunk_expired(struct iax2_trunk_peer *tpeer, struct timeval *now) -{ - /* Drop when trunk is about 5 seconds idle */ - if (now->tv_sec > tpeer->trunkact.tv_sec + 5) - return 1; - return 0; -} - -static int timing_read(int *id, int fd, short events, void *cbdata) -{ - char buf[1024]; - int res; - struct iax2_trunk_peer *tpeer, *prev = NULL, *drop=NULL; - int processed = 0; - int totalcalls = 0; -#ifdef DAHDI_TIMERACK - int x = 1; -#endif - struct timeval now; - if (iaxtrunkdebug) - ast_verbose("Beginning trunk processing. Trunk queue ceiling is %d bytes per host\n", MAX_TRUNKDATA); - gettimeofday(&now, NULL); - if (events & AST_IO_PRI) { -#ifdef DAHDI_TIMERACK - /* Great, this is a timing interface, just call the ioctl */ - if (ioctl(fd, DAHDI_TIMERACK, &x)) { - ast_log(LOG_WARNING, "Unable to acknowledge timer. IAX trunking will fail!\n"); - usleep(1); - return -1; - } -#endif - } else { - /* Read and ignore from the pseudo channel for timing */ - res = read(fd, buf, sizeof(buf)); - if (res < 1) { - ast_log(LOG_WARNING, "Unable to read from timing fd\n"); - return 1; - } - } - /* For each peer that supports trunking... */ - ast_mutex_lock(&tpeerlock); - tpeer = tpeers; - while(tpeer) { - processed++; - res = 0; - ast_mutex_lock(&tpeer->lock); - /* We can drop a single tpeer per pass. That makes all this logic - substantially easier */ - if (!drop && iax2_trunk_expired(tpeer, &now)) { - /* Take it out of the list, but don't free it yet, because it - could be in use */ - if (prev) - prev->next = tpeer->next; - else - tpeers = tpeer->next; - drop = tpeer; - } else { - res = send_trunk(tpeer, &now); - if (iaxtrunkdebug) - ast_verbose(" - Trunk peer (%s:%d) has %d call chunk%s in transit, %d bytes backloged and has hit a high water mark of %d bytes\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), res, (res != 1) ? "s" : "", tpeer->trunkdatalen, tpeer->trunkdataalloc); - } - totalcalls += res; - res = 0; - ast_mutex_unlock(&tpeer->lock); - prev = tpeer; - tpeer = tpeer->next; - } - ast_mutex_unlock(&tpeerlock); - if (drop) { - ast_mutex_lock(&drop->lock); - /* Once we have this lock, we're sure nobody else is using it or could use it once we release it, - because by the time they could get tpeerlock, we've already grabbed it */ - if (option_debug) - ast_log(LOG_DEBUG, "Dropping unused iax2 trunk peer '%s:%d'\n", ast_inet_ntoa(drop->addr.sin_addr), ntohs(drop->addr.sin_port)); - if (drop->trunkdata) { - free(drop->trunkdata); - drop->trunkdata = NULL; - } - ast_mutex_unlock(&drop->lock); - ast_mutex_destroy(&drop->lock); - free(drop); - - } - if (iaxtrunkdebug) - ast_verbose("Ending trunk processing with %d peers and %d call chunks processed\n", processed, totalcalls); - iaxtrunkdebug =0; - return 1; -} - -struct dpreq_data { - int callno; - char context[AST_MAX_EXTENSION]; - char callednum[AST_MAX_EXTENSION]; - char *callerid; -}; - -static void dp_lookup(int callno, const char *context, const char *callednum, const char *callerid, int skiplock) -{ - unsigned short dpstatus = 0; - struct iax_ie_data ied1; - int mm; - - memset(&ied1, 0, sizeof(ied1)); - mm = ast_matchmore_extension(NULL, context, callednum, 1, callerid); - /* Must be started */ - if (!strcmp(callednum, ast_parking_ext()) || ast_exists_extension(NULL, context, callednum, 1, callerid)) { - dpstatus = IAX_DPSTATUS_EXISTS; - } else if (ast_canmatch_extension(NULL, context, callednum, 1, callerid)) { - dpstatus = IAX_DPSTATUS_CANEXIST; - } else { - dpstatus = IAX_DPSTATUS_NONEXISTENT; - } - if (ast_ignore_pattern(context, callednum)) - dpstatus |= IAX_DPSTATUS_IGNOREPAT; - if (mm) - dpstatus |= IAX_DPSTATUS_MATCHMORE; - if (!skiplock) - ast_mutex_lock(&iaxsl[callno]); - if (iaxs[callno]) { - iax_ie_append_str(&ied1, IAX_IE_CALLED_NUMBER, callednum); - iax_ie_append_short(&ied1, IAX_IE_DPSTATUS, dpstatus); - iax_ie_append_short(&ied1, IAX_IE_REFRESH, iaxdefaultdpcache); - send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_DPREP, 0, ied1.buf, ied1.pos, -1); - } - if (!skiplock) - ast_mutex_unlock(&iaxsl[callno]); -} - -static void *dp_lookup_thread(void *data) -{ - /* Look up for dpreq */ - struct dpreq_data *dpr = data; - dp_lookup(dpr->callno, dpr->context, dpr->callednum, dpr->callerid, 0); - if (dpr->callerid) - free(dpr->callerid); - free(dpr); - return NULL; -} - -static void spawn_dp_lookup(int callno, const char *context, const char *callednum, const char *callerid) -{ - pthread_t newthread; - struct dpreq_data *dpr; - pthread_attr_t attr; - - if (!(dpr = ast_calloc(1, sizeof(*dpr)))) - return; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - dpr->callno = callno; - ast_copy_string(dpr->context, context, sizeof(dpr->context)); - ast_copy_string(dpr->callednum, callednum, sizeof(dpr->callednum)); - if (callerid) - dpr->callerid = ast_strdup(callerid); - if (ast_pthread_create(&newthread, &attr, dp_lookup_thread, dpr)) { - ast_log(LOG_WARNING, "Unable to start lookup thread!\n"); - } - - pthread_attr_destroy(&attr); -} - -struct iax_dual { - struct ast_channel *chan1; - struct ast_channel *chan2; -}; - -static void *iax_park_thread(void *stuff) -{ - struct ast_channel *chan1, *chan2; - struct iax_dual *d; - struct ast_frame *f; - int ext; - int res; - d = stuff; - chan1 = d->chan1; - chan2 = d->chan2; - free(d); - f = ast_read(chan1); - if (f) - ast_frfree(f); - res = ast_park_call(chan1, chan2, 0, &ext); - ast_hangup(chan2); - ast_log(LOG_NOTICE, "Parked on extension '%d'\n", ext); - return NULL; -} - -static int iax_park(struct ast_channel *chan1, struct ast_channel *chan2) -{ - struct iax_dual *d; - struct ast_channel *chan1m, *chan2m; - pthread_t th; - chan1m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan1->exten, chan1->context, chan1->amaflags, "Parking/%s", chan1->name); - chan2m = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->amaflags, "IAXPeer/%s",chan2->name); - if (chan2m && chan1m) { - /* Make formats okay */ - chan1m->readformat = chan1->readformat; - chan1m->writeformat = chan1->writeformat; - ast_channel_masquerade(chan1m, chan1); - /* Setup the extensions and such */ - ast_copy_string(chan1m->context, chan1->context, sizeof(chan1m->context)); - ast_copy_string(chan1m->exten, chan1->exten, sizeof(chan1m->exten)); - chan1m->priority = chan1->priority; - - /* We make a clone of the peer channel too, so we can play - back the announcement */ - /* Make formats okay */ - chan2m->readformat = chan2->readformat; - chan2m->writeformat = chan2->writeformat; - ast_channel_masquerade(chan2m, chan2); - /* Setup the extensions and such */ - ast_copy_string(chan2m->context, chan2->context, sizeof(chan2m->context)); - ast_copy_string(chan2m->exten, chan2->exten, sizeof(chan2m->exten)); - chan2m->priority = chan2->priority; - if (ast_do_masquerade(chan2m)) { - ast_log(LOG_WARNING, "Masquerade failed :(\n"); - ast_hangup(chan2m); - return -1; - } - } else { - if (chan1m) - ast_hangup(chan1m); - if (chan2m) - ast_hangup(chan2m); - return -1; - } - if ((d = ast_calloc(1, sizeof(*d)))) { - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - d->chan1 = chan1m; - d->chan2 = chan2m; - if (!ast_pthread_create_background(&th, &attr, iax_park_thread, d)) { - pthread_attr_destroy(&attr); - return 0; - } - pthread_attr_destroy(&attr); - free(d); - } - return -1; -} - - -static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force); - -static int check_provisioning(struct sockaddr_in *sin, int sockfd, char *si, unsigned int ver) -{ - unsigned int ourver; - char rsi[80]; - snprintf(rsi, sizeof(rsi), "si-%s", si); - if (iax_provision_version(&ourver, rsi, 1)) - return 0; - if (option_debug) - ast_log(LOG_DEBUG, "Service identifier '%s', we think '%08x', they think '%08x'\n", si, ourver, ver); - if (ourver != ver) - iax2_provision(sin, sockfd, NULL, rsi, 1); - return 0; -} - -static void construct_rr(struct chan_iax2_pvt *pvt, struct iax_ie_data *iep) -{ - jb_info stats; - jb_getinfo(pvt->jb, &stats); - - memset(iep, 0, sizeof(*iep)); - - iax_ie_append_int(iep,IAX_IE_RR_JITTER, stats.jitter); - if(stats.frames_in == 0) stats.frames_in = 1; - iax_ie_append_int(iep,IAX_IE_RR_LOSS, ((0xff & (stats.losspct/1000)) << 24 | (stats.frames_lost & 0x00ffffff))); - iax_ie_append_int(iep,IAX_IE_RR_PKTS, stats.frames_in); - iax_ie_append_short(iep,IAX_IE_RR_DELAY, stats.current - stats.min); - iax_ie_append_int(iep,IAX_IE_RR_DROPPED, stats.frames_dropped); - iax_ie_append_int(iep,IAX_IE_RR_OOO, stats.frames_ooo); -} - -static void save_rr(struct iax_frame *fr, struct iax_ies *ies) -{ - iaxs[fr->callno]->remote_rr.jitter = ies->rr_jitter; - iaxs[fr->callno]->remote_rr.losspct = ies->rr_loss >> 24; - iaxs[fr->callno]->remote_rr.losscnt = ies->rr_loss & 0xffffff; - iaxs[fr->callno]->remote_rr.packets = ies->rr_pkts; - iaxs[fr->callno]->remote_rr.delay = ies->rr_delay; - iaxs[fr->callno]->remote_rr.dropped = ies->rr_dropped; - iaxs[fr->callno]->remote_rr.ooo = ies->rr_ooo; -} - -static int socket_process(struct iax2_thread *thread); - -/*! - * \brief Handle any deferred full frames for this thread - */ -static void handle_deferred_full_frames(struct iax2_thread *thread) -{ - struct iax2_pkt_buf *pkt_buf; - - ast_mutex_lock(&thread->lock); - - while ((pkt_buf = AST_LIST_REMOVE_HEAD(&thread->full_frames, entry))) { - ast_mutex_unlock(&thread->lock); - - thread->buf = pkt_buf->buf; - thread->buf_len = pkt_buf->len; - thread->buf_size = pkt_buf->len + 1; - - socket_process(thread); - - thread->buf = NULL; - ast_free(pkt_buf); - - ast_mutex_lock(&thread->lock); - } - - ast_mutex_unlock(&thread->lock); -} - -/*! - * \brief Queue the last read full frame for processing by a certain thread - * - * If there are already any full frames queued, they are sorted - * by sequence number. - */ -static void defer_full_frame(struct iax2_thread *from_here, struct iax2_thread *to_here) -{ - struct iax2_pkt_buf *pkt_buf, *cur_pkt_buf; - struct ast_iax2_full_hdr *fh, *cur_fh; - - if (!(pkt_buf = ast_calloc(1, sizeof(*pkt_buf) + from_here->buf_len))) - return; - - pkt_buf->len = from_here->buf_len; - memcpy(pkt_buf->buf, from_here->buf, pkt_buf->len); - - fh = (struct ast_iax2_full_hdr *) pkt_buf->buf; - ast_mutex_lock(&to_here->lock); - AST_LIST_TRAVERSE_SAFE_BEGIN(&to_here->full_frames, cur_pkt_buf, entry) { - cur_fh = (struct ast_iax2_full_hdr *) cur_pkt_buf->buf; - if (fh->oseqno < cur_fh->oseqno) { - AST_LIST_INSERT_BEFORE_CURRENT(&to_here->full_frames, pkt_buf, entry); - break; - } - } - AST_LIST_TRAVERSE_SAFE_END - - if (!cur_pkt_buf) - AST_LIST_INSERT_TAIL(&to_here->full_frames, pkt_buf, entry); - - ast_mutex_unlock(&to_here->lock); -} - -static int socket_read(int *id, int fd, short events, void *cbdata) -{ - struct iax2_thread *thread; - socklen_t len; - time_t t; - static time_t last_errtime = 0; - struct ast_iax2_full_hdr *fh; - - if (!(thread = find_idle_thread())) { - time(&t); - if (t != last_errtime && option_debug) - ast_log(LOG_DEBUG, "Out of idle IAX2 threads for I/O, pausing!\n"); - last_errtime = t; - usleep(1); - return 1; - } - - len = sizeof(thread->iosin); - thread->iofd = fd; - thread->buf_len = recvfrom(fd, thread->readbuf, sizeof(thread->readbuf), 0, (struct sockaddr *) &thread->iosin, &len); - thread->buf_size = sizeof(thread->readbuf); - thread->buf = thread->readbuf; - if (thread->buf_len < 0) { - if (errno != ECONNREFUSED && errno != EAGAIN) - ast_log(LOG_WARNING, "Error: %s\n", strerror(errno)); - handle_error(); - thread->iostate = IAX_IOSTATE_IDLE; - signal_condition(&thread->lock, &thread->cond); - return 1; - } - if (test_losspct && ((100.0 * ast_random() / (RAND_MAX + 1.0)) < test_losspct)) { /* simulate random loss condition */ - thread->iostate = IAX_IOSTATE_IDLE; - signal_condition(&thread->lock, &thread->cond); - return 1; - } - - /* Determine if this frame is a full frame; if so, and any thread is currently - processing a full frame for the same callno from this peer, then drop this - frame (and the peer will retransmit it) */ - fh = (struct ast_iax2_full_hdr *) thread->buf; - if (ntohs(fh->scallno) & IAX_FLAG_FULL) { - struct iax2_thread *cur = NULL; - uint16_t callno = ntohs(fh->scallno) & ~IAX_FLAG_FULL; - - AST_LIST_LOCK(&active_list); - AST_LIST_TRAVERSE(&active_list, cur, list) { - if ((cur->ffinfo.callno == callno) && - !inaddrcmp(&cur->ffinfo.sin, &thread->iosin)) - break; - } - if (cur) { - /* we found another thread processing a full frame for this call, - so queue it up for processing later. */ - defer_full_frame(thread, cur); - AST_LIST_UNLOCK(&active_list); - thread->iostate = IAX_IOSTATE_IDLE; - signal_condition(&thread->lock, &thread->cond); - return 1; - } else { - /* this thread is going to process this frame, so mark it */ - thread->ffinfo.callno = callno; - memcpy(&thread->ffinfo.sin, &thread->iosin, sizeof(thread->ffinfo.sin)); - thread->ffinfo.type = fh->type; - thread->ffinfo.csub = fh->csub; - } - AST_LIST_UNLOCK(&active_list); - } - - /* Mark as ready and send on its way */ - thread->iostate = IAX_IOSTATE_READY; -#ifdef DEBUG_SCHED_MULTITHREAD - ast_copy_string(thread->curfunc, "socket_process", sizeof(thread->curfunc)); -#endif - signal_condition(&thread->lock, &thread->cond); - - return 1; -} - -static int socket_process(struct iax2_thread *thread) -{ - struct sockaddr_in sin; - int res; - int updatehistory=1; - int new = NEW_PREVENT; - void *ptr; - int dcallno = 0; - struct ast_iax2_full_hdr *fh = (struct ast_iax2_full_hdr *)thread->buf; - struct ast_iax2_mini_hdr *mh = (struct ast_iax2_mini_hdr *)thread->buf; - struct ast_iax2_meta_hdr *meta = (struct ast_iax2_meta_hdr *)thread->buf; - struct ast_iax2_video_hdr *vh = (struct ast_iax2_video_hdr *)thread->buf; - struct ast_iax2_meta_trunk_hdr *mth; - struct ast_iax2_meta_trunk_entry *mte; - struct ast_iax2_meta_trunk_mini *mtm; - struct iax_frame *fr; - struct iax_frame *cur; - struct ast_frame f = { 0, }; - struct ast_channel *c; - struct iax2_dpcache *dp; - struct iax2_peer *peer; - struct iax2_trunk_peer *tpeer; - struct timeval rxtrunktime; - struct iax_ies ies; - struct iax_ie_data ied0, ied1; - int format; - int fd; - int exists; - int minivid = 0; - unsigned int ts; - char empty[32]=""; /* Safety measure */ - struct iax_frame *duped_fr; - char host_pref_buf[128]; - char caller_pref_buf[128]; - struct ast_codec_pref pref; - char *using_prefs = "mine"; - - /* allocate an iax_frame with 4096 bytes of data buffer */ - fr = alloca(sizeof(*fr) + 4096); - memset(fr, 0, sizeof(*fr)); - fr->afdatalen = 4096; /* From alloca() above */ - - /* Copy frequently used parameters to the stack */ - res = thread->buf_len; - fd = thread->iofd; - memcpy(&sin, &thread->iosin, sizeof(sin)); - - if (res < sizeof(*mh)) { - ast_log(LOG_WARNING, "midget packet received (%d of %zd min)\n", res, sizeof(*mh)); - return 1; - } - if ((vh->zeros == 0) && (ntohs(vh->callno) & 0x8000)) { - if (res < sizeof(*vh)) { - ast_log(LOG_WARNING, "Rejecting packet from '%s.%d' that is flagged as a video frame but is too short\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - return 1; - } - - /* This is a video frame, get call number */ - fr->callno = find_callno(ntohs(vh->callno) & ~0x8000, dcallno, &sin, new, fd, 0); - minivid = 1; - } else if ((meta->zeros == 0) && !(ntohs(meta->metacmd) & 0x8000)) { - unsigned char metatype; - - if (res < sizeof(*meta)) { - ast_log(LOG_WARNING, "Rejecting packet from '%s.%d' that is flagged as a meta frame but is too short\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - return 1; - } - - /* This is a meta header */ - switch(meta->metacmd) { - case IAX_META_TRUNK: - if (res < (sizeof(*meta) + sizeof(*mth))) { - ast_log(LOG_WARNING, "midget meta trunk packet received (%d of %zd min)\n", res, - sizeof(*meta) + sizeof(*mth)); - return 1; - } - mth = (struct ast_iax2_meta_trunk_hdr *)(meta->data); - ts = ntohl(mth->ts); - metatype = meta->cmddata; - res -= (sizeof(*meta) + sizeof(*mth)); - ptr = mth->data; - tpeer = find_tpeer(&sin, fd); - if (!tpeer) { - ast_log(LOG_WARNING, "Unable to accept trunked packet from '%s:%d': No matching peer\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - return 1; - } - tpeer->trunkact = ast_tvnow(); - if (!ts || ast_tvzero(tpeer->rxtrunktime)) - tpeer->rxtrunktime = tpeer->trunkact; - rxtrunktime = tpeer->rxtrunktime; - ast_mutex_unlock(&tpeer->lock); - while(res >= sizeof(*mte)) { - /* Process channels */ - unsigned short callno, trunked_ts, len; - - if (metatype == IAX_META_TRUNK_MINI) { - mtm = (struct ast_iax2_meta_trunk_mini *)ptr; - ptr += sizeof(*mtm); - res -= sizeof(*mtm); - len = ntohs(mtm->len); - callno = ntohs(mtm->mini.callno); - trunked_ts = ntohs(mtm->mini.ts); - } else if (metatype == IAX_META_TRUNK_SUPERMINI) { - mte = (struct ast_iax2_meta_trunk_entry *)ptr; - ptr += sizeof(*mte); - res -= sizeof(*mte); - len = ntohs(mte->len); - callno = ntohs(mte->callno); - trunked_ts = 0; - } else { - ast_log(LOG_WARNING, "Unknown meta trunk cmd from '%s:%d': dropping\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - break; - } - /* Stop if we don't have enough data */ - if (len > res) - break; - fr->callno = find_callno_locked(callno & ~IAX_FLAG_FULL, 0, &sin, NEW_PREVENT, fd, 0); - if (fr->callno) { - /* If it's a valid call, deliver the contents. If not, we - drop it, since we don't have a scallno to use for an INVAL */ - /* Process as a mini frame */ - memset(&f, 0, sizeof(f)); - f.frametype = AST_FRAME_VOICE; - if (iaxs[fr->callno]) { - if (iaxs[fr->callno]->voiceformat > 0) { - f.subclass = iaxs[fr->callno]->voiceformat; - f.datalen = len; - if (f.datalen >= 0) { - if (f.datalen) - f.data = ptr; - if(trunked_ts) { - fr->ts = (iaxs[fr->callno]->last & 0xFFFF0000L) | (trunked_ts & 0xffff); - } else - fr->ts = fix_peerts(&rxtrunktime, fr->callno, ts); - /* Don't pass any packets until we're started */ - if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) { - /* Common things */ - f.src = "IAX2"; - if (f.datalen && (f.frametype == AST_FRAME_VOICE)) - f.samples = ast_codec_get_samples(&f); - iax_frame_wrap(fr, &f); - duped_fr = iaxfrdup2(fr); - if (duped_fr) { - schedule_delivery(duped_fr, updatehistory, 1, &fr->ts); - } - /* It is possible for the pvt structure to go away after we call schedule_delivery */ - if (iaxs[fr->callno] && iaxs[fr->callno]->last < fr->ts) { - iaxs[fr->callno]->last = fr->ts; -#if 1 - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "For call=%d, set last=%d\n", fr->callno, fr->ts); -#endif - } - } - } else { - ast_log(LOG_WARNING, "Datalen < 0?\n"); - } - } else { - ast_log(LOG_WARNING, "Received trunked frame before first full voice frame\n"); - iax2_vnak(fr->callno); - } - } - ast_mutex_unlock(&iaxsl[fr->callno]); - } - ptr += len; - res -= len; - } - - } - return 1; - } - -#ifdef DEBUG_SUPPORT - if (iaxdebug && (res >= sizeof(*fh))) - iax_showframe(NULL, fh, 1, &sin, res - sizeof(*fh)); -#endif - if (ntohs(mh->callno) & IAX_FLAG_FULL) { - if (res < sizeof(*fh)) { - ast_log(LOG_WARNING, "Rejecting packet from '%s.%d' that is flagged as a full frame but is too short\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - return 1; - } - - /* Get the destination call number */ - dcallno = ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS; - /* Retrieve the type and subclass */ - f.frametype = fh->type; - if (f.frametype == AST_FRAME_VIDEO) { - f.subclass = uncompress_subclass(fh->csub & ~0x40) | ((fh->csub >> 6) & 0x1); - } else { - f.subclass = uncompress_subclass(fh->csub); - } - - /* Deal with POKE/PONG without allocating a callno */ - if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_POKE) { - /* Reply back with a PONG, but don't care about the result. */ - send_apathetic_reply(1, ntohs(fh->scallno), &sin, IAX_COMMAND_PONG, ntohs(fh->ts), fh->iseqno + 1); - return 1; - } else if (f.frametype == AST_FRAME_IAX && f.subclass == IAX_COMMAND_ACK && dcallno == 1) { - /* Ignore */ - return 1; - } - - if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == IAX_COMMAND_NEW) || (f.subclass == IAX_COMMAND_REGREQ) || - (f.subclass == IAX_COMMAND_POKE) || (f.subclass == IAX_COMMAND_FWDOWNL) || - (f.subclass == IAX_COMMAND_REGREL))) - new = NEW_ALLOW; - } else { - /* Don't know anything about it yet */ - f.frametype = AST_FRAME_NULL; - f.subclass = 0; - } - - if (!fr->callno) { - int check_dcallno = 0; - - /* - * We enforce accurate destination call numbers for all full frames except - * LAGRQ and PING commands. This is because older versions of Asterisk - * schedule these commands to get sent very quickly, and they will sometimes - * be sent before they receive the first frame from the other side. When - * that happens, it doesn't contain the destination call number. However, - * not checking it for these frames is safe. - * - * Discussed in the following thread: - * http://lists.digium.com/pipermail/asterisk-dev/2008-May/033217.html - */ - - if (ntohs(mh->callno) & IAX_FLAG_FULL) { - check_dcallno = f.frametype == AST_FRAME_IAX ? (f.subclass != IAX_COMMAND_PING && f.subclass != IAX_COMMAND_LAGRQ) : 1; - } - - fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd, check_dcallno); - } - - if (fr->callno > 0) - ast_mutex_lock(&iaxsl[fr->callno]); - - if (!fr->callno || !iaxs[fr->callno]) { - /* A call arrived for a nonexistent destination. Unless it's an "inval" - frame, reply with an inval */ - if (ntohs(mh->callno) & IAX_FLAG_FULL) { - /* We can only raw hangup control frames */ - if (((f.subclass != IAX_COMMAND_INVAL) && - (f.subclass != IAX_COMMAND_TXCNT) && - (f.subclass != IAX_COMMAND_TXACC) && - (f.subclass != IAX_COMMAND_FWDOWNL))|| - (f.frametype != AST_FRAME_IAX)) - raw_hangup(&sin, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS, ntohs(mh->callno) & ~IAX_FLAG_FULL, - fd); - } - if (fr->callno > 0) - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - if (ast_test_flag(iaxs[fr->callno], IAX_ENCRYPTED)) { - if (decrypt_frame(fr->callno, fh, &f, &res)) { - ast_log(LOG_NOTICE, "Packet Decrypt Failed!\n"); - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } -#ifdef DEBUG_SUPPORT - else if (iaxdebug) - iax_showframe(NULL, fh, 3, &sin, res - sizeof(*fh)); -#endif - } - - /* count this frame */ - iaxs[fr->callno]->frames_received++; - - if (!inaddrcmp(&sin, &iaxs[fr->callno]->addr) && !minivid && - f.subclass != IAX_COMMAND_TXCNT && /* for attended transfer */ - f.subclass != IAX_COMMAND_TXACC) { /* for attended transfer */ - unsigned short new_peercallno; - - new_peercallno = (unsigned short) (ntohs(mh->callno) & ~IAX_FLAG_FULL); - if (new_peercallno && new_peercallno != iaxs[fr->callno]->peercallno) { - if (iaxs[fr->callno]->peercallno) { - remove_by_peercallno(iaxs[fr->callno]); - } - iaxs[fr->callno]->peercallno = new_peercallno; - store_by_peercallno(iaxs[fr->callno]); - } - } - if (ntohs(mh->callno) & IAX_FLAG_FULL) { - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Received packet %d, (%d, %d)\n", fh->oseqno, f.frametype, f.subclass); - /* Check if it's out of order (and not an ACK or INVAL) */ - fr->oseqno = fh->oseqno; - fr->iseqno = fh->iseqno; - fr->ts = ntohl(fh->ts); -#ifdef IAXTESTS - if (test_resync) { - if (option_debug) - ast_log(LOG_DEBUG, "Simulating frame ts resync, was %u now %u\n", fr->ts, fr->ts + test_resync); - fr->ts += test_resync; - } -#endif /* IAXTESTS */ -#if 0 - if ( (ntohs(fh->dcallno) & IAX_FLAG_RETRANS) || - ( (f.frametype != AST_FRAME_VOICE) && ! (f.frametype == AST_FRAME_IAX && - (f.subclass == IAX_COMMAND_NEW || - f.subclass == IAX_COMMAND_AUTHREQ || - f.subclass == IAX_COMMAND_ACCEPT || - f.subclass == IAX_COMMAND_REJECT)) ) ) -#endif - if ((ntohs(fh->dcallno) & IAX_FLAG_RETRANS) || (f.frametype != AST_FRAME_VOICE)) - updatehistory = 0; - if ((iaxs[fr->callno]->iseqno != fr->oseqno) && - (iaxs[fr->callno]->iseqno || - ((f.subclass != IAX_COMMAND_TXCNT) && - (f.subclass != IAX_COMMAND_TXREADY) && /* for attended transfer */ - (f.subclass != IAX_COMMAND_TXREL) && /* for attended transfer */ - (f.subclass != IAX_COMMAND_UNQUELCH ) && /* for attended transfer */ - (f.subclass != IAX_COMMAND_TXACC)) || - (f.frametype != AST_FRAME_IAX))) { - if ( - ((f.subclass != IAX_COMMAND_ACK) && - (f.subclass != IAX_COMMAND_INVAL) && - (f.subclass != IAX_COMMAND_TXCNT) && - (f.subclass != IAX_COMMAND_TXREADY) && /* for attended transfer */ - (f.subclass != IAX_COMMAND_TXREL) && /* for attended transfer */ - (f.subclass != IAX_COMMAND_UNQUELCH ) && /* for attended transfer */ - (f.subclass != IAX_COMMAND_TXACC) && - (f.subclass != IAX_COMMAND_VNAK)) || - (f.frametype != AST_FRAME_IAX)) { - /* If it's not an ACK packet, it's out of order. */ - if (option_debug) - ast_log(LOG_DEBUG, "Packet arrived out of order (expecting %d, got %d) (frametype = %d, subclass = %d)\n", - iaxs[fr->callno]->iseqno, fr->oseqno, f.frametype, f.subclass); - /* Check to see if we need to request retransmission, - * and take sequence number wraparound into account */ - if ((unsigned char) (iaxs[fr->callno]->iseqno - fr->oseqno) < 128) { - /* If we've already seen it, ack it XXX There's a border condition here XXX */ - if ((f.frametype != AST_FRAME_IAX) || - ((f.subclass != IAX_COMMAND_ACK) && (f.subclass != IAX_COMMAND_INVAL))) { - if (option_debug) - ast_log(LOG_DEBUG, "Acking anyway\n"); - /* XXX Maybe we should handle its ack to us, but then again, it's probably outdated anyway, and if - we have anything to send, we'll retransmit and get an ACK back anyway XXX */ - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - } - } else { - /* Send a VNAK requesting retransmission */ - iax2_vnak(fr->callno); - } - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } else { - /* Increment unless it's an ACK or VNAK */ - if (((f.subclass != IAX_COMMAND_ACK) && - (f.subclass != IAX_COMMAND_INVAL) && - (f.subclass != IAX_COMMAND_TXCNT) && - (f.subclass != IAX_COMMAND_TXACC) && - (f.subclass != IAX_COMMAND_VNAK)) || - (f.frametype != AST_FRAME_IAX)) - iaxs[fr->callno]->iseqno++; - } - /* A full frame */ - if (res < sizeof(*fh)) { - ast_log(LOG_WARNING, "midget packet received (%d of %zd min)\n", res, sizeof(*fh)); - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - /* Ensure text frames are NULL-terminated */ - if (f.frametype == AST_FRAME_TEXT && thread->buf[res - 1] != '\0') { - if (res < thread->buf_size) - thread->buf[res++] = '\0'; - else /* Trims one character from the text message, but that's better than overwriting the end of the buffer. */ - thread->buf[res - 1] = '\0'; - } - f.datalen = res - sizeof(*fh); - - /* Handle implicit ACKing unless this is an INVAL, and only if this is - from the real peer, not the transfer peer */ - if (!inaddrcmp(&sin, &iaxs[fr->callno]->addr) && - ((f.subclass != IAX_COMMAND_INVAL) || - (f.frametype != AST_FRAME_IAX))) { - unsigned char x; - int call_to_destroy; - /* XXX This code is not very efficient. Surely there is a better way which still - properly handles boundary conditions? XXX */ - /* First we have to qualify that the ACKed value is within our window */ - for (x=iaxs[fr->callno]->rseqno; x != iaxs[fr->callno]->oseqno; x++) - if (fr->iseqno == x) - break; - if ((x != iaxs[fr->callno]->oseqno) || (iaxs[fr->callno]->oseqno == fr->iseqno)) { - /* The acknowledgement is within our window. Time to acknowledge everything - that it says to */ - for (x=iaxs[fr->callno]->rseqno; x != fr->iseqno; x++) { - /* Ack the packet with the given timestamp */ - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Cancelling transmission of packet %d\n", x); - call_to_destroy = 0; - AST_LIST_LOCK(&iaxq.queue); - AST_LIST_TRAVERSE(&iaxq.queue, cur, list) { - /* If it's our call, and our timestamp, mark -1 retries */ - if ((fr->callno == cur->callno) && (x == cur->oseqno)) { - cur->retries = -1; - /* Destroy call if this is the end */ - if (cur->final) - call_to_destroy = fr->callno; - } - } - AST_LIST_UNLOCK(&iaxq.queue); - if (call_to_destroy) { - if (iaxdebug && option_debug) - ast_log(LOG_DEBUG, "Really destroying %d, having been acked on final message\n", call_to_destroy); - ast_mutex_lock(&iaxsl[call_to_destroy]); - iax2_destroy(call_to_destroy); - ast_mutex_unlock(&iaxsl[call_to_destroy]); - } - } - /* Note how much we've received acknowledgement for */ - if (iaxs[fr->callno]) - iaxs[fr->callno]->rseqno = fr->iseqno; - else { - /* Stop processing now */ - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } else if (option_debug) - ast_log(LOG_DEBUG, "Received iseqno %d not within window %d->%d\n", fr->iseqno, iaxs[fr->callno]->rseqno, iaxs[fr->callno]->oseqno); - } - if (inaddrcmp(&sin, &iaxs[fr->callno]->addr) && - ((f.frametype != AST_FRAME_IAX) || - ((f.subclass != IAX_COMMAND_TXACC) && - (f.subclass != IAX_COMMAND_TXCNT)))) { - /* Only messages we accept from a transfer host are TXACC and TXCNT */ - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - - if (f.datalen) { - if (f.frametype == AST_FRAME_IAX) { - if (iax_parse_ies(&ies, thread->buf + sizeof(*fh), f.datalen)) { - ast_log(LOG_WARNING, "Undecodable frame received from '%s'\n", ast_inet_ntoa(sin.sin_addr)); - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - f.data = NULL; - f.datalen = 0; - } else - f.data = thread->buf + sizeof(*fh); - } else { - if (f.frametype == AST_FRAME_IAX) - f.data = NULL; - else - f.data = empty; - memset(&ies, 0, sizeof(ies)); - } - - /* when we receive the first full frame for a new incoming channel, - it is safe to start the PBX on the channel because we have now - completed a 3-way handshake with the peer */ - if ((f.frametype == AST_FRAME_VOICE) || - (f.frametype == AST_FRAME_VIDEO) || - (f.frametype == AST_FRAME_IAX)) { - if (ast_test_flag(iaxs[fr->callno], IAX_DELAYPBXSTART)) { - ast_clear_flag(iaxs[fr->callno], IAX_DELAYPBXSTART); - if (!ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->chosenformat)) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } - } - - if (f.frametype == AST_FRAME_VOICE) { - if (f.subclass != iaxs[fr->callno]->voiceformat) { - iaxs[fr->callno]->voiceformat = f.subclass; - if (option_debug) - ast_log(LOG_DEBUG, "Ooh, voice format changed to %d\n", f.subclass); - if (iaxs[fr->callno]->owner) { - int orignative; -retryowner: - if (ast_mutex_trylock(&iaxs[fr->callno]->owner->lock)) { - DEADLOCK_AVOIDANCE(&iaxsl[fr->callno]); - if (iaxs[fr->callno] && iaxs[fr->callno]->owner) goto retryowner; - } - if (iaxs[fr->callno]) { - if (iaxs[fr->callno]->owner) { - orignative = iaxs[fr->callno]->owner->nativeformats; - iaxs[fr->callno]->owner->nativeformats = f.subclass; - if (iaxs[fr->callno]->owner->readformat) - ast_set_read_format(iaxs[fr->callno]->owner, iaxs[fr->callno]->owner->readformat); - iaxs[fr->callno]->owner->nativeformats = orignative; - ast_mutex_unlock(&iaxs[fr->callno]->owner->lock); - } - } else { - if (option_debug) - ast_log(LOG_DEBUG, "Neat, somebody took away the channel at a magical time but i found it!\n"); - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } - } - } - if (f.frametype == AST_FRAME_VIDEO) { - if (f.subclass != iaxs[fr->callno]->videoformat) { - if (option_debug) - ast_log(LOG_DEBUG, "Ooh, video format changed to %d\n", f.subclass & ~0x1); - iaxs[fr->callno]->videoformat = f.subclass & ~0x1; - } - } - if (f.frametype == AST_FRAME_CONTROL && iaxs[fr->callno]->owner) { - if (f.subclass == AST_CONTROL_BUSY) { - iaxs[fr->callno]->owner->hangupcause = AST_CAUSE_BUSY; - } else if (f.subclass == AST_CONTROL_CONGESTION) { - iaxs[fr->callno]->owner->hangupcause = AST_CAUSE_CONGESTION; - } - } - if (f.frametype == AST_FRAME_IAX) { - AST_SCHED_DEL(sched, iaxs[fr->callno]->initid); - /* Handle the IAX pseudo frame itself */ - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "IAX subclass %d received\n", f.subclass); - - /* Update last ts unless the frame's timestamp originated with us. */ - if (iaxs[fr->callno]->last < fr->ts && - f.subclass != IAX_COMMAND_ACK && - f.subclass != IAX_COMMAND_PONG && - f.subclass != IAX_COMMAND_LAGRP) { - iaxs[fr->callno]->last = fr->ts; - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "For call=%d, set last=%d\n", fr->callno, fr->ts); - } - - switch(f.subclass) { - case IAX_COMMAND_ACK: - /* Do nothing */ - break; - case IAX_COMMAND_QUELCH: - if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) { - /* Generate Manager Hold event, if necessary*/ - if (iaxs[fr->callno]->owner) { - manager_event(EVENT_FLAG_CALL, "Hold", - "Channel: %s\r\n" - "Uniqueid: %s\r\n", - iaxs[fr->callno]->owner->name, - iaxs[fr->callno]->owner->uniqueid); - } - - ast_set_flag(iaxs[fr->callno], IAX_QUELCH); - if (ies.musiconhold) { - if (iaxs[fr->callno]->owner && ast_bridged_channel(iaxs[fr->callno]->owner)) { - const char *mohsuggest = iaxs[fr->callno]->mohsuggest; - iax2_queue_control_data(fr->callno, AST_CONTROL_HOLD, - S_OR(mohsuggest, NULL), - !ast_strlen_zero(mohsuggest) ? strlen(mohsuggest) + 1 : 0); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } - } - } - break; - case IAX_COMMAND_UNQUELCH: - if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) { - /* Generate Manager Unhold event, if necessary*/ - if (iaxs[fr->callno]->owner && ast_test_flag(iaxs[fr->callno], IAX_QUELCH)) { - manager_event(EVENT_FLAG_CALL, "Unhold", - "Channel: %s\r\n" - "Uniqueid: %s\r\n", - iaxs[fr->callno]->owner->name, - iaxs[fr->callno]->owner->uniqueid); - } - - ast_clear_flag(iaxs[fr->callno], IAX_QUELCH); - if (iaxs[fr->callno]->owner && ast_bridged_channel(iaxs[fr->callno]->owner)) { - iax2_queue_control_data(fr->callno, AST_CONTROL_UNHOLD, NULL, 0); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } - } - break; - case IAX_COMMAND_TXACC: - if (iaxs[fr->callno]->transferring == TRANSFER_BEGIN) { - /* Ack the packet with the given timestamp */ - AST_LIST_LOCK(&iaxq.queue); - AST_LIST_TRAVERSE(&iaxq.queue, cur, list) { - /* Cancel any outstanding txcnt's */ - if ((fr->callno == cur->callno) && (cur->transfer)) - cur->retries = -1; - } - AST_LIST_UNLOCK(&iaxq.queue); - memset(&ied1, 0, sizeof(ied1)); - iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->callno); - send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREADY, 0, ied1.buf, ied1.pos, -1); - iaxs[fr->callno]->transferring = TRANSFER_READY; - } - break; - case IAX_COMMAND_NEW: - /* Ignore if it's already up */ - if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) - break; - if (ies.provverpres && ies.serviceident && sin.sin_addr.s_addr) { - ast_mutex_unlock(&iaxsl[fr->callno]); - check_provisioning(&sin, fd, ies.serviceident, ies.provver); - ast_mutex_lock(&iaxsl[fr->callno]); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } - /* If we're in trunk mode, do it now, and update the trunk number in our frame before continuing */ - if (ast_test_flag(iaxs[fr->callno], IAX_TRUNK)) { - int new_callno; - if ((new_callno = make_trunk(fr->callno, 1)) != -1) - fr->callno = new_callno; - } - /* For security, always ack immediately */ - if (delayreject) - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - if (check_access(fr->callno, &sin, &ies)) { - /* They're not allowed on */ - auth_fail(fr->callno, IAX_COMMAND_REJECT); - if (authdebug) - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, who was trying to reach '%s@%s'\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context); - break; - } - if (strcasecmp(iaxs[fr->callno]->exten, "TBD")) { - const char *context, *exten, *cid_num; - - context = ast_strdupa(iaxs[fr->callno]->context); - exten = ast_strdupa(iaxs[fr->callno]->exten); - cid_num = ast_strdupa(iaxs[fr->callno]->cid_num); - - /* This might re-enter the IAX code and need the lock */ - ast_mutex_unlock(&iaxsl[fr->callno]); - exists = ast_exists_extension(NULL, context, exten, 1, cid_num); - ast_mutex_lock(&iaxsl[fr->callno]); - - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } else - exists = 0; - if (ast_strlen_zero(iaxs[fr->callno]->secret) && ast_strlen_zero(iaxs[fr->callno]->inkeys)) { - if (strcmp(iaxs[fr->callno]->exten, "TBD") && !exists) { - memset(&ied0, 0, sizeof(ied0)); - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension"); - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION); - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - if (authdebug) - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context); - } else { - /* Select an appropriate format */ - - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) { - using_prefs = "reqonly"; - } else { - using_prefs = "disabled"; - } - format = iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability; - memset(&pref, 0, sizeof(pref)); - strcpy(caller_pref_buf, "disabled"); - strcpy(host_pref_buf, "disabled"); - } else { - using_prefs = "mine"; - /* If the information elements are in here... use them */ - if (ies.codec_prefs) - ast_codec_pref_convert(&iaxs[fr->callno]->rprefs, ies.codec_prefs, 32, 0); - if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) { - /* If we are codec_first_choice we let the caller have the 1st shot at picking the codec.*/ - if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { - pref = iaxs[fr->callno]->rprefs; - using_prefs = "caller"; - } else { - pref = iaxs[fr->callno]->prefs; - } - } else - pref = iaxs[fr->callno]->prefs; - - format = ast_codec_choose(&pref, iaxs[fr->callno]->capability & iaxs[fr->callno]->peercapability, 0); - ast_codec_pref_string(&iaxs[fr->callno]->rprefs, caller_pref_buf, sizeof(caller_pref_buf) - 1); - ast_codec_pref_string(&iaxs[fr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1); - } - if (!format) { - if(!ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) - format = iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability; - if (!format) { - memset(&ied0, 0, sizeof(ied0)); - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec"); - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL); - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - if (authdebug) { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability); - else - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability); - } - } else { - /* Pick one... */ - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) { - if(!(iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability)) - format = 0; - } else { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { - using_prefs = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled"; - memset(&pref, 0, sizeof(pref)); - format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability); - strcpy(caller_pref_buf,"disabled"); - strcpy(host_pref_buf,"disabled"); - } else { - using_prefs = "mine"; - if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) { - /* Do the opposite of what we tried above. */ - if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { - pref = iaxs[fr->callno]->prefs; - } else { - pref = iaxs[fr->callno]->rprefs; - using_prefs = "caller"; - } - format = ast_codec_choose(&pref, iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability, 1); - - } else /* if no codec_prefs IE do it the old way */ - format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability); - } - } - - if (!format) { - memset(&ied0, 0, sizeof(ied0)); - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec"); - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL); - ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability); - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - if (authdebug) - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability); - ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE); - break; - } - } - } - if (format) { - /* No authentication required, let them in */ - memset(&ied1, 0, sizeof(ied1)); - iax_ie_append_int(&ied1, IAX_IE_FORMAT, format); - send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1); - if (strcmp(iaxs[fr->callno]->exten, "TBD")) { - ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepting UNAUTHENTICATED call from %s:\n" - "%srequested format = %s,\n" - "%srequested prefs = %s,\n" - "%sactual format = %s,\n" - "%shost prefs = %s,\n" - "%spriority = %s\n", - ast_inet_ntoa(sin.sin_addr), - VERBOSE_PREFIX_4, - ast_getformatname(iaxs[fr->callno]->peerformat), - VERBOSE_PREFIX_4, - caller_pref_buf, - VERBOSE_PREFIX_4, - ast_getformatname(format), - VERBOSE_PREFIX_4, - host_pref_buf, - VERBOSE_PREFIX_4, - using_prefs); - - iaxs[fr->callno]->chosenformat = format; - ast_set_flag(iaxs[fr->callno], IAX_DELAYPBXSTART); - } else { - ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD); - /* If this is a TBD call, we're ready but now what... */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepted unauthenticated TBD call from %s\n", ast_inet_ntoa(sin.sin_addr)); - } - } - } - break; - } - if (iaxs[fr->callno]->authmethods & IAX_AUTH_MD5) - merge_encryption(iaxs[fr->callno],ies.encmethods); - else - iaxs[fr->callno]->encmethods = 0; - if (!authenticate_request(fr->callno) && iaxs[fr->callno]) - ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - break; - case IAX_COMMAND_DPREQ: - /* Request status in the dialplan */ - if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD) && - !ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED) && ies.called_number) { - if (iaxcompat) { - /* Spawn a thread for the lookup */ - spawn_dp_lookup(fr->callno, iaxs[fr->callno]->context, ies.called_number, iaxs[fr->callno]->cid_num); - } else { - /* Just look it up */ - dp_lookup(fr->callno, iaxs[fr->callno]->context, ies.called_number, iaxs[fr->callno]->cid_num, 1); - } - } - break; - case IAX_COMMAND_HANGUP: - ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE); - if (option_debug) - ast_log(LOG_DEBUG, "Immediately destroying %d, having received hangup\n", fr->callno); - /* Set hangup cause according to remote */ - if (ies.causecode && iaxs[fr->callno]->owner) - iaxs[fr->callno]->owner->hangupcause = ies.causecode; - /* Send ack immediately, before we destroy */ - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - iax2_destroy(fr->callno); - break; - case IAX_COMMAND_REJECT: - /* Set hangup cause according to remote */ - if (ies.causecode && iaxs[fr->callno]->owner) - iaxs[fr->callno]->owner->hangupcause = ies.causecode; - - if (!ast_test_flag(iaxs[fr->callno], IAX_PROVISION)) { - if (iaxs[fr->callno]->owner && authdebug) - ast_log(LOG_WARNING, "Call rejected by %s: %s\n", - ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr), - ies.cause ? ies.cause : "<Unknown>"); - if (option_debug) - ast_log(LOG_DEBUG, "Immediately destroying %d, having received reject\n", - fr->callno); - } - /* Send ack immediately, before we destroy */ - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, - fr->ts, NULL, 0, fr->iseqno); - if (!ast_test_flag(iaxs[fr->callno], IAX_PROVISION)) - iaxs[fr->callno]->error = EPERM; - iax2_destroy(fr->callno); - break; - case IAX_COMMAND_TRANSFER: - { - struct ast_channel *bridged_chan; - - if (iaxs[fr->callno]->owner && (bridged_chan = ast_bridged_channel(iaxs[fr->callno]->owner)) && ies.called_number) { - /* Set BLINDTRANSFER channel variables */ - - ast_mutex_unlock(&iaxsl[fr->callno]); - pbx_builtin_setvar_helper(iaxs[fr->callno]->owner, "BLINDTRANSFER", bridged_chan->name); - ast_mutex_lock(&iaxsl[fr->callno]); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - - pbx_builtin_setvar_helper(bridged_chan, "BLINDTRANSFER", iaxs[fr->callno]->owner->name); - if (!strcmp(ies.called_number, ast_parking_ext())) { - struct ast_channel *saved_channel = iaxs[fr->callno]->owner; - ast_mutex_unlock(&iaxsl[fr->callno]); - if (iax_park(bridged_chan, saved_channel)) { - ast_log(LOG_WARNING, "Failed to park call on '%s'\n", bridged_chan->name); - } else { - ast_log(LOG_DEBUG, "Parked call on '%s'\n", bridged_chan->name); - } - ast_mutex_lock(&iaxsl[fr->callno]); - } else { - if (ast_async_goto(bridged_chan, iaxs[fr->callno]->context, ies.called_number, 1)) - ast_log(LOG_WARNING, "Async goto of '%s' to '%s@%s' failed\n", bridged_chan->name, - ies.called_number, iaxs[fr->callno]->context); - else - ast_log(LOG_DEBUG, "Async goto of '%s' to '%s@%s' started\n", bridged_chan->name, - ies.called_number, iaxs[fr->callno]->context); - } - } else - ast_log(LOG_DEBUG, "Async goto not applicable on call %d\n", fr->callno); - - break; - } - case IAX_COMMAND_ACCEPT: - /* Ignore if call is already up or needs authentication or is a TBD */ - if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD | IAX_STATE_AUTHENTICATED)) - break; - if (ast_test_flag(iaxs[fr->callno], IAX_PROVISION)) { - /* Send ack immediately, before we destroy */ - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - iax2_destroy(fr->callno); - break; - } - if (ies.format) { - iaxs[fr->callno]->peerformat = ies.format; - } else { - if (iaxs[fr->callno]->owner) - iaxs[fr->callno]->peerformat = iaxs[fr->callno]->owner->nativeformats; - else - iaxs[fr->callno]->peerformat = iaxs[fr->callno]->capability; - } - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Call accepted by %s (format %s)\n", ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr), ast_getformatname(iaxs[fr->callno]->peerformat)); - if (!(iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability)) { - memset(&ied0, 0, sizeof(ied0)); - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec"); - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL); - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - if (authdebug) - ast_log(LOG_NOTICE, "Rejected call to %s, format 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability); - } else { - ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED); - if (iaxs[fr->callno]->owner) { - /* Switch us to use a compatible format */ - iaxs[fr->callno]->owner->nativeformats = iaxs[fr->callno]->peerformat; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Format for call is %s\n", ast_getformatname(iaxs[fr->callno]->owner->nativeformats)); -retryowner2: - if (ast_mutex_trylock(&iaxs[fr->callno]->owner->lock)) { - DEADLOCK_AVOIDANCE(&iaxsl[fr->callno]); - if (iaxs[fr->callno] && iaxs[fr->callno]->owner) goto retryowner2; - } - - if (iaxs[fr->callno] && iaxs[fr->callno]->owner) { - /* Setup read/write formats properly. */ - if (iaxs[fr->callno]->owner->writeformat) - ast_set_write_format(iaxs[fr->callno]->owner, iaxs[fr->callno]->owner->writeformat); - if (iaxs[fr->callno]->owner->readformat) - ast_set_read_format(iaxs[fr->callno]->owner, iaxs[fr->callno]->owner->readformat); - ast_mutex_unlock(&iaxs[fr->callno]->owner->lock); - } - } - } - if (iaxs[fr->callno]) { - ast_mutex_lock(&dpcache_lock); - dp = iaxs[fr->callno]->dpentries; - while(dp) { - if (!(dp->flags & CACHE_FLAG_TRANSMITTED)) { - iax2_dprequest(dp, fr->callno); - } - dp = dp->peer; - } - ast_mutex_unlock(&dpcache_lock); - } - break; - case IAX_COMMAND_POKE: - /* Send back a pong packet with the original timestamp */ - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_PONG, fr->ts, NULL, 0, -1); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - break; - case IAX_COMMAND_PING: - { - struct iax_ie_data pingied; - construct_rr(iaxs[fr->callno], &pingied); - /* Send back a pong packet with the original timestamp */ - send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_PONG, fr->ts, pingied.buf, pingied.pos, -1); - } - break; - case IAX_COMMAND_PONG: - /* Calculate ping time */ - iaxs[fr->callno]->pingtime = calc_timestamp(iaxs[fr->callno], 0, &f) - fr->ts; - /* save RR info */ - save_rr(fr, &ies); - - if (iaxs[fr->callno]->peerpoke) { - peer = iaxs[fr->callno]->peerpoke; - if ((peer->lastms < 0) || (peer->historicms > peer->maxms)) { - if (iaxs[fr->callno]->pingtime <= peer->maxms) { - ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE! Time: %d\n", peer->name, iaxs[fr->callno]->pingtime); - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Reachable\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime); - ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */ - } - } else if ((peer->historicms > 0) && (peer->historicms <= peer->maxms)) { - if (iaxs[fr->callno]->pingtime > peer->maxms) { - ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED (%d ms)!\n", peer->name, iaxs[fr->callno]->pingtime); - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Lagged\r\nTime: %d\r\n", peer->name, iaxs[fr->callno]->pingtime); - ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */ - } - } - peer->lastms = iaxs[fr->callno]->pingtime; - if (peer->smoothing && (peer->lastms > -1)) - peer->historicms = (iaxs[fr->callno]->pingtime + peer->historicms) / 2; - else if (peer->smoothing && peer->lastms < 0) - peer->historicms = (0 + peer->historicms) / 2; - else - peer->historicms = iaxs[fr->callno]->pingtime; - - /* Remove scheduled iax2_poke_noanswer */ - if (peer->pokeexpire > -1) { - if (!ast_sched_del(sched, peer->pokeexpire)) { - peer_unref(peer); - peer->pokeexpire = -1; - } - } - /* Schedule the next cycle */ - if ((peer->lastms < 0) || (peer->historicms > peer->maxms)) - peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_peer_s, peer_ref(peer)); - else - peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqok, iax2_poke_peer_s, peer_ref(peer)); - if (peer->pokeexpire == -1) - peer_unref(peer); - /* and finally send the ack */ - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - /* And wrap up the qualify call */ - iax2_destroy(fr->callno); - peer->callno = 0; - if (option_debug) - ast_log(LOG_DEBUG, "Peer %s: got pong, lastms %d, historicms %d, maxms %d\n", peer->name, peer->lastms, peer->historicms, peer->maxms); - } - break; - case IAX_COMMAND_LAGRQ: - case IAX_COMMAND_LAGRP: - f.src = "LAGRQ"; - f.mallocd = 0; - f.offset = 0; - f.samples = 0; - iax_frame_wrap(fr, &f); - if(f.subclass == IAX_COMMAND_LAGRQ) { - /* Received a LAGRQ - echo back a LAGRP */ - fr->af.subclass = IAX_COMMAND_LAGRP; - iax2_send(iaxs[fr->callno], &fr->af, fr->ts, -1, 0, 0, 0); - } else { - /* Received LAGRP in response to our LAGRQ */ - unsigned int ts; - /* This is a reply we've been given, actually measure the difference */ - ts = calc_timestamp(iaxs[fr->callno], 0, &fr->af); - iaxs[fr->callno]->lag = ts - fr->ts; - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Peer %s lag measured as %dms\n", - ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr), iaxs[fr->callno]->lag); - } - break; - case IAX_COMMAND_AUTHREQ: - if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) { - ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>"); - break; - } - if (authenticate_reply(iaxs[fr->callno], &iaxs[fr->callno]->addr, &ies, iaxs[fr->callno]->secret, iaxs[fr->callno]->outkey)) { - struct ast_frame hangup_fr = { .frametype = AST_FRAME_CONTROL, - .subclass = AST_CONTROL_HANGUP, - }; - ast_log(LOG_WARNING, - "I don't know how to authenticate %s to %s\n", - ies.username ? ies.username : "<unknown>", ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr)); - iax2_queue_frame(fr->callno, &hangup_fr); - } - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - break; - case IAX_COMMAND_AUTHREP: - /* For security, always ack immediately */ - if (delayreject) - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - /* Ignore once we've started */ - if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED | IAX_STATE_TBD)) { - ast_log(LOG_WARNING, "Call on %s is already up, can't start on it\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>"); - break; - } - if (authenticate_verify(iaxs[fr->callno], &ies)) { - if (authdebug) - ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", ast_inet_ntoa(iaxs[fr->callno]->addr.sin_addr), iaxs[fr->callno]->username); - memset(&ied0, 0, sizeof(ied0)); - auth_fail(fr->callno, IAX_COMMAND_REJECT); - break; - } - if (strcasecmp(iaxs[fr->callno]->exten, "TBD")) { - /* This might re-enter the IAX code and need the lock */ - exists = ast_exists_extension(NULL, iaxs[fr->callno]->context, iaxs[fr->callno]->exten, 1, iaxs[fr->callno]->cid_num); - } else - exists = 0; - if (strcmp(iaxs[fr->callno]->exten, "TBD") && !exists) { - if (authdebug) - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context); - memset(&ied0, 0, sizeof(ied0)); - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension"); - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION); - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } else { - /* Select an appropriate format */ - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) { - using_prefs = "reqonly"; - } else { - using_prefs = "disabled"; - } - format = iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability; - memset(&pref, 0, sizeof(pref)); - strcpy(caller_pref_buf, "disabled"); - strcpy(host_pref_buf, "disabled"); - } else { - using_prefs = "mine"; - if (ies.codec_prefs) - ast_codec_pref_convert(&iaxs[fr->callno]->rprefs, ies.codec_prefs, 32, 0); - if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) { - if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { - pref = iaxs[fr->callno]->rprefs; - using_prefs = "caller"; - } else { - pref = iaxs[fr->callno]->prefs; - } - } else /* if no codec_prefs IE do it the old way */ - pref = iaxs[fr->callno]->prefs; - - format = ast_codec_choose(&pref, iaxs[fr->callno]->capability & iaxs[fr->callno]->peercapability, 0); - ast_codec_pref_string(&iaxs[fr->callno]->rprefs, caller_pref_buf, sizeof(caller_pref_buf) - 1); - ast_codec_pref_string(&iaxs[fr->callno]->prefs, host_pref_buf, sizeof(host_pref_buf) - 1); - } - if (!format) { - if(!ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) { - if (option_debug) - ast_log(LOG_DEBUG, "We don't do requested format %s, falling back to peer capability %d\n", ast_getformatname(iaxs[fr->callno]->peerformat), iaxs[fr->callno]->peercapability); - format = iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability; - } - if (!format) { - if (authdebug) { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability); - else - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability); - } - memset(&ied0, 0, sizeof(ied0)); - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec"); - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL); - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } else { - /* Pick one... */ - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) { - if(!(iaxs[fr->callno]->peerformat & iaxs[fr->callno]->capability)) - format = 0; - } else { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOPREFS)) { - using_prefs = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ? "reqonly" : "disabled"; - memset(&pref, 0, sizeof(pref)); - format = ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP) ? - iaxs[fr->callno]->peerformat : ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability); - strcpy(caller_pref_buf,"disabled"); - strcpy(host_pref_buf,"disabled"); - } else { - using_prefs = "mine"; - if (ast_codec_pref_index(&iaxs[fr->callno]->rprefs, 0)) { - /* Do the opposite of what we tried above. */ - if (ast_test_flag(iaxs[fr->callno], IAX_CODEC_USER_FIRST)) { - pref = iaxs[fr->callno]->prefs; - } else { - pref = iaxs[fr->callno]->rprefs; - using_prefs = "caller"; - } - format = ast_codec_choose(&pref, iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability, 1); - } else /* if no codec_prefs IE do it the old way */ - format = ast_best_codec(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability); - } - } - if (!format) { - ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability); - if (authdebug) { - if(ast_test_flag(iaxs[fr->callno], IAX_CODEC_NOCAP)) - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested 0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->capability); - else - ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat, iaxs[fr->callno]->peercapability, iaxs[fr->callno]->capability); - } - memset(&ied0, 0, sizeof(ied0)); - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "Unable to negotiate codec"); - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL); - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } - } - } - if (format) { - /* Authentication received */ - memset(&ied1, 0, sizeof(ied1)); - iax_ie_append_int(&ied1, IAX_IE_FORMAT, format); - send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1); - if (strcmp(iaxs[fr->callno]->exten, "TBD")) { - ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepting AUTHENTICATED call from %s:\n" - "%srequested format = %s,\n" - "%srequested prefs = %s,\n" - "%sactual format = %s,\n" - "%shost prefs = %s,\n" - "%spriority = %s\n", - ast_inet_ntoa(sin.sin_addr), - VERBOSE_PREFIX_4, - ast_getformatname(iaxs[fr->callno]->peerformat), - VERBOSE_PREFIX_4, - caller_pref_buf, - VERBOSE_PREFIX_4, - ast_getformatname(format), - VERBOSE_PREFIX_4, - host_pref_buf, - VERBOSE_PREFIX_4, - using_prefs); - - ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED); - if(!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format))) - iax2_destroy(fr->callno); - } else { - ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD); - /* If this is a TBD call, we're ready but now what... */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepted AUTHENTICATED TBD call from %s\n", ast_inet_ntoa(sin.sin_addr)); - } - } - } - break; - case IAX_COMMAND_DIAL: - if (ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD)) { - ast_clear_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD); - ast_string_field_set(iaxs[fr->callno], exten, ies.called_number ? ies.called_number : "s"); - if (!ast_exists_extension(NULL, iaxs[fr->callno]->context, iaxs[fr->callno]->exten, 1, iaxs[fr->callno]->cid_num)) { - if (authdebug) - ast_log(LOG_NOTICE, "Rejected dial attempt from %s, request '%s@%s' does not exist\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->exten, iaxs[fr->callno]->context); - memset(&ied0, 0, sizeof(ied0)); - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No such context/extension"); - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_NO_ROUTE_DESTINATION); - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } else { - ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepting DIAL from %s, formats = 0x%x\n", ast_inet_ntoa(sin.sin_addr), iaxs[fr->callno]->peerformat); - ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED); - send_command(iaxs[fr->callno], AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, 0, NULL, 0, -1); - if(!(c = ast_iax2_new(fr->callno, AST_STATE_RING, iaxs[fr->callno]->peerformat))) - iax2_destroy(fr->callno); - } - } - break; - case IAX_COMMAND_INVAL: - iaxs[fr->callno]->error = ENOTCONN; - if (option_debug) - ast_log(LOG_DEBUG, "Immediately destroying %d, having received INVAL\n", fr->callno); - iax2_destroy(fr->callno); - if (option_debug) - ast_log(LOG_DEBUG, "Destroying call %d\n", fr->callno); - break; - case IAX_COMMAND_VNAK: - if (option_debug) - ast_log(LOG_DEBUG, "Received VNAK: resending outstanding frames\n"); - /* Force retransmission */ - vnak_retransmit(fr->callno, fr->iseqno); - break; - case IAX_COMMAND_REGREQ: - case IAX_COMMAND_REGREL: - /* For security, always ack immediately */ - if (delayreject) - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - if (register_verify(fr->callno, &sin, &ies)) { - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - /* Send delayed failure */ - auth_fail(fr->callno, IAX_COMMAND_REGREJ); - break; - } - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - if ((ast_strlen_zero(iaxs[fr->callno]->secret) && ast_strlen_zero(iaxs[fr->callno]->inkeys)) || - ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_AUTHENTICATED | IAX_STATE_UNCHANGED)) { - if (f.subclass == IAX_COMMAND_REGREL) - memset(&sin, 0, sizeof(sin)); - if (update_registry(&sin, fr->callno, ies.devicetype, fd, ies.refresh)) - ast_log(LOG_WARNING, "Registry error\n"); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - if (ies.provverpres && ies.serviceident && sin.sin_addr.s_addr) { - ast_mutex_unlock(&iaxsl[fr->callno]); - check_provisioning(&sin, fd, ies.serviceident, ies.provver); - ast_mutex_lock(&iaxsl[fr->callno]); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } - break; - } - registry_authrequest(fr->callno); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - break; - case IAX_COMMAND_REGACK: - if (iax2_ack_registry(&ies, &sin, fr->callno)) - ast_log(LOG_WARNING, "Registration failure\n"); - /* Send ack immediately, before we destroy */ - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - iax2_destroy(fr->callno); - break; - case IAX_COMMAND_REGREJ: - if (iaxs[fr->callno]->reg) { - if (authdebug) { - ast_log(LOG_NOTICE, "Registration of '%s' rejected: '%s' from: '%s'\n", iaxs[fr->callno]->reg->username, ies.cause ? ies.cause : "<unknown>", ast_inet_ntoa(sin.sin_addr)); - manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelDriver: IAX2\r\nUsername: %s\r\nStatus: Rejected\r\nCause: %s\r\n", iaxs[fr->callno]->reg->username, ies.cause ? ies.cause : "<unknown>"); - } - iaxs[fr->callno]->reg->regstate = REG_STATE_REJECTED; - } - /* Send ack immediately, before we destroy */ - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - iax2_destroy(fr->callno); - break; - case IAX_COMMAND_REGAUTH: - /* Authentication request */ - if (registry_rerequest(&ies, fr->callno, &sin)) { - memset(&ied0, 0, sizeof(ied0)); - iax_ie_append_str(&ied0, IAX_IE_CAUSE, "No authority found"); - iax_ie_append_byte(&ied0, IAX_IE_CAUSECODE, AST_CAUSE_FACILITY_NOT_SUBSCRIBED); - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - } - break; - case IAX_COMMAND_TXREJ: - iaxs[fr->callno]->transferring = 0; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel '%s' unable to transfer\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>"); - memset(&iaxs[fr->callno]->transfer, 0, sizeof(iaxs[fr->callno]->transfer)); - if (iaxs[fr->callno]->bridgecallno) { - if (iaxs[iaxs[fr->callno]->bridgecallno]->transferring) { - iaxs[iaxs[fr->callno]->bridgecallno]->transferring = 0; - send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1); - } - } - break; - case IAX_COMMAND_TXREADY: - if ((iaxs[fr->callno]->transferring == TRANSFER_BEGIN) || - (iaxs[fr->callno]->transferring == TRANSFER_MBEGIN)) { - if (iaxs[fr->callno]->transferring == TRANSFER_MBEGIN) - iaxs[fr->callno]->transferring = TRANSFER_MREADY; - else - iaxs[fr->callno]->transferring = TRANSFER_READY; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel '%s' ready to transfer\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>"); - if (iaxs[fr->callno]->bridgecallno) { - if ((iaxs[iaxs[fr->callno]->bridgecallno]->transferring == TRANSFER_READY) || - (iaxs[iaxs[fr->callno]->bridgecallno]->transferring == TRANSFER_MREADY)) { - /* They're both ready, now release them. */ - if (iaxs[fr->callno]->transferring == TRANSFER_MREADY) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Attempting media bridge of %s and %s\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>", - iaxs[iaxs[fr->callno]->bridgecallno]->owner ? iaxs[iaxs[fr->callno]->bridgecallno]->owner->name : "<Unknown>"); - - iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_MEDIA; - iaxs[fr->callno]->transferring = TRANSFER_MEDIA; - - memset(&ied0, 0, sizeof(ied0)); - memset(&ied1, 0, sizeof(ied1)); - iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[iaxs[fr->callno]->bridgecallno]->peercallno); - iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->peercallno); - send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXMEDIA, 0, ied0.buf, ied0.pos, -1); - send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXMEDIA, 0, ied1.buf, ied1.pos, -1); - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Releasing %s and %s\n", iaxs[fr->callno]->owner ? iaxs[fr->callno]->owner->name : "<Unknown>", - iaxs[iaxs[fr->callno]->bridgecallno]->owner ? iaxs[iaxs[fr->callno]->bridgecallno]->owner->name : "<Unknown>"); - - iaxs[iaxs[fr->callno]->bridgecallno]->transferring = TRANSFER_RELEASED; - iaxs[fr->callno]->transferring = TRANSFER_RELEASED; - ast_set_flag(iaxs[iaxs[fr->callno]->bridgecallno], IAX_ALREADYGONE); - ast_set_flag(iaxs[fr->callno], IAX_ALREADYGONE); - - /* Stop doing lag & ping requests */ - stop_stuff(fr->callno); - stop_stuff(iaxs[fr->callno]->bridgecallno); - - memset(&ied0, 0, sizeof(ied0)); - memset(&ied1, 0, sizeof(ied1)); - iax_ie_append_short(&ied0, IAX_IE_CALLNO, iaxs[iaxs[fr->callno]->bridgecallno]->peercallno); - iax_ie_append_short(&ied1, IAX_IE_CALLNO, iaxs[fr->callno]->peercallno); - send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied0.buf, ied0.pos, -1); - send_command(iaxs[iaxs[fr->callno]->bridgecallno], AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied1.buf, ied1.pos, -1); - } - - } - } - } - break; - case IAX_COMMAND_TXREQ: - try_transfer(iaxs[fr->callno], &ies); - break; - case IAX_COMMAND_TXCNT: - if (iaxs[fr->callno]->transferring) - send_command_transfer(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_TXACC, 0, NULL, 0); - break; - case IAX_COMMAND_TXREL: - /* Send ack immediately, rather than waiting until we've changed addresses */ - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - complete_transfer(fr->callno, &ies); - stop_stuff(fr->callno); /* for attended transfer to work with libiax */ - break; - case IAX_COMMAND_TXMEDIA: - if (iaxs[fr->callno]->transferring == TRANSFER_READY) { - AST_LIST_LOCK(&iaxq.queue); - AST_LIST_TRAVERSE(&iaxq.queue, cur, list) { - /* Cancel any outstanding frames and start anew */ - if ((fr->callno == cur->callno) && (cur->transfer)) { - cur->retries = -1; - } - } - AST_LIST_UNLOCK(&iaxq.queue); - /* Start sending our media to the transfer address, but otherwise leave the call as-is */ - iaxs[fr->callno]->transferring = TRANSFER_MEDIAPASS; - } - break; - case IAX_COMMAND_DPREP: - complete_dpreply(iaxs[fr->callno], &ies); - break; - case IAX_COMMAND_UNSUPPORT: - ast_log(LOG_NOTICE, "Peer did not understand our iax command '%d'\n", ies.iax_unknown); - break; - case IAX_COMMAND_FWDOWNL: - /* Firmware download */ - if (!ast_test_flag(&globalflags, IAX_ALLOWFWDOWNLOAD)) { - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_UNSUPPORT, 0, NULL, 0, -1); - break; - } - memset(&ied0, 0, sizeof(ied0)); - res = iax_firmware_append(&ied0, (unsigned char *)ies.devicetype, ies.fwdesc); - if (res < 0) - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied0.buf, ied0.pos, -1); - else if (res > 0) - send_command_final(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_FWDATA, 0, ied0.buf, ied0.pos, -1); - else - send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_FWDATA, 0, ied0.buf, ied0.pos, -1); - if (!iaxs[fr->callno]) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - break; - default: - if (option_debug) - ast_log(LOG_DEBUG, "Unknown IAX command %d on %d/%d\n", f.subclass, fr->callno, iaxs[fr->callno]->peercallno); - memset(&ied0, 0, sizeof(ied0)); - iax_ie_append_byte(&ied0, IAX_IE_IAX_UNKNOWN, f.subclass); - send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_UNSUPPORT, 0, ied0.buf, ied0.pos, -1); - } - /* Don't actually pass these frames along */ - if ((f.subclass != IAX_COMMAND_ACK) && - (f.subclass != IAX_COMMAND_TXCNT) && - (f.subclass != IAX_COMMAND_TXACC) && - (f.subclass != IAX_COMMAND_INVAL) && - (f.subclass != IAX_COMMAND_VNAK)) { - if (iaxs[fr->callno] && iaxs[fr->callno]->aseqno != iaxs[fr->callno]->iseqno) - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - } - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - /* Unless this is an ACK or INVAL frame, ack it */ - if (iaxs[fr->callno] && iaxs[fr->callno]->aseqno != iaxs[fr->callno]->iseqno) - send_command_immediate(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr->ts, NULL, 0,fr->iseqno); - } else if (minivid) { - f.frametype = AST_FRAME_VIDEO; - if (iaxs[fr->callno]->videoformat > 0) - f.subclass = iaxs[fr->callno]->videoformat | (ntohs(vh->ts) & 0x8000 ? 1 : 0); - else { - ast_log(LOG_WARNING, "Received mini frame before first full video frame\n"); - iax2_vnak(fr->callno); - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - f.datalen = res - sizeof(*vh); - if (f.datalen) - f.data = thread->buf + sizeof(*vh); - else - f.data = NULL; -#ifdef IAXTESTS - if (test_resync) { - fr->ts = (iaxs[fr->callno]->last & 0xFFFF8000L) | ((ntohs(vh->ts) + test_resync) & 0x7fff); - } else -#endif /* IAXTESTS */ - fr->ts = (iaxs[fr->callno]->last & 0xFFFF8000L) | (ntohs(vh->ts) & 0x7fff); - } else { - /* A mini frame */ - f.frametype = AST_FRAME_VOICE; - if (iaxs[fr->callno]->voiceformat > 0) - f.subclass = iaxs[fr->callno]->voiceformat; - else { - if (option_debug) - ast_log(LOG_DEBUG, "Received mini frame before first full voice frame\n"); - iax2_vnak(fr->callno); - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - f.datalen = res - sizeof(struct ast_iax2_mini_hdr); - if (f.datalen < 0) { - ast_log(LOG_WARNING, "Datalen < 0?\n"); - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - if (f.datalen) - f.data = thread->buf + sizeof(*mh); - else - f.data = NULL; -#ifdef IAXTESTS - if (test_resync) { - fr->ts = (iaxs[fr->callno]->last & 0xFFFF0000L) | ((ntohs(mh->ts) + test_resync) & 0xffff); - } else -#endif /* IAXTESTS */ - fr->ts = (iaxs[fr->callno]->last & 0xFFFF0000L) | ntohs(mh->ts); - /* FIXME? Surely right here would be the right place to undo timestamp wraparound? */ - } - /* Don't pass any packets until we're started */ - if (!ast_test_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED)) { - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; - } - /* Common things */ - f.src = "IAX2"; - f.mallocd = 0; - f.offset = 0; - f.len = 0; - if (f.datalen && (f.frametype == AST_FRAME_VOICE)) { - f.samples = ast_codec_get_samples(&f); - /* We need to byteswap incoming slinear samples from network byte order */ - if (f.subclass == AST_FORMAT_SLINEAR) - ast_frame_byteswap_be(&f); - } else - f.samples = 0; - iax_frame_wrap(fr, &f); - - /* If this is our most recent packet, use it as our basis for timestamping */ - if (iaxs[fr->callno] && iaxs[fr->callno]->last < fr->ts) { - /*iaxs[fr->callno]->last = fr->ts; (do it afterwards cos schedule/forward_delivery needs the last ts too)*/ - fr->outoforder = 0; - } else { - if (option_debug && iaxdebug && iaxs[fr->callno]) - ast_log(LOG_DEBUG, "Received out of order packet... (type=%d, subclass %d, ts = %d, last = %d)\n", f.frametype, f.subclass, fr->ts, iaxs[fr->callno]->last); - fr->outoforder = -1; - } - fr->cacheable = ((f.frametype == AST_FRAME_VOICE) || (f.frametype == AST_FRAME_VIDEO)); - duped_fr = iaxfrdup2(fr); - if (duped_fr) { - schedule_delivery(duped_fr, updatehistory, 0, &fr->ts); - } - if (iaxs[fr->callno] && iaxs[fr->callno]->last < fr->ts) { - iaxs[fr->callno]->last = fr->ts; -#if 1 - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "For call=%d, set last=%d\n", fr->callno, fr->ts); -#endif - } - - /* Always run again */ - ast_mutex_unlock(&iaxsl[fr->callno]); - return 1; -} - -/* Function to clean up process thread if it is cancelled */ -static void iax2_process_thread_cleanup(void *data) -{ - struct iax2_thread *thread = data; - ast_mutex_destroy(&thread->lock); - ast_cond_destroy(&thread->cond); - free(thread); - ast_atomic_dec_and_test(&iaxactivethreadcount); -} - -static void *iax2_process_thread(void *data) -{ - struct iax2_thread *thread = data; - struct timeval tv; - struct timespec ts; - int put_into_idle = 0; - - ast_atomic_fetchadd_int(&iaxactivethreadcount,1); - pthread_cleanup_push(iax2_process_thread_cleanup, data); - for(;;) { - /* Wait for something to signal us to be awake */ - ast_mutex_lock(&thread->lock); - - /* Flag that we're ready to accept signals */ - thread->ready_for_signal = 1; - - /* Put into idle list if applicable */ - if (put_into_idle) - insert_idle_thread(thread); - - if (thread->type == IAX_TYPE_DYNAMIC) { - struct iax2_thread *t = NULL; - /* Wait to be signalled or time out */ - tv = ast_tvadd(ast_tvnow(), ast_samp2tv(30000, 1000)); - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; - if (ast_cond_timedwait(&thread->cond, &thread->lock, &ts) == ETIMEDOUT) { - /* This thread was never put back into the available dynamic - * thread list, so just go away. */ - if (!put_into_idle) { - ast_mutex_unlock(&thread->lock); - break; - } - AST_LIST_LOCK(&dynamic_list); - /* Account for the case where this thread is acquired *right* after a timeout */ - if ((t = AST_LIST_REMOVE(&dynamic_list, thread, list))) - iaxdynamicthreadcount--; - AST_LIST_UNLOCK(&dynamic_list); - if (t) { - /* This dynamic thread timed out waiting for a task and was - * not acquired immediately after the timeout, - * so it's time to go away. */ - ast_mutex_unlock(&thread->lock); - break; - } - /* Someone grabbed our thread *right* after we timed out. - * Wait for them to set us up with something to do and signal - * us to continue. */ - tv = ast_tvadd(ast_tvnow(), ast_samp2tv(30000, 1000)); - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; - if (ast_cond_timedwait(&thread->cond, &thread->lock, &ts) == ETIMEDOUT) - { - ast_mutex_unlock(&thread->lock); - break; - } - } - } else { - ast_cond_wait(&thread->cond, &thread->lock); - } - - /* Go back into our respective list */ - put_into_idle = 1; - - ast_mutex_unlock(&thread->lock); - - if (thread->iostate == IAX_IOSTATE_IDLE) - continue; - - /* Add ourselves to the active list now */ - AST_LIST_LOCK(&active_list); - AST_LIST_INSERT_HEAD(&active_list, thread, list); - AST_LIST_UNLOCK(&active_list); - - /* See what we need to do */ - switch(thread->iostate) { - case IAX_IOSTATE_READY: - thread->actions++; - thread->iostate = IAX_IOSTATE_PROCESSING; - socket_process(thread); - handle_deferred_full_frames(thread); - break; - case IAX_IOSTATE_SCHEDREADY: - thread->actions++; - thread->iostate = IAX_IOSTATE_PROCESSING; -#ifdef SCHED_MULTITHREADED - thread->schedfunc(thread->scheddata); -#endif - break; - } - time(&thread->checktime); - thread->iostate = IAX_IOSTATE_IDLE; -#ifdef DEBUG_SCHED_MULTITHREAD - thread->curfunc[0]='\0'; -#endif - - /* Now... remove ourselves from the active list, and return to the idle list */ - AST_LIST_LOCK(&active_list); - AST_LIST_REMOVE(&active_list, thread, list); - AST_LIST_UNLOCK(&active_list); - - /* Make sure another frame didn't sneak in there after we thought we were done. */ - handle_deferred_full_frames(thread); - } - - /*!\note For some reason, idle threads are exiting without being removed - * from an idle list, which is causing memory corruption. Forcibly remove - * it from the list, if it's there. - */ - AST_LIST_LOCK(&idle_list); - AST_LIST_REMOVE(&idle_list, thread, list); - AST_LIST_UNLOCK(&idle_list); - - AST_LIST_LOCK(&dynamic_list); - AST_LIST_REMOVE(&dynamic_list, thread, list); - AST_LIST_UNLOCK(&dynamic_list); - - /* I am exiting here on my own volition, I need to clean up my own data structures - * Assume that I am no longer in any of the lists (idle, active, or dynamic) - */ - pthread_cleanup_pop(1); - - return NULL; -} - -static int iax2_do_register(struct iax2_registry *reg) -{ - struct iax_ie_data ied; - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Sending registration request for '%s'\n", reg->username); - - if (reg->dnsmgr && - ((reg->regstate == REG_STATE_TIMEOUT) || !reg->addr.sin_addr.s_addr)) { - /* Maybe the IP has changed, force DNS refresh */ - ast_dnsmgr_refresh(reg->dnsmgr); - } - - /* - * if IP has Changed, free allocated call to create a new one with new IP - * call has the pointer to IP and must be updated to the new one - */ - if (reg->dnsmgr && ast_dnsmgr_changed(reg->dnsmgr) && (reg->callno > 0)) { - int callno = reg->callno; - ast_mutex_lock(&iaxsl[callno]); - iax2_destroy(callno); - ast_mutex_unlock(&iaxsl[callno]); - reg->callno = 0; - } - if (!reg->addr.sin_addr.s_addr) { - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "Unable to send registration request for '%s' without IP address\n", reg->username); - /* Setup the next registration attempt */ - AST_SCHED_DEL(sched, reg->expire); - reg->expire = iax2_sched_add(sched, (5 * reg->refresh / 6) * 1000, iax2_do_register_s, reg); - return -1; - } - - if (!reg->callno) { - if (option_debug) - ast_log(LOG_DEBUG, "Allocate call number\n"); - reg->callno = find_callno_locked(0, 0, ®->addr, NEW_FORCE, defaultsockfd, 0); - if (reg->callno < 1) { - ast_log(LOG_WARNING, "Unable to create call for registration\n"); - return -1; - } else if (option_debug) - ast_log(LOG_DEBUG, "Registration created on call %d\n", reg->callno); - iaxs[reg->callno]->reg = reg; - ast_mutex_unlock(&iaxsl[reg->callno]); - } - /* Schedule the next registration attempt */ - AST_SCHED_DEL(sched, reg->expire); - /* Setup the next registration a little early */ - reg->expire = iax2_sched_add(sched, (5 * reg->refresh / 6) * 1000, iax2_do_register_s, reg); - /* Send the request */ - memset(&ied, 0, sizeof(ied)); - iax_ie_append_str(&ied, IAX_IE_USERNAME, reg->username); - iax_ie_append_short(&ied, IAX_IE_REFRESH, reg->refresh); - send_command(iaxs[reg->callno],AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1); - reg->regstate = REG_STATE_REGSENT; - return 0; -} - -static char *iax2_prov_complete_template_3rd(const char *line, const char *word, int pos, int state) -{ - if (pos != 3) - return NULL; - return iax_prov_complete_template(line, word, pos, state); -} - -static int iax2_provision(struct sockaddr_in *end, int sockfd, char *dest, const char *template, int force) -{ - /* Returns 1 if provisioned, -1 if not able to find destination, or 0 if no provisioning - is found for template */ - struct iax_ie_data provdata; - struct iax_ie_data ied; - unsigned int sig; - struct sockaddr_in sin; - int callno; - struct create_addr_info cai; - - memset(&cai, 0, sizeof(cai)); - - if (option_debug) - ast_log(LOG_DEBUG, "Provisioning '%s' from template '%s'\n", dest, template); - - if (iax_provision_build(&provdata, &sig, template, force)) { - if (option_debug) - ast_log(LOG_DEBUG, "No provisioning found for template '%s'\n", template); - return 0; - } - - if (end) { - memcpy(&sin, end, sizeof(sin)); - cai.sockfd = sockfd; - } else if (create_addr(dest, NULL, &sin, &cai)) - return -1; - - /* Build the rest of the message */ - memset(&ied, 0, sizeof(ied)); - iax_ie_append_raw(&ied, IAX_IE_PROVISIONING, provdata.buf, provdata.pos); - - callno = find_callno_locked(0, 0, &sin, NEW_FORCE, cai.sockfd, 0); - if (!callno) - return -1; - - if (iaxs[callno]) { - /* Schedule autodestruct in case they don't ever give us anything back */ - AST_SCHED_DEL(sched, iaxs[callno]->autoid); - iaxs[callno]->autoid = iax2_sched_add(sched, 15000, auto_hangup, (void *)(long)callno); - ast_set_flag(iaxs[callno], IAX_PROVISION); - /* Got a call number now, so go ahead and send the provisioning information */ - send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_PROVISION, 0, ied.buf, ied.pos, -1); - } - ast_mutex_unlock(&iaxsl[callno]); - - return 1; -} - -static char *papp = "IAX2Provision"; -static char *psyn = "Provision a calling IAXy with a given template"; -static char *pdescrip = -" IAX2Provision([template]): Provisions the calling IAXy (assuming\n" -"the calling entity is in fact an IAXy) with the given template or\n" -"default if one is not specified. Returns -1 on error or 0 on success.\n"; - -/*! iax2provision -\ingroup applications -*/ -static int iax2_prov_app(struct ast_channel *chan, void *data) -{ - int res; - char *sdata; - char *opts; - int force =0; - unsigned short callno = PTR_TO_CALLNO(chan->tech_pvt); - if (ast_strlen_zero(data)) - data = "default"; - sdata = ast_strdupa(data); - opts = strchr(sdata, '|'); - if (opts) - *opts='\0'; - - if (chan->tech != &iax2_tech) { - ast_log(LOG_NOTICE, "Can't provision a non-IAX device!\n"); - return -1; - } - if (!callno || !iaxs[callno] || !iaxs[callno]->addr.sin_addr.s_addr) { - ast_log(LOG_NOTICE, "Can't provision something with no IP?\n"); - return -1; - } - res = iax2_provision(&iaxs[callno]->addr, iaxs[callno]->sockfd, NULL, sdata, force); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Provisioned IAXY at '%s' with '%s'= %d\n", - ast_inet_ntoa(iaxs[callno]->addr.sin_addr), - sdata, res); - return res; -} - - -static int iax2_prov_cmd(int fd, int argc, char *argv[]) -{ - int force = 0; - int res; - if (argc < 4) - return RESULT_SHOWUSAGE; - if ((argc > 4)) { - if (!strcasecmp(argv[4], "forced")) - force = 1; - else - return RESULT_SHOWUSAGE; - } - res = iax2_provision(NULL, -1, argv[2], argv[3], force); - if (res < 0) - ast_cli(fd, "Unable to find peer/address '%s'\n", argv[2]); - else if (res < 1) - ast_cli(fd, "No template (including wildcard) matching '%s'\n", argv[3]); - else - ast_cli(fd, "Provisioning '%s' with template '%s'%s\n", argv[2], argv[3], force ? ", forced" : ""); - return RESULT_SUCCESS; -} - -static void __iax2_poke_noanswer(const void *data) -{ - struct iax2_peer *peer = (struct iax2_peer *)data; - int callno; - - if (peer->lastms > -1) { - ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Time: %d\n", peer->name, peer->lastms); - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, peer->lastms); - ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */ - } - if ((callno = peer->callno) > 0) { - ast_mutex_lock(&iaxsl[callno]); - iax2_destroy(callno); - ast_mutex_unlock(&iaxsl[callno]); - } - peer->callno = 0; - peer->lastms = -1; - /* Try again quickly */ - peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_peer_s, peer_ref(peer)); - if (peer->pokeexpire == -1) - peer_unref(peer); -} - -static int iax2_poke_noanswer(const void *data) -{ - struct iax2_peer *peer = (struct iax2_peer *)data; - peer->pokeexpire = -1; -#ifdef SCHED_MULTITHREADED - if (schedule_action(__iax2_poke_noanswer, data)) -#endif - __iax2_poke_noanswer(data); - peer_unref(peer); - return 0; -} - -static int iax2_poke_peer_cb(void *obj, void *arg, int flags) -{ - struct iax2_peer *peer = obj; - - iax2_poke_peer(peer, 0); - - return 0; -} - -static int iax2_poke_peer(struct iax2_peer *peer, int heldcall) -{ - int callno; - if (!peer->maxms || (!peer->addr.sin_addr.s_addr && !peer->dnsmgr)) { - /* IF we have no IP without dnsmgr, or this isn't to be monitored, return - immediately after clearing things out */ - peer->lastms = 0; - peer->historicms = 0; - peer->pokeexpire = -1; - peer->callno = 0; - return 0; - } - - /* The peer could change the callno inside iax2_destroy, since we do deadlock avoidance */ - if ((callno = peer->callno) > 0) { - ast_log(LOG_NOTICE, "Still have a callno...\n"); - ast_mutex_lock(&iaxsl[callno]); - iax2_destroy(callno); - ast_mutex_unlock(&iaxsl[callno]); - } - if (heldcall) - ast_mutex_unlock(&iaxsl[heldcall]); - callno = peer->callno = find_callno(0, 0, &peer->addr, NEW_FORCE, peer->sockfd, 0); - if (heldcall) - ast_mutex_lock(&iaxsl[heldcall]); - if (peer->callno < 1) { - ast_log(LOG_WARNING, "Unable to allocate call for poking peer '%s'\n", peer->name); - return -1; - } - - /* Speed up retransmission times for this qualify call */ - iaxs[peer->callno]->pingtime = peer->maxms / 4 + 1; - iaxs[peer->callno]->peerpoke = peer; - - /* Remove any pending pokeexpire task */ - if (peer->pokeexpire > -1) { - if (!ast_sched_del(sched, peer->pokeexpire)) { - peer->pokeexpire = -1; - peer_unref(peer); - } - } - - /* Queue up a new task to handle no reply */ - /* If the host is already unreachable then use the unreachable interval instead */ - if (peer->lastms < 0) { - peer->pokeexpire = iax2_sched_add(sched, peer->pokefreqnotok, iax2_poke_noanswer, peer_ref(peer)); - } else - peer->pokeexpire = iax2_sched_add(sched, DEFAULT_MAXMS * 2, iax2_poke_noanswer, peer_ref(peer)); - - if (peer->pokeexpire == -1) - peer_unref(peer); - - /* And send the poke */ - ast_mutex_lock(&iaxsl[callno]); - if (iaxs[callno]) { - send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, NULL, 0, -1); - } - ast_mutex_unlock(&iaxsl[callno]); - - return 0; -} - -static void free_context(struct iax2_context *con) -{ - struct iax2_context *conl; - while(con) { - conl = con; - con = con->next; - free(conl); - } -} - -static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause) -{ - int callno; - int res; - int fmt, native; - struct sockaddr_in sin; - struct ast_channel *c; - struct parsed_dial_string pds; - struct create_addr_info cai; - char *tmpstr; - - memset(&pds, 0, sizeof(pds)); - tmpstr = ast_strdupa(data); - parse_dial_string(tmpstr, &pds); - - if (ast_strlen_zero(pds.peer)) { - ast_log(LOG_WARNING, "No peer provided in the IAX2 dial string '%s'\n", (char *) data); - return NULL; - } - - memset(&cai, 0, sizeof(cai)); - cai.capability = iax2_capability; - - ast_copy_flags(&cai, &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); - - /* Populate our address from the given */ - if (create_addr(pds.peer, NULL, &sin, &cai)) { - *cause = AST_CAUSE_UNREGISTERED; - return NULL; - } - - if (pds.port) - sin.sin_port = htons(atoi(pds.port)); - - callno = find_callno_locked(0, 0, &sin, NEW_FORCE, cai.sockfd, 0); - if (callno < 1) { - ast_log(LOG_WARNING, "Unable to create call\n"); - *cause = AST_CAUSE_CONGESTION; - return NULL; - } - - /* If this is a trunk, update it now */ - ast_copy_flags(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); - if (ast_test_flag(&cai, IAX_TRUNK)) { - int new_callno; - if ((new_callno = make_trunk(callno, 1)) != -1) - callno = new_callno; - } - iaxs[callno]->maxtime = cai.maxtime; - if (cai.found) - ast_string_field_set(iaxs[callno], host, pds.peer); - - c = ast_iax2_new(callno, AST_STATE_DOWN, cai.capability); - - ast_mutex_unlock(&iaxsl[callno]); - - if (c) { - /* Choose a format we can live with */ - if (c->nativeformats & format) - c->nativeformats &= format; - else { - native = c->nativeformats; - fmt = format; - res = ast_translator_best_choice(&fmt, &native); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to create translator path for %s to %s on %s\n", - ast_getformatname(c->nativeformats), ast_getformatname(fmt), c->name); - ast_hangup(c); - return NULL; - } - c->nativeformats = native; - } - c->readformat = ast_best_codec(c->nativeformats); - c->writeformat = c->readformat; - } - - return c; -} - -static void *sched_thread(void *ignore) -{ - int count; - int res; - struct timeval tv; - struct timespec ts; - - for (;;) { - pthread_testcancel(); - ast_mutex_lock(&sched_lock); - res = ast_sched_wait(sched); - if ((res > 1000) || (res < 0)) - res = 1000; - tv = ast_tvadd(ast_tvnow(), ast_samp2tv(res, 1000)); - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; - ast_cond_timedwait(&sched_cond, &sched_lock, &ts); - ast_mutex_unlock(&sched_lock); - pthread_testcancel(); - - count = ast_sched_runq(sched); - if (option_debug && count >= 20) - ast_log(LOG_DEBUG, "chan_iax2: ast_sched_runq ran %d scheduled tasks all at once\n", count); - } - return NULL; -} - -static void *network_thread(void *ignore) -{ - /* Our job is simple: Send queued messages, retrying if necessary. Read frames - from the network, and queue them for delivery to the channels */ - int res, count, wakeup; - struct iax_frame *f; - - if (timingfd > -1) - ast_io_add(io, timingfd, timing_read, AST_IO_IN | AST_IO_PRI, NULL); - - for(;;) { - pthread_testcancel(); - - /* Go through the queue, sending messages which have not yet been - sent, and scheduling retransmissions if appropriate */ - AST_LIST_LOCK(&iaxq.queue); - count = 0; - wakeup = -1; - AST_LIST_TRAVERSE_SAFE_BEGIN(&iaxq.queue, f, list) { - if (f->sentyet) - continue; - - /* Try to lock the pvt, if we can't... don't fret - defer it till later */ - if (ast_mutex_trylock(&iaxsl[f->callno])) { - wakeup = 1; - continue; - } - - f->sentyet++; - - if (iaxs[f->callno]) { - send_packet(f); - count++; - } - - ast_mutex_unlock(&iaxsl[f->callno]); - - if (f->retries < 0) { - /* This is not supposed to be retransmitted */ - AST_LIST_REMOVE_CURRENT(&iaxq.queue, list); - iaxq.count--; - /* Free the iax frame */ - iax_frame_free(f); - } else { - /* We need reliable delivery. Schedule a retransmission */ - f->retries++; - f->retrans = iax2_sched_add(sched, f->retrytime, attempt_transmit, f); - } - } - AST_LIST_TRAVERSE_SAFE_END - AST_LIST_UNLOCK(&iaxq.queue); - - pthread_testcancel(); - - if (option_debug && count >= 20) - ast_log(LOG_DEBUG, "chan_iax2: Sent %d queued outbound frames all at once\n", count); - - /* Now do the IO, and run scheduled tasks */ - res = ast_io_wait(io, wakeup); - if (res >= 0) { - if (option_debug && res >= 20) - ast_log(LOG_DEBUG, "chan_iax2: ast_io_wait ran %d I/Os all at once\n", res); - } - } - return NULL; -} - -static int start_network_thread(void) -{ - pthread_attr_t attr; - int threadcount = 0; - int x; - for (x = 0; x < iaxthreadcount; x++) { - struct iax2_thread *thread = ast_calloc(1, sizeof(struct iax2_thread)); - if (thread) { - thread->type = IAX_TYPE_POOL; - thread->threadnum = ++threadcount; - ast_mutex_init(&thread->lock); - ast_cond_init(&thread->cond, NULL); - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (ast_pthread_create(&thread->threadid, &attr, iax2_process_thread, thread)) { - ast_log(LOG_WARNING, "Failed to create new thread!\n"); - free(thread); - thread = NULL; - } - AST_LIST_LOCK(&idle_list); - AST_LIST_INSERT_TAIL(&idle_list, thread, list); - AST_LIST_UNLOCK(&idle_list); - } - } - ast_pthread_create_background(&schedthreadid, NULL, sched_thread, NULL); - ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL); - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "%d helper threads started\n", threadcount); - return 0; -} - -static struct iax2_context *build_context(char *context) -{ - struct iax2_context *con; - - if ((con = ast_calloc(1, sizeof(*con)))) - ast_copy_string(con->context, context, sizeof(con->context)); - - return con; -} - -static int get_auth_methods(char *value) -{ - int methods = 0; - if (strstr(value, "rsa")) - methods |= IAX_AUTH_RSA; - if (strstr(value, "md5")) - methods |= IAX_AUTH_MD5; - if (strstr(value, "plaintext")) - methods |= IAX_AUTH_PLAINTEXT; - return methods; -} - - -/*! \brief Check if address can be used as packet source. - \return 0 address available, 1 address unavailable, -1 error -*/ -static int check_srcaddr(struct sockaddr *sa, socklen_t salen) -{ - int sd; - int res; - - sd = socket(AF_INET, SOCK_DGRAM, 0); - if (sd < 0) { - ast_log(LOG_ERROR, "Socket: %s\n", strerror(errno)); - return -1; - } - - res = bind(sd, sa, salen); - if (res < 0) { - if (option_debug) - ast_log(LOG_DEBUG, "Can't bind: %s\n", strerror(errno)); - close(sd); - return 1; - } - - close(sd); - return 0; -} - -/*! \brief Parse the "sourceaddress" value, - lookup in netsock list and set peer's sockfd. Defaults to defaultsockfd if - not found. */ -static int peer_set_srcaddr(struct iax2_peer *peer, const char *srcaddr) -{ - struct sockaddr_in sin; - int nonlocal = 1; - int port = IAX_DEFAULT_PORTNO; - int sockfd = defaultsockfd; - char *tmp; - char *addr; - char *portstr; - - if (!(tmp = ast_strdupa(srcaddr))) - return -1; - - addr = strsep(&tmp, ":"); - portstr = tmp; - - if (portstr) { - port = atoi(portstr); - if (port < 1) - port = IAX_DEFAULT_PORTNO; - } - - if (!ast_get_ip(&sin, addr)) { - struct ast_netsock *sock; - int res; - - sin.sin_port = 0; - sin.sin_family = AF_INET; - res = check_srcaddr((struct sockaddr *) &sin, sizeof(sin)); - if (res == 0) { - /* ip address valid. */ - sin.sin_port = htons(port); - if (!(sock = ast_netsock_find(netsock, &sin))) - sock = ast_netsock_find(outsock, &sin); - if (sock) { - sockfd = ast_netsock_sockfd(sock); - nonlocal = 0; - } else { - unsigned int orig_saddr = sin.sin_addr.s_addr; - /* INADDR_ANY matches anyway! */ - sin.sin_addr.s_addr = INADDR_ANY; - if (ast_netsock_find(netsock, &sin)) { - sin.sin_addr.s_addr = orig_saddr; - sock = ast_netsock_bind(outsock, io, srcaddr, port, tos, socket_read, NULL); - if (sock) { - sockfd = ast_netsock_sockfd(sock); - ast_netsock_unref(sock); - nonlocal = 0; - } else { - nonlocal = 2; - } - } - } - } - } - - peer->sockfd = sockfd; - - if (nonlocal == 1) { - ast_log(LOG_WARNING, "Non-local or unbound address specified (%s) in sourceaddress for '%s', reverting to default\n", - srcaddr, peer->name); - return -1; - } else if (nonlocal == 2) { - ast_log(LOG_WARNING, "Unable to bind to sourceaddress '%s' for '%s', reverting to default\n", - srcaddr, peer->name); - return -1; - } else { - if (option_debug) - ast_log(LOG_DEBUG, "Using sourceaddress %s for '%s'\n", srcaddr, peer->name); - return 0; - } -} - -static void peer_destructor(void *obj) -{ - struct iax2_peer *peer = obj; - int callno = peer->callno; - - ast_free_ha(peer->ha); - - if (callno > 0) { - ast_mutex_lock(&iaxsl[callno]); - iax2_destroy(callno); - ast_mutex_unlock(&iaxsl[callno]); - } - - register_peer_exten(peer, 0); - - if (peer->dnsmgr) - ast_dnsmgr_release(peer->dnsmgr); - - ast_string_field_free_memory(peer); -} - -/*! \brief Create peer structure based on configuration */ -static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly) -{ - struct iax2_peer *peer = NULL; - struct ast_ha *oldha = NULL; - int maskfound=0; - int found=0; - int firstpass=1; - struct iax2_peer tmp_peer = { - .name = name, - }; - - if (!temponly) { - peer = ao2_find(peers, &tmp_peer, OBJ_POINTER); - if (peer && !ast_test_flag(peer, IAX_DELME)) - firstpass = 0; - } - - if (peer) { - found++; - if (firstpass) { - oldha = peer->ha; - peer->ha = NULL; - } - unlink_peer(peer); - } else if ((peer = ao2_alloc(sizeof(*peer), peer_destructor))) { - peer->expire = -1; - peer->pokeexpire = -1; - peer->sockfd = defaultsockfd; - if (ast_string_field_init(peer, 32)) - peer = peer_unref(peer); - } - - if (peer) { - if (firstpass) { - ast_copy_flags(peer, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF); - peer->encmethods = iax2_encryption; - peer->adsi = adsi; - ast_string_field_set(peer,secret,""); - if (!found) { - ast_string_field_set(peer, name, name); - peer->addr.sin_port = htons(IAX_DEFAULT_PORTNO); - peer->expiry = min_reg_expire; - } - peer->prefs = prefs; - peer->capability = iax2_capability; - peer->smoothing = 0; - peer->pokefreqok = DEFAULT_FREQ_OK; - peer->pokefreqnotok = DEFAULT_FREQ_NOTOK; - ast_string_field_set(peer,context,""); - ast_string_field_set(peer,peercontext,""); - ast_clear_flag(peer, IAX_HASCALLERID); - ast_string_field_set(peer, cid_name, ""); - ast_string_field_set(peer, cid_num, ""); - } - - if (!v) { - v = alt; - alt = NULL; - } - while(v) { - if (!strcasecmp(v->name, "secret")) { - ast_string_field_set(peer, secret, v->value); - } else if (!strcasecmp(v->name, "mailbox")) { - ast_string_field_set(peer, mailbox, v->value); - } else if (!strcasecmp(v->name, "hasvoicemail")) { - if (ast_true(v->value) && ast_strlen_zero(peer->mailbox)) { - ast_string_field_set(peer, mailbox, name); - } - } else if (!strcasecmp(v->name, "mohinterpret")) { - ast_string_field_set(peer, mohinterpret, v->value); - } else if (!strcasecmp(v->name, "mohsuggest")) { - ast_string_field_set(peer, mohsuggest, v->value); - } else if (!strcasecmp(v->name, "dbsecret")) { - ast_string_field_set(peer, dbsecret, v->value); - } else if (!strcasecmp(v->name, "trunk")) { - ast_set2_flag(peer, ast_true(v->value), IAX_TRUNK); - if (ast_test_flag(peer, IAX_TRUNK) && (timingfd < 0)) { - ast_log(LOG_WARNING, "Unable to support trunking on peer '%s' without timing\n", peer->name); - ast_clear_flag(peer, IAX_TRUNK); - } - } else if (!strcasecmp(v->name, "auth")) { - peer->authmethods = get_auth_methods(v->value); - } else if (!strcasecmp(v->name, "encryption")) { - peer->encmethods = get_encrypt_methods(v->value); - } else if (!strcasecmp(v->name, "notransfer")) { - ast_log(LOG_NOTICE, "The option 'notransfer' is deprecated in favor of 'transfer' which has options 'yes', 'no', and 'mediaonly'\n"); - ast_clear_flag(peer, IAX_TRANSFERMEDIA); - ast_set2_flag(peer, ast_true(v->value), IAX_NOTRANSFER); - } else if (!strcasecmp(v->name, "transfer")) { - if (!strcasecmp(v->value, "mediaonly")) { - ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA); - } else if (ast_true(v->value)) { - ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0); - } else - ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER); - } else if (!strcasecmp(v->name, "jitterbuffer")) { - ast_set2_flag(peer, ast_true(v->value), IAX_USEJITTERBUF); - } else if (!strcasecmp(v->name, "forcejitterbuffer")) { - ast_set2_flag(peer, ast_true(v->value), IAX_FORCEJITTERBUF); - } else if (!strcasecmp(v->name, "host")) { - if (!strcasecmp(v->value, "dynamic")) { - /* They'll register with us */ - ast_set_flag(peer, IAX_DYNAMIC); - if (!found) { - /* Initialize stuff iff we're not found, otherwise - we keep going with what we had */ - memset(&peer->addr.sin_addr, 0, 4); - if (peer->addr.sin_port) { - /* If we've already got a port, make it the default rather than absolute */ - peer->defaddr.sin_port = peer->addr.sin_port; - peer->addr.sin_port = 0; - } - } - } else { - /* Non-dynamic. Make sure we become that way if we're not */ - AST_SCHED_DEL(sched, peer->expire); - ast_clear_flag(peer, IAX_DYNAMIC); - if (ast_dnsmgr_lookup(v->value, &peer->addr.sin_addr, &peer->dnsmgr)) - return peer_unref(peer); - if (!peer->addr.sin_port) - peer->addr.sin_port = htons(IAX_DEFAULT_PORTNO); - } - if (!maskfound) - inet_aton("255.255.255.255", &peer->mask); - } else if (!strcasecmp(v->name, "defaultip")) { - if (ast_get_ip(&peer->defaddr, v->value)) - return peer_unref(peer); - } else if (!strcasecmp(v->name, "sourceaddress")) { - peer_set_srcaddr(peer, v->value); - } else if (!strcasecmp(v->name, "permit") || - !strcasecmp(v->name, "deny")) { - peer->ha = ast_append_ha(v->name, v->value, peer->ha); - } else if (!strcasecmp(v->name, "mask")) { - maskfound++; - inet_aton(v->value, &peer->mask); - } else if (!strcasecmp(v->name, "context")) { - ast_string_field_set(peer, context, v->value); - } else if (!strcasecmp(v->name, "regexten")) { - ast_string_field_set(peer, regexten, v->value); - } else if (!strcasecmp(v->name, "peercontext")) { - ast_string_field_set(peer, peercontext, v->value); - } else if (!strcasecmp(v->name, "port")) { - if (ast_test_flag(peer, IAX_DYNAMIC)) - peer->defaddr.sin_port = htons(atoi(v->value)); - else - peer->addr.sin_port = htons(atoi(v->value)); - } else if (!strcasecmp(v->name, "username")) { - ast_string_field_set(peer, username, v->value); - } else if (!strcasecmp(v->name, "allow")) { - ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 1); - } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 0); - } else if (!strcasecmp(v->name, "callerid")) { - if (!ast_strlen_zero(v->value)) { - char name2[80]; - char num2[80]; - ast_callerid_split(v->value, name2, sizeof(name2), num2, sizeof(num2)); - ast_string_field_set(peer, cid_name, name2); - ast_string_field_set(peer, cid_num, num2); - } else { - ast_string_field_set(peer, cid_name, ""); - ast_string_field_set(peer, cid_num, ""); - } - ast_set_flag(peer, IAX_HASCALLERID); - } else if (!strcasecmp(v->name, "fullname")) { - ast_string_field_set(peer, cid_name, S_OR(v->value, "")); - ast_set_flag(peer, IAX_HASCALLERID); - } else if (!strcasecmp(v->name, "cid_number")) { - ast_string_field_set(peer, cid_num, S_OR(v->value, "")); - ast_set_flag(peer, IAX_HASCALLERID); - } else if (!strcasecmp(v->name, "sendani")) { - ast_set2_flag(peer, ast_true(v->value), IAX_SENDANI); - } else if (!strcasecmp(v->name, "inkeys")) { - ast_string_field_set(peer, inkeys, v->value); - } else if (!strcasecmp(v->name, "outkey")) { - ast_string_field_set(peer, outkey, v->value); - } else if (!strcasecmp(v->name, "qualify")) { - if (!strcasecmp(v->value, "no")) { - peer->maxms = 0; - } else if (!strcasecmp(v->value, "yes")) { - peer->maxms = DEFAULT_MAXMS; - } else if (sscanf(v->value, "%d", &peer->maxms) != 1) { - ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno); - peer->maxms = 0; - } - } else if (!strcasecmp(v->name, "qualifysmoothing")) { - peer->smoothing = ast_true(v->value); - } else if (!strcasecmp(v->name, "qualifyfreqok")) { - if (sscanf(v->value, "%d", &peer->pokefreqok) != 1) { - ast_log(LOG_WARNING, "Qualification testing frequency of peer '%s' when OK should a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno); - } - } else if (!strcasecmp(v->name, "qualifyfreqnotok")) { - if (sscanf(v->value, "%d", &peer->pokefreqnotok) != 1) { - ast_log(LOG_WARNING, "Qualification testing frequency of peer '%s' when NOT OK should be a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno); - } else ast_log(LOG_WARNING, "Set peer->pokefreqnotok to %d\n", peer->pokefreqnotok); - } else if (!strcasecmp(v->name, "timezone")) { - ast_string_field_set(peer, zonetag, v->value); - } else if (!strcasecmp(v->name, "adsi")) { - peer->adsi = ast_true(v->value); - }/* else if (strcasecmp(v->name,"type")) */ - /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ - v = v->next; - if (!v) { - v = alt; - alt = NULL; - } - } - if (!peer->authmethods) - peer->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT; - ast_clear_flag(peer, IAX_DELME); - /* Make sure these are IPv4 addresses */ - peer->addr.sin_family = AF_INET; - } - if (oldha) - ast_free_ha(oldha); - return peer; -} - -static void user_destructor(void *obj) -{ - struct iax2_user *user = obj; - - ast_free_ha(user->ha); - free_context(user->contexts); - if(user->vars) { - ast_variables_destroy(user->vars); - user->vars = NULL; - } - ast_string_field_free_memory(user); -} - -/*! \brief Create in-memory user structure from configuration */ -static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly) -{ - struct iax2_user *user = NULL; - struct iax2_context *con, *conl = NULL; - struct ast_ha *oldha = NULL; - struct iax2_context *oldcon = NULL; - int format; - int firstpass=1; - int oldcurauthreq = 0; - char *varname = NULL, *varval = NULL; - struct ast_variable *tmpvar = NULL; - struct iax2_user tmp_user = { - .name = name, - }; - - if (!temponly) { - user = ao2_find(users, &tmp_user, OBJ_POINTER); - if (user && !ast_test_flag(user, IAX_DELME)) - firstpass = 0; - } - - if (user) { - if (firstpass) { - oldcurauthreq = user->curauthreq; - oldha = user->ha; - oldcon = user->contexts; - user->ha = NULL; - user->contexts = NULL; - } - /* Already in the list, remove it and it will be added back (or FREE'd) */ - ao2_unlink(users, user); - } else { - user = ao2_alloc(sizeof(*user), user_destructor); - } - - if (user) { - if (firstpass) { - ast_string_field_free_memory(user); - memset(user, 0, sizeof(struct iax2_user)); - if (ast_string_field_init(user, 32)) { - user = user_unref(user); - goto cleanup; - } - user->maxauthreq = maxauthreq; - user->curauthreq = oldcurauthreq; - user->prefs = prefs; - user->capability = iax2_capability; - user->encmethods = iax2_encryption; - user->adsi = adsi; - ast_string_field_set(user, name, name); - ast_string_field_set(user, language, language); - ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP); - ast_clear_flag(user, IAX_HASCALLERID); - ast_string_field_set(user, cid_name, ""); - ast_string_field_set(user, cid_num, ""); - } - if (!v) { - v = alt; - alt = NULL; - } - while(v) { - if (!strcasecmp(v->name, "context")) { - con = build_context(v->value); - if (con) { - if (conl) - conl->next = con; - else - user->contexts = con; - conl = con; - } - } else if (!strcasecmp(v->name, "permit") || - !strcasecmp(v->name, "deny")) { - user->ha = ast_append_ha(v->name, v->value, user->ha); - } else if (!strcasecmp(v->name, "setvar")) { - varname = ast_strdupa(v->value); - if (varname && (varval = strchr(varname,'='))) { - *varval = '\0'; - varval++; - if((tmpvar = ast_variable_new(varname, varval))) { - tmpvar->next = user->vars; - user->vars = tmpvar; - } - } - } else if (!strcasecmp(v->name, "allow")) { - ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 1); - } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&user->prefs, &user->capability,v->value, 0); - } else if (!strcasecmp(v->name, "trunk")) { - ast_set2_flag(user, ast_true(v->value), IAX_TRUNK); - if (ast_test_flag(user, IAX_TRUNK) && (timingfd < 0)) { - ast_log(LOG_WARNING, "Unable to support trunking on user '%s' without timing\n", user->name); - ast_clear_flag(user, IAX_TRUNK); - } - } else if (!strcasecmp(v->name, "auth")) { - user->authmethods = get_auth_methods(v->value); - } else if (!strcasecmp(v->name, "encryption")) { - user->encmethods = get_encrypt_methods(v->value); - } else if (!strcasecmp(v->name, "notransfer")) { - ast_log(LOG_NOTICE, "The option 'notransfer' is deprecated in favor of 'transfer' which has options 'yes', 'no', and 'mediaonly'\n"); - ast_clear_flag(user, IAX_TRANSFERMEDIA); - ast_set2_flag(user, ast_true(v->value), IAX_NOTRANSFER); - } else if (!strcasecmp(v->name, "transfer")) { - if (!strcasecmp(v->value, "mediaonly")) { - ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA); - } else if (ast_true(v->value)) { - ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0); - } else - ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER); - } else if (!strcasecmp(v->name, "codecpriority")) { - if(!strcasecmp(v->value, "caller")) - ast_set_flag(user, IAX_CODEC_USER_FIRST); - else if(!strcasecmp(v->value, "disabled")) - ast_set_flag(user, IAX_CODEC_NOPREFS); - else if(!strcasecmp(v->value, "reqonly")) { - ast_set_flag(user, IAX_CODEC_NOCAP); - ast_set_flag(user, IAX_CODEC_NOPREFS); - } - } else if (!strcasecmp(v->name, "jitterbuffer")) { - ast_set2_flag(user, ast_true(v->value), IAX_USEJITTERBUF); - } else if (!strcasecmp(v->name, "forcejitterbuffer")) { - ast_set2_flag(user, ast_true(v->value), IAX_FORCEJITTERBUF); - } else if (!strcasecmp(v->name, "dbsecret")) { - ast_string_field_set(user, dbsecret, v->value); - } else if (!strcasecmp(v->name, "secret")) { - if (!ast_strlen_zero(user->secret)) { - char *old = ast_strdupa(user->secret); - - ast_string_field_build(user, secret, "%s;%s", old, v->value); - } else - ast_string_field_set(user, secret, v->value); - } else if (!strcasecmp(v->name, "callerid")) { - if (!ast_strlen_zero(v->value) && strcasecmp(v->value, "asreceived")) { - char name2[80]; - char num2[80]; - ast_callerid_split(v->value, name2, sizeof(name2), num2, sizeof(num2)); - ast_string_field_set(user, cid_name, name2); - ast_string_field_set(user, cid_num, num2); - ast_set_flag(user, IAX_HASCALLERID); - } else { - ast_clear_flag(user, IAX_HASCALLERID); - ast_string_field_set(user, cid_name, ""); - ast_string_field_set(user, cid_num, ""); - } - } else if (!strcasecmp(v->name, "fullname")) { - if (!ast_strlen_zero(v->value)) { - ast_string_field_set(user, cid_name, v->value); - ast_set_flag(user, IAX_HASCALLERID); - } else { - ast_string_field_set(user, cid_name, ""); - if (ast_strlen_zero(user->cid_num)) - ast_clear_flag(user, IAX_HASCALLERID); - } - } else if (!strcasecmp(v->name, "cid_number")) { - if (!ast_strlen_zero(v->value)) { - ast_string_field_set(user, cid_num, v->value); - ast_set_flag(user, IAX_HASCALLERID); - } else { - ast_string_field_set(user, cid_num, ""); - if (ast_strlen_zero(user->cid_name)) - ast_clear_flag(user, IAX_HASCALLERID); - } - } else if (!strcasecmp(v->name, "accountcode")) { - ast_string_field_set(user, accountcode, v->value); - } else if (!strcasecmp(v->name, "mohinterpret")) { - ast_string_field_set(user, mohinterpret, v->value); - } else if (!strcasecmp(v->name, "mohsuggest")) { - ast_string_field_set(user, mohsuggest, v->value); - } else if (!strcasecmp(v->name, "language")) { - ast_string_field_set(user, language, v->value); - } else if (!strcasecmp(v->name, "amaflags")) { - format = ast_cdr_amaflags2int(v->value); - if (format < 0) { - ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno); - } else { - user->amaflags = format; - } - } else if (!strcasecmp(v->name, "inkeys")) { - ast_string_field_set(user, inkeys, v->value); - } else if (!strcasecmp(v->name, "maxauthreq")) { - user->maxauthreq = atoi(v->value); - if (user->maxauthreq < 0) - user->maxauthreq = 0; - } else if (!strcasecmp(v->name, "adsi")) { - user->adsi = ast_true(v->value); - }/* else if (strcasecmp(v->name,"type")) */ - /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ - v = v->next; - if (!v) { - v = alt; - alt = NULL; - } - } - if (!user->authmethods) { - if (!ast_strlen_zero(user->secret)) { - user->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT; - if (!ast_strlen_zero(user->inkeys)) - user->authmethods |= IAX_AUTH_RSA; - } else if (!ast_strlen_zero(user->inkeys)) { - user->authmethods = IAX_AUTH_RSA; - } else { - user->authmethods = IAX_AUTH_MD5 | IAX_AUTH_PLAINTEXT; - } - } - ast_clear_flag(user, IAX_DELME); - } -cleanup: - if (oldha) - ast_free_ha(oldha); - if (oldcon) - free_context(oldcon); - return user; -} - -static int peer_delme_cb(void *obj, void *arg, int flags) -{ - struct iax2_peer *peer = obj; - - ast_set_flag(peer, IAX_DELME); - - return 0; -} - -static int user_delme_cb(void *obj, void *arg, int flags) -{ - struct iax2_user *user = obj; - - ast_set_flag(user, IAX_DELME); - - return 0; -} - -static void delete_users(void) -{ - struct iax2_registry *reg; - - ao2_callback(users, 0, user_delme_cb, NULL); - - AST_LIST_LOCK(®istrations); - while ((reg = AST_LIST_REMOVE_HEAD(®istrations, entry))) { - ast_sched_del(sched, reg->expire); - if (reg->callno) { - int callno = reg->callno; - ast_mutex_lock(&iaxsl[callno]); - if (iaxs[callno]) { - iaxs[callno]->reg = NULL; - iax2_destroy(callno); - } - ast_mutex_unlock(&iaxsl[callno]); - } - if (reg->dnsmgr) - ast_dnsmgr_release(reg->dnsmgr); - free(reg); - } - AST_LIST_UNLOCK(®istrations); - - ao2_callback(peers, 0, peer_delme_cb, NULL); -} - -static void prune_users(void) -{ - struct iax2_user *user; - struct ao2_iterator i; - - i = ao2_iterator_init(users, 0); - while ((user = ao2_iterator_next(&i))) { - if (ast_test_flag(user, IAX_DELME)) - ao2_unlink(users, user); - user_unref(user); - } -} - -/* Prune peers who still are supposed to be deleted */ -static void prune_peers(void) -{ - struct iax2_peer *peer; - struct ao2_iterator i; - - i = ao2_iterator_init(peers, 0); - while ((peer = ao2_iterator_next(&i))) { - if (ast_test_flag(peer, IAX_DELME)) - unlink_peer(peer); - peer_unref(peer); - } -} - -static void set_timing(void) -{ -#ifdef HAVE_DAHDI - int bs = trunkfreq * 8; - if (timingfd > -1) { - if ( -#ifdef DAHDI_TIMERACK - ioctl(timingfd, DAHDI_TIMERCONFIG, &bs) && -#endif - ioctl(timingfd, DAHDI_SET_BLOCKSIZE, &bs)) - ast_log(LOG_WARNING, "Unable to set blocksize on timing source\n"); - } -#endif -} - -static void set_config_destroy(void) -{ - strcpy(accountcode, ""); - strcpy(language, ""); - strcpy(mohinterpret, "default"); - strcpy(mohsuggest, ""); - amaflags = 0; - delayreject = 0; - ast_clear_flag((&globalflags), IAX_NOTRANSFER); - ast_clear_flag((&globalflags), IAX_TRANSFERMEDIA); - ast_clear_flag((&globalflags), IAX_USEJITTERBUF); - ast_clear_flag((&globalflags), IAX_FORCEJITTERBUF); - delete_users(); -} - -/*! \brief Load configuration */ -static int set_config(char *config_file, int reload) -{ - struct ast_config *cfg, *ucfg; - int capability=iax2_capability; - struct ast_variable *v; - char *cat; - const char *utype; - const char *tosval; - int format; - int portno = IAX_DEFAULT_PORTNO; - int x; - struct iax2_user *user; - struct iax2_peer *peer; - struct ast_netsock *ns; -#if 0 - static unsigned short int last_port=0; -#endif - - cfg = ast_config_load(config_file); - - if (!cfg) { - ast_log(LOG_ERROR, "Unable to load config %s\n", config_file); - return -1; - } - - if (reload) { - set_config_destroy(); - } - - /* Reset global codec prefs */ - memset(&prefs, 0 , sizeof(struct ast_codec_pref)); - - /* Reset Global Flags */ - memset(&globalflags, 0, sizeof(globalflags)); - ast_set_flag(&globalflags, IAX_RTUPDATE); - -#ifdef SO_NO_CHECK - nochecksums = 0; -#endif - - min_reg_expire = IAX_DEFAULT_REG_EXPIRE; - max_reg_expire = IAX_DEFAULT_REG_EXPIRE; - - maxauthreq = 3; - - v = ast_variable_browse(cfg, "general"); - - /* Seed initial tos value */ - tosval = ast_variable_retrieve(cfg, "general", "tos"); - if (tosval) { - if (ast_str2tos(tosval, &tos)) - ast_log(LOG_WARNING, "Invalid tos value, see doc/ip-tos.txt for more information.\n"); - } - while(v) { - if (!strcasecmp(v->name, "bindport")){ - if (reload) - ast_log(LOG_NOTICE, "Ignoring bindport on reload\n"); - else - portno = atoi(v->value); - } else if (!strcasecmp(v->name, "pingtime")) - ping_time = atoi(v->value); - else if (!strcasecmp(v->name, "iaxthreadcount")) { - if (reload) { - if (atoi(v->value) != iaxthreadcount) - ast_log(LOG_NOTICE, "Ignoring any changes to iaxthreadcount during reload\n"); - } else { - iaxthreadcount = atoi(v->value); - if (iaxthreadcount < 1) { - ast_log(LOG_NOTICE, "iaxthreadcount must be at least 1.\n"); - iaxthreadcount = 1; - } else if (iaxthreadcount > 256) { - ast_log(LOG_NOTICE, "limiting iaxthreadcount to 256\n"); - iaxthreadcount = 256; - } - } - } else if (!strcasecmp(v->name, "iaxmaxthreadcount")) { - if (reload) { - AST_LIST_LOCK(&dynamic_list); - iaxmaxthreadcount = atoi(v->value); - AST_LIST_UNLOCK(&dynamic_list); - } else { - iaxmaxthreadcount = atoi(v->value); - if (iaxmaxthreadcount < 0) { - ast_log(LOG_NOTICE, "iaxmaxthreadcount must be at least 0.\n"); - iaxmaxthreadcount = 0; - } else if (iaxmaxthreadcount > 256) { - ast_log(LOG_NOTICE, "Limiting iaxmaxthreadcount to 256\n"); - iaxmaxthreadcount = 256; - } - } - } else if (!strcasecmp(v->name, "nochecksums")) { -#ifdef SO_NO_CHECK - if (ast_true(v->value)) - nochecksums = 1; - else - nochecksums = 0; -#else - if (ast_true(v->value)) - ast_log(LOG_WARNING, "Disabling RTP checksums is not supported on this operating system!\n"); -#endif - } - else if (!strcasecmp(v->name, "maxjitterbuffer")) - maxjitterbuffer = atoi(v->value); - else if (!strcasecmp(v->name, "resyncthreshold")) - resyncthreshold = atoi(v->value); - else if (!strcasecmp(v->name, "maxjitterinterps")) - maxjitterinterps = atoi(v->value); - else if (!strcasecmp(v->name, "lagrqtime")) - lagrq_time = atoi(v->value); - else if (!strcasecmp(v->name, "maxregexpire")) - max_reg_expire = atoi(v->value); - else if (!strcasecmp(v->name, "minregexpire")) - min_reg_expire = atoi(v->value); - else if (!strcasecmp(v->name, "bindaddr")) { - if (reload) { - ast_log(LOG_NOTICE, "Ignoring bindaddr on reload\n"); - } else { - if (!(ns = ast_netsock_bind(netsock, io, v->value, portno, tos, socket_read, NULL))) { - ast_log(LOG_WARNING, "Unable apply binding to '%s' at line %d\n", v->value, v->lineno); - } else { - if (option_verbose > 1) { - if (strchr(v->value, ':')) - ast_verbose(VERBOSE_PREFIX_2 "Binding IAX2 to '%s'\n", v->value); - else - ast_verbose(VERBOSE_PREFIX_2 "Binding IAX2 to '%s:%d'\n", v->value, portno); - } - if (defaultsockfd < 0) - defaultsockfd = ast_netsock_sockfd(ns); - ast_netsock_unref(ns); - } - } - } else if (!strcasecmp(v->name, "authdebug")) - authdebug = ast_true(v->value); - else if (!strcasecmp(v->name, "encryption")) - iax2_encryption = get_encrypt_methods(v->value); - else if (!strcasecmp(v->name, "notransfer")) { - ast_log(LOG_NOTICE, "The option 'notransfer' is deprecated in favor of 'transfer' which has options 'yes', 'no', and 'mediaonly'\n"); - ast_clear_flag((&globalflags), IAX_TRANSFERMEDIA); - ast_set2_flag((&globalflags), ast_true(v->value), IAX_NOTRANSFER); - } else if (!strcasecmp(v->name, "transfer")) { - if (!strcasecmp(v->value, "mediaonly")) { - ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA); - } else if (ast_true(v->value)) { - ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, 0); - } else - ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_NOTRANSFER); - } else if (!strcasecmp(v->name, "codecpriority")) { - if(!strcasecmp(v->value, "caller")) - ast_set_flag((&globalflags), IAX_CODEC_USER_FIRST); - else if(!strcasecmp(v->value, "disabled")) - ast_set_flag((&globalflags), IAX_CODEC_NOPREFS); - else if(!strcasecmp(v->value, "reqonly")) { - ast_set_flag((&globalflags), IAX_CODEC_NOCAP); - ast_set_flag((&globalflags), IAX_CODEC_NOPREFS); - } - } else if (!strcasecmp(v->name, "jitterbuffer")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_USEJITTERBUF); - else if (!strcasecmp(v->name, "forcejitterbuffer")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_FORCEJITTERBUF); - else if (!strcasecmp(v->name, "delayreject")) - delayreject = ast_true(v->value); - else if (!strcasecmp(v->name, "allowfwdownload")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_ALLOWFWDOWNLOAD); - else if (!strcasecmp(v->name, "rtcachefriends")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTCACHEFRIENDS); - else if (!strcasecmp(v->name, "rtignoreregexpire")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTIGNOREREGEXPIRE); - else if (!strcasecmp(v->name, "rtupdate")) - ast_set2_flag((&globalflags), ast_true(v->value), IAX_RTUPDATE); - else if (!strcasecmp(v->name, "trunktimestamps")) - ast_set2_flag(&globalflags, ast_true(v->value), IAX_TRUNKTIMESTAMPS); - else if (!strcasecmp(v->name, "rtautoclear")) { - int i = atoi(v->value); - if(i > 0) - global_rtautoclear = i; - else - i = 0; - ast_set2_flag((&globalflags), i || ast_true(v->value), IAX_RTAUTOCLEAR); - } else if (!strcasecmp(v->name, "trunkfreq")) { - trunkfreq = atoi(v->value); - if (trunkfreq < 10) - trunkfreq = 10; - } else if (!strcasecmp(v->name, "autokill")) { - if (sscanf(v->value, "%d", &x) == 1) { - if (x >= 0) - autokill = x; - else - ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno); - } else if (ast_true(v->value)) { - autokill = DEFAULT_MAXMS; - } else { - autokill = 0; - } - } else if (!strcasecmp(v->name, "bandwidth")) { - if (!strcasecmp(v->value, "low")) { - capability = IAX_CAPABILITY_LOWBANDWIDTH; - } else if (!strcasecmp(v->value, "medium")) { - capability = IAX_CAPABILITY_MEDBANDWIDTH; - } else if (!strcasecmp(v->value, "high")) { - capability = IAX_CAPABILITY_FULLBANDWIDTH; - } else - ast_log(LOG_WARNING, "bandwidth must be either low, medium, or high\n"); - } else if (!strcasecmp(v->name, "allow")) { - ast_parse_allow_disallow(&prefs, &capability, v->value, 1); - } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&prefs, &capability, v->value, 0); - } else if (!strcasecmp(v->name, "register")) { - iax2_register(v->value, v->lineno); - } else if (!strcasecmp(v->name, "iaxcompat")) { - iaxcompat = ast_true(v->value); - } else if (!strcasecmp(v->name, "regcontext")) { - ast_copy_string(regcontext, v->value, sizeof(regcontext)); - /* Create context if it doesn't exist already */ - if (!ast_context_find(regcontext)) - ast_context_create(NULL, regcontext, "IAX2"); - } else if (!strcasecmp(v->name, "tos")) { - if (ast_str2tos(v->value, &tos)) - ast_log(LOG_WARNING, "Invalid tos value at line %d, see doc/ip-tos.txt for more information.'\n", v->lineno); - } else if (!strcasecmp(v->name, "accountcode")) { - ast_copy_string(accountcode, v->value, sizeof(accountcode)); - } else if (!strcasecmp(v->name, "mohinterpret")) { - ast_copy_string(mohinterpret, v->value, sizeof(mohinterpret)); - } else if (!strcasecmp(v->name, "mohsuggest")) { - ast_copy_string(mohsuggest, v->value, sizeof(mohsuggest)); - } else if (!strcasecmp(v->name, "amaflags")) { - format = ast_cdr_amaflags2int(v->value); - if (format < 0) { - ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno); - } else { - amaflags = format; - } - } else if (!strcasecmp(v->name, "language")) { - ast_copy_string(language, v->value, sizeof(language)); - } else if (!strcasecmp(v->name, "maxauthreq")) { - maxauthreq = atoi(v->value); - if (maxauthreq < 0) - maxauthreq = 0; - } else if (!strcasecmp(v->name, "adsi")) { - adsi = ast_true(v->value); - } /*else if (strcasecmp(v->name,"type")) */ - /* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */ - v = v->next; - } - - if (defaultsockfd < 0) { - if (!(ns = ast_netsock_bind(netsock, io, "0.0.0.0", portno, tos, socket_read, NULL))) { - ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno)); - } else { - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Binding IAX2 to default address 0.0.0.0:%d\n", portno); - defaultsockfd = ast_netsock_sockfd(ns); - ast_netsock_unref(ns); - } - } - if (reload) { - ast_netsock_release(outsock); - outsock = ast_netsock_list_alloc(); - if (!outsock) { - ast_log(LOG_ERROR, "Could not allocate outsock list.\n"); - return -1; - } - ast_netsock_init(outsock); - } - - if (min_reg_expire > max_reg_expire) { - ast_log(LOG_WARNING, "Minimum registration interval of %d is more than maximum of %d, resetting minimum to %d\n", - min_reg_expire, max_reg_expire, max_reg_expire); - min_reg_expire = max_reg_expire; - } - iax2_capability = capability; - - ucfg = ast_config_load("users.conf"); - if (ucfg) { - struct ast_variable *gen; - int genhasiax; - int genregisteriax; - const char *hasiax, *registeriax; - - genhasiax = ast_true(ast_variable_retrieve(ucfg, "general", "hasiax")); - genregisteriax = ast_true(ast_variable_retrieve(ucfg, "general", "registeriax")); - gen = ast_variable_browse(ucfg, "general"); - cat = ast_category_browse(ucfg, NULL); - while (cat) { - if (strcasecmp(cat, "general")) { - hasiax = ast_variable_retrieve(ucfg, cat, "hasiax"); - registeriax = ast_variable_retrieve(ucfg, cat, "registeriax"); - if (ast_true(hasiax) || (!hasiax && genhasiax)) { - /* Start with general parameters, then specific parameters, user and peer */ - user = build_user(cat, gen, ast_variable_browse(ucfg, cat), 0); - if (user) { - __ao2_link(users, user, (MAX_PEER_BUCKETS == 1) ? 1 : 0); - user = user_unref(user); - } - peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0); - if (peer) { - if (ast_test_flag(peer, IAX_DYNAMIC)) - reg_source_db(peer); - __ao2_link(peers, peer, (MAX_PEER_BUCKETS == 1) ? 1 : 0); - peer = peer_unref(peer); - } - } - if (ast_true(registeriax) || (!registeriax && genregisteriax)) { - char tmp[256]; - const char *host = ast_variable_retrieve(ucfg, cat, "host"); - const char *username = ast_variable_retrieve(ucfg, cat, "username"); - const char *secret = ast_variable_retrieve(ucfg, cat, "secret"); - if (!host) - host = ast_variable_retrieve(ucfg, "general", "host"); - if (!username) - username = ast_variable_retrieve(ucfg, "general", "username"); - if (!secret) - secret = ast_variable_retrieve(ucfg, "general", "secret"); - if (!ast_strlen_zero(username) && !ast_strlen_zero(host)) { - if (!ast_strlen_zero(secret)) - snprintf(tmp, sizeof(tmp), "%s:%s@%s", username, secret, host); - else - snprintf(tmp, sizeof(tmp), "%s@%s", username, host); - iax2_register(tmp, 0); - } - } - } - cat = ast_category_browse(ucfg, cat); - } - ast_config_destroy(ucfg); - } - - cat = ast_category_browse(cfg, NULL); - while(cat) { - if (strcasecmp(cat, "general")) { - utype = ast_variable_retrieve(cfg, cat, "type"); - if (utype) { - if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) { - user = build_user(cat, ast_variable_browse(cfg, cat), NULL, 0); - if (user) { - __ao2_link(users, user, (MAX_PEER_BUCKETS == 1) ? 1 : 0); - user = user_unref(user); - } - } - if (!strcasecmp(utype, "peer") || !strcasecmp(utype, "friend")) { - peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0); - if (peer) { - if (ast_test_flag(peer, IAX_DYNAMIC)) - reg_source_db(peer); - __ao2_link(peers, peer, (MAX_PEER_BUCKETS == 1) ? 1 : 0); - peer = peer_unref(peer); - } - } else if (strcasecmp(utype, "user")) { - ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config_file); - } - } else - ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat); - } - cat = ast_category_browse(cfg, cat); - } - ast_config_destroy(cfg); - set_timing(); - return 1; -} - -static void poke_all_peers(void) -{ - struct ao2_iterator i; - struct iax2_peer *peer; - - i = ao2_iterator_init(peers, 0); - while ((peer = ao2_iterator_next(&i))) { - iax2_poke_peer(peer, 0); - peer_unref(peer); - } -} -static int reload_config(void) -{ - char *config = "iax.conf"; - struct iax2_registry *reg; - - if (set_config(config, 1) > 0) { - prune_peers(); - prune_users(); - AST_LIST_LOCK(®istrations); - AST_LIST_TRAVERSE(®istrations, reg, entry) - iax2_do_register(reg); - AST_LIST_UNLOCK(®istrations); - /* Qualify hosts, too */ - poke_all_peers(); - } - reload_firmware(0); - iax_provision_reload(); - - return 0; -} - -static int iax2_reload(int fd, int argc, char *argv[]) -{ - return reload_config(); -} - -static int reload(void) -{ - return reload_config(); -} - -static int cache_get_callno_locked(const char *data) -{ - struct sockaddr_in sin; - int x; - int callno; - struct iax_ie_data ied; - struct create_addr_info cai; - struct parsed_dial_string pds; - char *tmpstr; - - for (x = 0; x < ARRAY_LEN(iaxs); x++) { - /* Look for an *exact match* call. Once a call is negotiated, it can only - look up entries for a single context */ - if (!ast_mutex_trylock(&iaxsl[x])) { - if (iaxs[x] && !strcasecmp(data, iaxs[x]->dproot)) - return x; - ast_mutex_unlock(&iaxsl[x]); - } - } - - /* No match found, we need to create a new one */ - - memset(&cai, 0, sizeof(cai)); - memset(&ied, 0, sizeof(ied)); - memset(&pds, 0, sizeof(pds)); - - tmpstr = ast_strdupa(data); - parse_dial_string(tmpstr, &pds); - - if (ast_strlen_zero(pds.peer)) { - ast_log(LOG_WARNING, "No peer provided in the IAX2 dial string '%s'\n", data); - return -1; - } - - /* Populate our address from the given */ - if (create_addr(pds.peer, NULL, &sin, &cai)) - return -1; - - if (option_debug) - ast_log(LOG_DEBUG, "peer: %s, username: %s, password: %s, context: %s\n", - pds.peer, pds.username, pds.password, pds.context); - - callno = find_callno_locked(0, 0, &sin, NEW_FORCE, cai.sockfd, 0); - if (callno < 1) { - ast_log(LOG_WARNING, "Unable to create call\n"); - return -1; - } - - ast_string_field_set(iaxs[callno], dproot, data); - iaxs[callno]->capability = IAX_CAPABILITY_FULLBANDWIDTH; - - iax_ie_append_short(&ied, IAX_IE_VERSION, IAX_PROTO_VERSION); - iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, "TBD"); - /* the string format is slightly different from a standard dial string, - because the context appears in the 'exten' position - */ - if (pds.exten) - iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, pds.exten); - if (pds.username) - iax_ie_append_str(&ied, IAX_IE_USERNAME, pds.username); - iax_ie_append_int(&ied, IAX_IE_FORMAT, IAX_CAPABILITY_FULLBANDWIDTH); - iax_ie_append_int(&ied, IAX_IE_CAPABILITY, IAX_CAPABILITY_FULLBANDWIDTH); - /* Keep password handy */ - if (pds.password) - ast_string_field_set(iaxs[callno], secret, pds.password); - if (pds.key) - ast_string_field_set(iaxs[callno], outkey, pds.key); - /* Start the call going */ - send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1); - - return callno; -} - -static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *data, const char *context, const char *exten, int priority) -{ - struct iax2_dpcache *dp, *prev = NULL, *next; - struct timeval tv; - int x; - int com[2]; - int timeout; - int old=0; - int outfd; - int abort; - int callno; - struct ast_channel *c; - struct ast_frame *f; - gettimeofday(&tv, NULL); - dp = dpcache; - while(dp) { - next = dp->next; - /* Expire old caches */ - if (ast_tvcmp(tv, dp->expiry) > 0) { - /* It's expired, let it disappear */ - if (prev) - prev->next = dp->next; - else - dpcache = dp->next; - if (!dp->peer && !(dp->flags & CACHE_FLAG_PENDING) && !dp->callno) { - /* Free memory and go again */ - free(dp); - } else { - ast_log(LOG_WARNING, "DP still has peer field or pending or callno (flags = %d, peer = %p callno = %d)\n", dp->flags, dp->peer, dp->callno); - } - dp = next; - continue; - } - /* We found an entry that matches us! */ - if (!strcmp(dp->peercontext, data) && !strcmp(dp->exten, exten)) - break; - prev = dp; - dp = next; - } - if (!dp) { - /* No matching entry. Create a new one. */ - /* First, can we make a callno? */ - callno = cache_get_callno_locked(data); - if (callno < 0) { - ast_log(LOG_WARNING, "Unable to generate call for '%s'\n", data); - return NULL; - } - if (!(dp = ast_calloc(1, sizeof(*dp)))) { - ast_mutex_unlock(&iaxsl[callno]); - return NULL; - } - ast_copy_string(dp->peercontext, data, sizeof(dp->peercontext)); - ast_copy_string(dp->exten, exten, sizeof(dp->exten)); - gettimeofday(&dp->expiry, NULL); - dp->orig = dp->expiry; - /* Expires in 30 mins by default */ - dp->expiry.tv_sec += iaxdefaultdpcache; - dp->next = dpcache; - dp->flags = CACHE_FLAG_PENDING; - for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++) - dp->waiters[x] = -1; - dpcache = dp; - dp->peer = iaxs[callno]->dpentries; - iaxs[callno]->dpentries = dp; - /* Send the request if we're already up */ - if (ast_test_flag(&iaxs[callno]->state, IAX_STATE_STARTED)) - iax2_dprequest(dp, callno); - ast_mutex_unlock(&iaxsl[callno]); - } - /* By here we must have a dp */ - if (dp->flags & CACHE_FLAG_PENDING) { - /* Okay, here it starts to get nasty. We need a pipe now to wait - for a reply to come back so long as it's pending */ - for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++) { - /* Find an empty slot */ - if (dp->waiters[x] < 0) - break; - } - if (x >= sizeof(dp->waiters) / sizeof(dp->waiters[0])) { - ast_log(LOG_WARNING, "No more waiter positions available\n"); - return NULL; - } - if (pipe(com)) { - ast_log(LOG_WARNING, "Unable to create pipe for comm\n"); - return NULL; - } - dp->waiters[x] = com[1]; - /* Okay, now we wait */ - timeout = iaxdefaulttimeout * 1000; - /* Temporarily unlock */ - ast_mutex_unlock(&dpcache_lock); - /* Defer any dtmf */ - if (chan) - old = ast_channel_defer_dtmf(chan); - abort = 0; - while(timeout) { - c = ast_waitfor_nandfds(&chan, chan ? 1 : 0, &com[0], 1, NULL, &outfd, &timeout); - if (outfd > -1) { - break; - } - if (c) { - f = ast_read(c); - if (f) - ast_frfree(f); - else { - /* Got hung up on, abort! */ - break; - abort = 1; - } - } - } - if (!timeout) { - ast_log(LOG_WARNING, "Timeout waiting for %s exten %s\n", data, exten); - } - ast_mutex_lock(&dpcache_lock); - dp->waiters[x] = -1; - close(com[1]); - close(com[0]); - if (abort) { - /* Don't interpret anything, just abort. Not sure what th epoint - of undeferring dtmf on a hung up channel is but hey whatever */ - if (!old && chan) - ast_channel_undefer_dtmf(chan); - return NULL; - } - if (!(dp->flags & CACHE_FLAG_TIMEOUT)) { - /* Now to do non-independent analysis the results of our wait */ - if (dp->flags & CACHE_FLAG_PENDING) { - /* Still pending... It's a timeout. Wake everybody up. Consider it no longer - pending. Don't let it take as long to timeout. */ - dp->flags &= ~CACHE_FLAG_PENDING; - dp->flags |= CACHE_FLAG_TIMEOUT; - /* Expire after only 60 seconds now. This is designed to help reduce backlog in heavily loaded - systems without leaving it unavailable once the server comes back online */ - dp->expiry.tv_sec = dp->orig.tv_sec + 60; - for (x = 0; x < ARRAY_LEN(dp->waiters); x++) { - if (dp->waiters[x] > -1) { - if (write(dp->waiters[x], "asdf", 4) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } - } - } - } - } - /* Our caller will obtain the rest */ - if (!old && chan) - ast_channel_undefer_dtmf(chan); - } - return dp; -} - -/*! \brief Part of the IAX2 switch interface */ -static int iax2_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) -{ - struct iax2_dpcache *dp; - int res = 0; -#if 0 - ast_log(LOG_NOTICE, "iax2_exists: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data); -#endif - if ((priority != 1) && (priority != 2)) - return 0; - ast_mutex_lock(&dpcache_lock); - dp = find_cache(chan, data, context, exten, priority); - if (dp) { - if (dp->flags & CACHE_FLAG_EXISTS) - res= 1; - } - ast_mutex_unlock(&dpcache_lock); - if (!dp) { - ast_log(LOG_WARNING, "Unable to make DP cache\n"); - } - return res; -} - -/*! \brief part of the IAX2 dial plan switch interface */ -static int iax2_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) -{ - int res = 0; - struct iax2_dpcache *dp; -#if 0 - ast_log(LOG_NOTICE, "iax2_canmatch: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data); -#endif - if ((priority != 1) && (priority != 2)) - return 0; - ast_mutex_lock(&dpcache_lock); - dp = find_cache(chan, data, context, exten, priority); - if (dp) { - if (dp->flags & CACHE_FLAG_CANEXIST) - res= 1; - } - ast_mutex_unlock(&dpcache_lock); - if (!dp) { - ast_log(LOG_WARNING, "Unable to make DP cache\n"); - } - return res; -} - -/*! \brief Part of the IAX2 Switch interface */ -static int iax2_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) -{ - int res = 0; - struct iax2_dpcache *dp; -#if 0 - ast_log(LOG_NOTICE, "iax2_matchmore: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data); -#endif - if ((priority != 1) && (priority != 2)) - return 0; - ast_mutex_lock(&dpcache_lock); - dp = find_cache(chan, data, context, exten, priority); - if (dp) { - if (dp->flags & CACHE_FLAG_MATCHMORE) - res= 1; - } - ast_mutex_unlock(&dpcache_lock); - if (!dp) { - ast_log(LOG_WARNING, "Unable to make DP cache\n"); - } - return res; -} - -/*! \brief Execute IAX2 dialplan switch */ -static int iax2_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data) -{ - char odata[256]; - char req[256]; - char *ncontext; - struct iax2_dpcache *dp; - struct ast_app *dial; -#if 0 - ast_log(LOG_NOTICE, "iax2_exec: con: %s, exten: %s, pri: %d, cid: %s, data: %s, newstack: %d\n", context, exten, priority, callerid ? callerid : "<unknown>", data, newstack); -#endif - if (priority == 2) { - /* Indicate status, can be overridden in dialplan */ - const char *dialstatus = pbx_builtin_getvar_helper(chan, "DIALSTATUS"); - if (dialstatus) { - dial = pbx_findapp(dialstatus); - if (dial) - pbx_exec(chan, dial, ""); - } - return -1; - } else if (priority != 1) - return -1; - ast_mutex_lock(&dpcache_lock); - dp = find_cache(chan, data, context, exten, priority); - if (dp) { - if (dp->flags & CACHE_FLAG_EXISTS) { - ast_copy_string(odata, data, sizeof(odata)); - ncontext = strchr(odata, '/'); - if (ncontext) { - *ncontext = '\0'; - ncontext++; - snprintf(req, sizeof(req), "IAX2/%s/%s@%s", odata, exten, ncontext); - } else { - snprintf(req, sizeof(req), "IAX2/%s/%s", odata, exten); - } - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Executing Dial('%s')\n", req); - } else { - ast_mutex_unlock(&dpcache_lock); - ast_log(LOG_WARNING, "Can't execute nonexistent extension '%s[@%s]' in data '%s'\n", exten, context, data); - return -1; - } - } - ast_mutex_unlock(&dpcache_lock); - dial = pbx_findapp("Dial"); - if (dial) { - return pbx_exec(chan, dial, req); - } else { - ast_log(LOG_WARNING, "No dial application registered\n"); - } - return -1; -} - -static int function_iaxpeer(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) -{ - struct iax2_peer *peer; - char *peername, *colname; - - peername = ast_strdupa(data); - - /* if our channel, return the IP address of the endpoint of current channel */ - if (!strcmp(peername,"CURRENTCHANNEL")) { - unsigned short callno; - if (chan->tech != &iax2_tech) - return -1; - callno = PTR_TO_CALLNO(chan->tech_pvt); - ast_copy_string(buf, iaxs[callno]->addr.sin_addr.s_addr ? ast_inet_ntoa(iaxs[callno]->addr.sin_addr) : "", len); - return 0; - } - - if ((colname = strchr(peername, ':'))) /*! \todo : will be removed after the 1.4 relese */ - *colname++ = '\0'; - else if ((colname = strchr(peername, '|'))) - *colname++ = '\0'; - else - colname = "ip"; - - if (!(peer = find_peer(peername, 1))) - return -1; - - if (!strcasecmp(colname, "ip")) { - ast_copy_string(buf, peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "", len); - } else if (!strcasecmp(colname, "status")) { - peer_status(peer, buf, len); - } else if (!strcasecmp(colname, "mailbox")) { - ast_copy_string(buf, peer->mailbox, len); - } else if (!strcasecmp(colname, "context")) { - ast_copy_string(buf, peer->context, len); - } else if (!strcasecmp(colname, "expire")) { - snprintf(buf, len, "%d", peer->expire); - } else if (!strcasecmp(colname, "dynamic")) { - ast_copy_string(buf, (ast_test_flag(peer, IAX_DYNAMIC) ? "yes" : "no"), len); - } else if (!strcasecmp(colname, "callerid_name")) { - ast_copy_string(buf, peer->cid_name, len); - } else if (!strcasecmp(colname, "callerid_num")) { - ast_copy_string(buf, peer->cid_num, len); - } else if (!strcasecmp(colname, "codecs")) { - ast_getformatname_multiple(buf, len -1, peer->capability); - } else if (!strncasecmp(colname, "codec[", 6)) { - char *codecnum, *ptr; - int index = 0, codec = 0; - - codecnum = strchr(colname, '['); - *codecnum = '\0'; - codecnum++; - if ((ptr = strchr(codecnum, ']'))) { - *ptr = '\0'; - } - index = atoi(codecnum); - if((codec = ast_codec_pref_index(&peer->prefs, index))) { - ast_copy_string(buf, ast_getformatname(codec), len); - } else { - buf[0] = '\0'; - } - } else { - buf[0] = '\0'; - } - - peer_unref(peer); - - return 0; -} - -struct ast_custom_function iaxpeer_function = { - .name = "IAXPEER", - .synopsis = "Gets IAX peer information", - .syntax = "IAXPEER(<peername|CURRENTCHANNEL>[|item])", - .read = function_iaxpeer, - .desc = "If peername specified, valid items are:\n" - "- ip (default) The IP address.\n" - "- status The peer's status (if qualify=yes)\n" - "- mailbox The configured mailbox.\n" - "- context The configured context.\n" - "- expire The epoch time of the next expire.\n" - "- dynamic Is it dynamic? (yes/no).\n" - "- callerid_name The configured Caller ID name.\n" - "- callerid_num The configured Caller ID number.\n" - "- codecs The configured codecs.\n" - "- codec[x] Preferred codec index number 'x' (beginning with zero).\n" - "\n" - "If CURRENTCHANNEL specified, returns IP address of current channel\n" - "\n" -}; - - -/*! \brief Part of the device state notification system ---*/ -static int iax2_devicestate(void *data) -{ - struct parsed_dial_string pds; - char *tmp = ast_strdupa(data); - struct iax2_peer *p; - int res = AST_DEVICE_INVALID; - - memset(&pds, 0, sizeof(pds)); - parse_dial_string(tmp, &pds); - - if (ast_strlen_zero(pds.peer)) { - ast_log(LOG_WARNING, "No peer provided in the IAX2 dial string '%s'\n", (char *) data); - return res; - } - - if (option_debug > 2) - ast_log(LOG_DEBUG, "Checking device state for device %s\n", pds.peer); - - /* SLD: FIXME: second call to find_peer during registration */ - if (!(p = find_peer(pds.peer, 1))) - return res; - - res = AST_DEVICE_UNAVAILABLE; - if (option_debug > 2) - ast_log(LOG_DEBUG, "iax2_devicestate: Found peer. What's device state of %s? addr=%d, defaddr=%d maxms=%d, lastms=%d\n", - pds.peer, p->addr.sin_addr.s_addr, p->defaddr.sin_addr.s_addr, p->maxms, p->lastms); - - if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) && - (!p->maxms || ((p->lastms > -1) && (p->historicms <= p->maxms)))) { - /* Peer is registered, or have default IP address - and a valid registration */ - if (p->historicms == 0 || p->historicms <= p->maxms) - /* let the core figure out whether it is in use or not */ - res = AST_DEVICE_UNKNOWN; - } - - peer_unref(p); - - return res; -} - -static struct ast_switch iax2_switch = -{ - name: "IAX2", - description: "IAX Remote Dialplan Switch", - exists: iax2_exists, - canmatch: iax2_canmatch, - exec: iax2_exec, - matchmore: iax2_matchmore, -}; - -static char show_stats_usage[] = -"Usage: iax2 show stats\n" -" Display statistics on IAX channel driver.\n"; - -static char show_cache_usage[] = -"Usage: iax2 show cache\n" -" Display currently cached IAX Dialplan results.\n"; - -static char show_peer_usage[] = -"Usage: iax2 show peer <name>\n" -" Display details on specific IAX peer\n"; - -static char prune_realtime_usage[] = -"Usage: iax2 prune realtime [<peername>|all]\n" -" Prunes object(s) from the cache\n"; - -static char iax2_reload_usage[] = -"Usage: iax2 reload\n" -" Reloads IAX configuration from iax.conf\n"; - -static char show_prov_usage[] = -"Usage: iax2 provision <host> <template> [forced]\n" -" Provisions the given peer or IP address using a template\n" -" matching either 'template' or '*' if the template is not\n" -" found. If 'forced' is specified, even empty provisioning\n" -" fields will be provisioned as empty fields.\n"; - -static char show_users_usage[] = -"Usage: iax2 show users [like <pattern>]\n" -" Lists all known IAX2 users.\n" -" Optional regular expression pattern is used to filter the user list.\n"; - -static char show_channels_usage[] = -"Usage: iax2 show channels\n" -" Lists all currently active IAX channels.\n"; - -static char show_netstats_usage[] = -"Usage: iax2 show netstats\n" -" Lists network status for all currently active IAX channels.\n"; - -static char show_threads_usage[] = -"Usage: iax2 show threads\n" -" Lists status of IAX helper threads\n"; - -static char show_peers_usage[] = -"Usage: iax2 show peers [registered] [like <pattern>]\n" -" Lists all known IAX2 peers.\n" -" Optional 'registered' argument lists only peers with known addresses.\n" -" Optional regular expression pattern is used to filter the peer list.\n"; - -static char show_firmware_usage[] = -"Usage: iax2 show firmware\n" -" Lists all known IAX firmware images.\n"; - -static char show_reg_usage[] = -"Usage: iax2 show registry\n" -" Lists all registration requests and status.\n"; - -static char debug_usage[] = -"Usage: iax2 set debug\n" -" Enables dumping of IAX packets for debugging purposes\n"; - -static char no_debug_usage[] = -"Usage: iax2 set debug off\n" -" Disables dumping of IAX packets for debugging purposes\n"; - -static char debug_trunk_usage[] = -"Usage: iax2 set debug trunk\n" -" Requests current status of IAX trunking\n"; - -static char no_debug_trunk_usage[] = -"Usage: iax2 set debug trunk off\n" -" Requests current status of IAX trunking\n"; - -static char debug_jb_usage[] = -"Usage: iax2 set debug jb\n" -" Enables jitterbuffer debugging information\n"; - -static char no_debug_jb_usage[] = -"Usage: iax2 set debug jb off\n" -" Disables jitterbuffer debugging information\n"; - -static char iax2_test_losspct_usage[] = -"Usage: iax2 test losspct <percentage>\n" -" For testing, throws away <percentage> percent of incoming packets\n"; - -#ifdef IAXTESTS -static char iax2_test_late_usage[] = -"Usage: iax2 test late <ms>\n" -" For testing, count the next frame as <ms> ms late\n"; - -static char iax2_test_resync_usage[] = -"Usage: iax2 test resync <ms>\n" -" For testing, adjust all future frames by <ms> ms\n"; - -static char iax2_test_jitter_usage[] = -"Usage: iax2 test jitter <ms> <pct>\n" -" For testing, simulate maximum jitter of +/- <ms> on <pct> percentage of packets. If <pct> is not specified, adds jitter to all packets.\n"; -#endif /* IAXTESTS */ - -static struct ast_cli_entry cli_iax2_trunk_debug_deprecated = { - { "iax2", "trunk", "debug", NULL }, - iax2_do_trunk_debug, NULL, - NULL }; - -static struct ast_cli_entry cli_iax2_jb_debug_deprecated = { - { "iax2", "jb", "debug", NULL }, - iax2_do_jb_debug, NULL, - NULL }; - -static struct ast_cli_entry cli_iax2_no_debug_deprecated = { - { "iax2", "no", "debug", NULL }, - iax2_no_debug, NULL, - NULL }; - -static struct ast_cli_entry cli_iax2_no_trunk_debug_deprecated = { - { "iax2", "no", "trunk", "debug", NULL }, - iax2_no_trunk_debug, NULL, - NULL }; - -static struct ast_cli_entry cli_iax2_no_jb_debug_deprecated = { - { "iax2", "no", "jb", "debug", NULL }, - iax2_no_jb_debug, NULL, - NULL }; - -static struct ast_cli_entry cli_iax2[] = { - { { "iax2", "show", "cache", NULL }, - iax2_show_cache, "Display IAX cached dialplan", - show_cache_usage, NULL, }, - - { { "iax2", "show", "channels", NULL }, - iax2_show_channels, "List active IAX channels", - show_channels_usage, NULL, }, - - { { "iax2", "show", "firmware", NULL }, - iax2_show_firmware, "List available IAX firmwares", - show_firmware_usage, NULL, }, - - { { "iax2", "show", "netstats", NULL }, - iax2_show_netstats, "List active IAX channel netstats", - show_netstats_usage, NULL, }, - - { { "iax2", "show", "peers", NULL }, - iax2_show_peers, "List defined IAX peers", - show_peers_usage, NULL, }, - - { { "iax2", "show", "registry", NULL }, - iax2_show_registry, "Display IAX registration status", - show_reg_usage, NULL, }, - - { { "iax2", "show", "stats", NULL }, - iax2_show_stats, "Display IAX statistics", - show_stats_usage, NULL, }, - - { { "iax2", "show", "threads", NULL }, - iax2_show_threads, "Display IAX helper thread info", - show_threads_usage, NULL, }, - - { { "iax2", "show", "users", NULL }, - iax2_show_users, "List defined IAX users", - show_users_usage, NULL, }, - - { { "iax2", "prune", "realtime", NULL }, - iax2_prune_realtime, "Prune a cached realtime lookup", - prune_realtime_usage, complete_iax2_show_peer }, - - { { "iax2", "reload", NULL }, - iax2_reload, "Reload IAX configuration", - iax2_reload_usage }, - - { { "iax2", "show", "peer", NULL }, - iax2_show_peer, "Show details on specific IAX peer", - show_peer_usage, complete_iax2_show_peer }, - - { { "iax2", "set", "debug", NULL }, - iax2_do_debug, "Enable IAX debugging", - debug_usage }, - - { { "iax2", "set", "debug", "trunk", NULL }, - iax2_do_trunk_debug, "Enable IAX trunk debugging", - debug_trunk_usage, NULL, &cli_iax2_trunk_debug_deprecated }, - - { { "iax2", "set", "debug", "jb", NULL }, - iax2_do_jb_debug, "Enable IAX jitterbuffer debugging", - debug_jb_usage, NULL, &cli_iax2_jb_debug_deprecated }, - - { { "iax2", "set", "debug", "off", NULL }, - iax2_no_debug, "Disable IAX debugging", - no_debug_usage, NULL, &cli_iax2_no_debug_deprecated }, - - { { "iax2", "set", "debug", "trunk", "off", NULL }, - iax2_no_trunk_debug, "Disable IAX trunk debugging", - no_debug_trunk_usage, NULL, &cli_iax2_no_trunk_debug_deprecated }, - - { { "iax2", "set", "debug", "jb", "off", NULL }, - iax2_no_jb_debug, "Disable IAX jitterbuffer debugging", - no_debug_jb_usage, NULL, &cli_iax2_no_jb_debug_deprecated }, - - { { "iax2", "test", "losspct", NULL }, - iax2_test_losspct, "Set IAX2 incoming frame loss percentage", - iax2_test_losspct_usage }, - - { { "iax2", "provision", NULL }, - iax2_prov_cmd, "Provision an IAX device", - show_prov_usage, iax2_prov_complete_template_3rd }, - -#ifdef IAXTESTS - { { "iax2", "test", "late", NULL }, - iax2_test_late, "Test the receipt of a late frame", - iax2_test_late_usage }, - - { { "iax2", "test", "resync", NULL }, - iax2_test_resync, "Test a resync in received timestamps", - iax2_test_resync_usage }, - - { { "iax2", "test", "jitter", NULL }, - iax2_test_jitter, "Simulates jitter for testing", - iax2_test_jitter_usage }, -#endif /* IAXTESTS */ -}; - -static int __unload_module(void) -{ - struct iax2_thread *thread = NULL; - int x; - - /* Make sure threads do not hold shared resources when they are canceled */ - - /* Grab the sched lock resource to keep it away from threads about to die */ - /* Cancel the network thread, close the net socket */ - if (netthreadid != AST_PTHREADT_NULL) { - AST_LIST_LOCK(&iaxq.queue); - ast_mutex_lock(&sched_lock); - pthread_cancel(netthreadid); - ast_cond_signal(&sched_cond); - ast_mutex_unlock(&sched_lock); /* Release the schedule lock resource */ - AST_LIST_UNLOCK(&iaxq.queue); - pthread_join(netthreadid, NULL); - } - if (schedthreadid != AST_PTHREADT_NULL) { - ast_mutex_lock(&sched_lock); - pthread_cancel(schedthreadid); - ast_cond_signal(&sched_cond); - ast_mutex_unlock(&sched_lock); - pthread_join(schedthreadid, NULL); - } - - /* Call for all threads to halt */ - AST_LIST_LOCK(&idle_list); - AST_LIST_TRAVERSE_SAFE_BEGIN(&idle_list, thread, list) { - AST_LIST_REMOVE_CURRENT(&idle_list, list); - pthread_cancel(thread->threadid); - } - AST_LIST_TRAVERSE_SAFE_END - AST_LIST_UNLOCK(&idle_list); - - AST_LIST_LOCK(&active_list); - AST_LIST_TRAVERSE_SAFE_BEGIN(&active_list, thread, list) { - AST_LIST_REMOVE_CURRENT(&active_list, list); - pthread_cancel(thread->threadid); - } - AST_LIST_TRAVERSE_SAFE_END - AST_LIST_UNLOCK(&active_list); - - AST_LIST_LOCK(&dynamic_list); - AST_LIST_TRAVERSE_SAFE_BEGIN(&dynamic_list, thread, list) { - AST_LIST_REMOVE_CURRENT(&dynamic_list, list); - pthread_cancel(thread->threadid); - } - AST_LIST_TRAVERSE_SAFE_END - AST_LIST_UNLOCK(&dynamic_list); - - AST_LIST_HEAD_DESTROY(&iaxq.queue); - - /* Wait for threads to exit */ - while(0 < iaxactivethreadcount) - usleep(10000); - - ast_netsock_release(netsock); - ast_netsock_release(outsock); - for (x = 0; x < ARRAY_LEN(iaxs); x++) { - if (iaxs[x]) { - iax2_destroy(x); - } - } - ast_manager_unregister( "IAXpeers" ); - ast_manager_unregister( "IAXnetstats" ); - ast_unregister_application(papp); - ast_cli_unregister_multiple(cli_iax2, sizeof(cli_iax2) / sizeof(struct ast_cli_entry)); - ast_unregister_switch(&iax2_switch); - ast_channel_unregister(&iax2_tech); - delete_users(); - iax_provision_unload(); - sched_context_destroy(sched); - reload_firmware(1); - - ast_mutex_destroy(&waresl.lock); - - for (x = 0; x < ARRAY_LEN(iaxsl); x++) { - ast_mutex_destroy(&iaxsl[x]); - } - - ao2_ref(peers, -1); - ao2_ref(users, -1); - ao2_ref(iax_peercallno_pvts, -1); - - return 0; -} - -static int unload_module(void) -{ - ast_custom_function_unregister(&iaxpeer_function); - return __unload_module(); -} - -static int peer_set_sock_cb(void *obj, void *arg, int flags) -{ - struct iax2_peer *peer = obj; - - if (peer->sockfd < 0) - peer->sockfd = defaultsockfd; - - return 0; -} - -static int pvt_hash_cb(const void *obj, const int flags) -{ - const struct chan_iax2_pvt *pvt = obj; - - return pvt->peercallno; -} - -static int pvt_cmp_cb(void *obj, void *arg, int flags) -{ - struct chan_iax2_pvt *pvt = obj, *pvt2 = arg; - - /* The frames_received field is used to hold whether we're matching - * against a full frame or not ... */ - - return match(&pvt2->addr, pvt2->peercallno, pvt2->callno, pvt, - pvt2->frames_received) ? CMP_MATCH | CMP_STOP : 0; -} - -/*! \brief Load IAX2 module, load configuraiton ---*/ -static int load_module(void) -{ - char *config = "iax.conf"; - int res = 0; - int x; - struct iax2_registry *reg = NULL; - - peers = ao2_container_alloc(MAX_PEER_BUCKETS, peer_hash_cb, peer_cmp_cb); - if (!peers) - return AST_MODULE_LOAD_FAILURE; - users = ao2_container_alloc(MAX_USER_BUCKETS, user_hash_cb, user_cmp_cb); - if (!users) { - ao2_ref(peers, -1); - return AST_MODULE_LOAD_FAILURE; - } - iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb); - if (!iax_peercallno_pvts) { - ao2_ref(peers, -1); - ao2_ref(users, -1); - return AST_MODULE_LOAD_FAILURE; - } - - ast_custom_function_register(&iaxpeer_function); - - iax_set_output(iax_debug_output); - iax_set_error(iax_error_output); - jb_setoutput(jb_error_output, jb_warning_output, NULL); - -#ifdef HAVE_DAHDI -#ifdef DAHDI_TIMERACK - timingfd = open(DAHDI_FILE_TIMER, O_RDWR); - if (timingfd < 0) -#endif - timingfd = open(DAHDI_FILE_PSEUDO, O_RDWR); - if (timingfd < 0) - ast_log(LOG_WARNING, "Unable to open IAX timing interface: %s\n", strerror(errno)); -#endif - - memset(iaxs, 0, sizeof(iaxs)); - - for (x = 0; x < ARRAY_LEN(iaxsl); x++) { - ast_mutex_init(&iaxsl[x]); - } - - ast_cond_init(&sched_cond, NULL); - - io = io_context_create(); - sched = sched_context_create(); - - if (!io || !sched) { - ast_log(LOG_ERROR, "Out of memory\n"); - return -1; - } - - netsock = ast_netsock_list_alloc(); - if (!netsock) { - ast_log(LOG_ERROR, "Could not allocate netsock list.\n"); - return -1; - } - ast_netsock_init(netsock); - - outsock = ast_netsock_list_alloc(); - if (!outsock) { - ast_log(LOG_ERROR, "Could not allocate outsock list.\n"); - return -1; - } - ast_netsock_init(outsock); - - ast_mutex_init(&waresl.lock); - - AST_LIST_HEAD_INIT(&iaxq.queue); - - ast_cli_register_multiple(cli_iax2, sizeof(cli_iax2) / sizeof(struct ast_cli_entry)); - - ast_register_application(papp, iax2_prov_app, psyn, pdescrip); - - ast_manager_register( "IAXpeers", 0, manager_iax2_show_peers, "List IAX Peers" ); - ast_manager_register( "IAXnetstats", 0, manager_iax2_show_netstats, "Show IAX Netstats" ); - - if(set_config(config, 0) == -1) - return AST_MODULE_LOAD_DECLINE; - - if (ast_channel_register(&iax2_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class %s\n", "IAX2"); - __unload_module(); - return -1; - } - - if (ast_register_switch(&iax2_switch)) - ast_log(LOG_ERROR, "Unable to register IAX switch\n"); - - res = start_network_thread(); - if (!res) { - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "IAX Ready and Listening\n"); - } else { - ast_log(LOG_ERROR, "Unable to start network thread\n"); - ast_netsock_release(netsock); - ast_netsock_release(outsock); - } - - AST_LIST_LOCK(®istrations); - AST_LIST_TRAVERSE(®istrations, reg, entry) - iax2_do_register(reg); - AST_LIST_UNLOCK(®istrations); - - ao2_callback(peers, 0, peer_set_sock_cb, NULL); - ao2_callback(peers, 0, iax2_poke_peer_cb, NULL); - - reload_firmware(0); - iax_provision_reload(); - return res; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Inter Asterisk eXchange (Ver 2)", - .load = load_module, - .unload = unload_module, - .reload = reload, - ); diff --git a/1.4.23-rc4/channels/chan_local.c b/1.4.23-rc4/channels/chan_local.c deleted file mode 100644 index b2e3c72b8..000000000 --- a/1.4.23-rc4/channels/chan_local.c +++ /dev/null @@ -1,808 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \author Mark Spencer <markster@digium.com> - * - * \brief Local Proxy Channel - * - * \ingroup channel_drivers - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <sys/socket.h> -#include <errno.h> -#include <stdlib.h> -#include <fcntl.h> -#include <netdb.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/signal.h> - -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/lock.h" -#include "asterisk/sched.h" -#include "asterisk/io.h" -#include "asterisk/rtp.h" -#include "asterisk/acl.h" -#include "asterisk/callerid.h" -#include "asterisk/file.h" -#include "asterisk/cli.h" -#include "asterisk/app.h" -#include "asterisk/musiconhold.h" -#include "asterisk/manager.h" -#include "asterisk/stringfields.h" -#include "asterisk/devicestate.h" - -static const char tdesc[] = "Local Proxy Channel Driver"; - -#define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0) - -static struct ast_channel *local_request(const char *type, int format, void *data, int *cause); -static int local_digit_begin(struct ast_channel *ast, char digit); -static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int local_call(struct ast_channel *ast, char *dest, int timeout); -static int local_hangup(struct ast_channel *ast); -static int local_answer(struct ast_channel *ast); -static struct ast_frame *local_read(struct ast_channel *ast); -static int local_write(struct ast_channel *ast, struct ast_frame *f); -static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); -static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen); -static int local_sendtext(struct ast_channel *ast, const char *text); -static int local_devicestate(void *data); - -/* PBX interface structure for channel registration */ -static const struct ast_channel_tech local_tech = { - .type = "Local", - .description = tdesc, - .capabilities = -1, - .requester = local_request, - .send_digit_begin = local_digit_begin, - .send_digit_end = local_digit_end, - .call = local_call, - .hangup = local_hangup, - .answer = local_answer, - .read = local_read, - .write = local_write, - .write_video = local_write, - .exception = local_read, - .indicate = local_indicate, - .fixup = local_fixup, - .send_html = local_sendhtml, - .send_text = local_sendtext, - .devicestate = local_devicestate, -}; - -struct local_pvt { - ast_mutex_t lock; /* Channel private lock */ - unsigned int flags; /* Private flags */ - char context[AST_MAX_CONTEXT]; /* Context to call */ - char exten[AST_MAX_EXTENSION]; /* Extension to call */ - int reqformat; /* Requested format */ - struct ast_channel *owner; /* Master Channel */ - struct ast_channel *chan; /* Outbound channel */ - struct ast_module_user *u_owner; /*! reference to keep the module loaded while in use */ - struct ast_module_user *u_chan; /*! reference to keep the module loaded while in use */ - AST_LIST_ENTRY(local_pvt) list; /* Next entity */ -}; - -#define LOCAL_GLARE_DETECT (1 << 0) /*!< Detect glare on hangup */ -#define LOCAL_CANCEL_QUEUE (1 << 1) /*!< Cancel queue */ -#define LOCAL_ALREADY_MASQED (1 << 2) /*!< Already masqueraded */ -#define LOCAL_LAUNCHED_PBX (1 << 3) /*!< PBX was launched */ -#define LOCAL_NO_OPTIMIZATION (1 << 4) /*!< Do not optimize using masquerading */ - -static AST_LIST_HEAD_STATIC(locals, local_pvt); - -/*! \brief Adds devicestate to local channels */ -static int local_devicestate(void *data) -{ - char *exten = ast_strdupa(data); - char *context = NULL, *opts = NULL; - int res; - - if (!(context = strchr(exten, '@'))) { - ast_log(LOG_WARNING, "Someone used Local/%s somewhere without a @context. This is bad.\n", exten); - return AST_DEVICE_INVALID; - } - - *context++ = '\0'; - - /* Strip options if they exist */ - if ((opts = strchr(context, '/'))) - *opts = '\0'; - - if (option_debug > 2) - ast_log(LOG_DEBUG, "Checking if extension %s@%s exists (devicestate)\n", exten, context); - res = ast_exists_extension(NULL, context, exten, 1, NULL); - if (!res) - return AST_DEVICE_INVALID; - else - return AST_DEVICE_UNKNOWN; -} - -/*! - * \note Assumes the pvt is no longer in the pvts list - */ -static struct local_pvt *local_pvt_destroy(struct local_pvt *pvt) -{ - ast_mutex_destroy(&pvt->lock); - free(pvt); - return NULL; -} - -static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f, - struct ast_channel *us, int us_locked) -{ - struct ast_channel *other = NULL; - - /* Recalculate outbound channel */ - other = isoutbound ? p->owner : p->chan; - - /* do not queue frame if generator is on both local channels */ - if (us && us->generator && other->generator) - return 0; - - /* Set glare detection */ - ast_set_flag(p, LOCAL_GLARE_DETECT); - if (ast_test_flag(p, LOCAL_CANCEL_QUEUE)) { - /* We had a glare on the hangup. Forget all this business, - return and destroy p. */ - ast_mutex_unlock(&p->lock); - p = local_pvt_destroy(p); - return -1; - } - if (!other) { - ast_clear_flag(p, LOCAL_GLARE_DETECT); - return 0; - } - - /* Ensure that we have both channels locked */ - while (other && ast_channel_trylock(other)) { - ast_mutex_unlock(&p->lock); - if (us && us_locked) { - do { - ast_channel_unlock(us); - usleep(1); - ast_channel_lock(us); - } while (ast_mutex_trylock(&p->lock)); - } else { - usleep(1); - ast_mutex_lock(&p->lock); - } - other = isoutbound ? p->owner : p->chan; - } - - if (other) { - ast_queue_frame(other, f); - ast_channel_unlock(other); - } - - ast_clear_flag(p, LOCAL_GLARE_DETECT); - - return 0; -} - -static int local_answer(struct ast_channel *ast) -{ - struct local_pvt *p = ast->tech_pvt; - int isoutbound; - int res = -1; - - if (!p) - return -1; - - ast_mutex_lock(&p->lock); - isoutbound = IS_OUTBOUND(ast, p); - if (isoutbound) { - /* Pass along answer since somebody answered us */ - struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; - res = local_queue_frame(p, isoutbound, &answer, ast, 1); - } else - ast_log(LOG_WARNING, "Huh? Local is being asked to answer?\n"); - if (!res) - ast_mutex_unlock(&p->lock); - return res; -} - -static void check_bridge(struct local_pvt *p, int isoutbound) -{ - struct ast_channel_monitor *tmp; - if (ast_test_flag(p, LOCAL_ALREADY_MASQED) || ast_test_flag(p, LOCAL_NO_OPTIMIZATION) || !p->chan || !p->owner || (p->chan->_bridge != ast_bridged_channel(p->chan))) - return; - - /* only do the masquerade if we are being called on the outbound channel, - if it has been bridged to another channel and if there are no pending - frames on the owner channel (because they would be transferred to the - outbound channel during the masquerade) - */ - if (isoutbound && p->chan->_bridge /* Not ast_bridged_channel! Only go one step! */ && AST_LIST_EMPTY(&p->owner->readq)) { - /* Masquerade bridged channel into owner */ - /* Lock everything we need, one by one, and give up if - we can't get everything. Remember, we'll get another - chance in just a little bit */ - if (!ast_mutex_trylock(&(p->chan->_bridge)->lock)) { - if (!p->chan->_bridge->_softhangup) { - if (!ast_mutex_trylock(&p->owner->lock)) { - if (!p->owner->_softhangup) { - if (p->owner->monitor && !p->chan->_bridge->monitor) { - /* If a local channel is being monitored, we don't want a masquerade - * to cause the monitor to go away. Since the masquerade swaps the monitors, - * pre-swapping the monitors before the masquerade will ensure that the monitor - * ends up where it is expected. - */ - tmp = p->owner->monitor; - p->owner->monitor = p->chan->_bridge->monitor; - p->chan->_bridge->monitor = tmp; - } - if (p->chan->audiohooks) { - struct ast_audiohook_list *audiohooks_swapper; - audiohooks_swapper = p->chan->audiohooks; - p->chan->audiohooks = p->owner->audiohooks; - p->owner->audiohooks = audiohooks_swapper; - } - ast_channel_masquerade(p->owner, p->chan->_bridge); - ast_set_flag(p, LOCAL_ALREADY_MASQED); - } - ast_mutex_unlock(&p->owner->lock); - } - ast_mutex_unlock(&(p->chan->_bridge)->lock); - } - } - /* We only allow masquerading in one 'direction'... it's important to preserve the state - (group variables, etc.) that live on p->chan->_bridge (and were put there by the dialplan) - when the local channels go away. - */ -#if 0 - } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && AST_LIST_EMPTY(&p->chan->readq)) { - /* Masquerade bridged channel into chan */ - if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) { - if (!p->owner->_bridge->_softhangup) { - if (!ast_mutex_trylock(&p->chan->lock)) { - if (!p->chan->_softhangup) { - ast_channel_masquerade(p->chan, p->owner->_bridge); - ast_set_flag(p, LOCAL_ALREADY_MASQED); - } - ast_mutex_unlock(&p->chan->lock); - } - } - ast_mutex_unlock(&(p->owner->_bridge)->lock); - } -#endif - } -} - -static struct ast_frame *local_read(struct ast_channel *ast) -{ - return &ast_null_frame; -} - -static int local_write(struct ast_channel *ast, struct ast_frame *f) -{ - struct local_pvt *p = ast->tech_pvt; - int res = -1; - int isoutbound; - - if (!p) - return -1; - - /* Just queue for delivery to the other side */ - ast_mutex_lock(&p->lock); - isoutbound = IS_OUTBOUND(ast, p); - if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)) - check_bridge(p, isoutbound); - if (!ast_test_flag(p, LOCAL_ALREADY_MASQED)) - res = local_queue_frame(p, isoutbound, f, ast, 1); - else { - if (option_debug) - ast_log(LOG_DEBUG, "Not posting to queue since already masked on '%s'\n", ast->name); - res = 0; - } - if (!res) - ast_mutex_unlock(&p->lock); - return res; -} - -static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct local_pvt *p = newchan->tech_pvt; - - if (!p) - return -1; - - ast_mutex_lock(&p->lock); - - if ((p->owner != oldchan) && (p->chan != oldchan)) { - ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan); - ast_mutex_unlock(&p->lock); - return -1; - } - if (p->owner == oldchan) - p->owner = newchan; - else - p->chan = newchan; - ast_mutex_unlock(&p->lock); - return 0; -} - -static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen) -{ - struct local_pvt *p = ast->tech_pvt; - int res = 0; - struct ast_frame f = { AST_FRAME_CONTROL, }; - int isoutbound; - - if (!p) - return -1; - - /* If this is an MOH hold or unhold, do it on the Local channel versus real channel */ - if (condition == AST_CONTROL_HOLD) { - ast_moh_start(ast, data, NULL); - } else if (condition == AST_CONTROL_UNHOLD) { - ast_moh_stop(ast); - } else { - /* Queue up a frame representing the indication as a control frame */ - ast_mutex_lock(&p->lock); - isoutbound = IS_OUTBOUND(ast, p); - f.subclass = condition; - f.data = (void*)data; - f.datalen = datalen; - if (!(res = local_queue_frame(p, isoutbound, &f, ast, 1))) - ast_mutex_unlock(&p->lock); - } - - return res; -} - -static int local_digit_begin(struct ast_channel *ast, char digit) -{ - struct local_pvt *p = ast->tech_pvt; - int res = -1; - struct ast_frame f = { AST_FRAME_DTMF_BEGIN, }; - int isoutbound; - - if (!p) - return -1; - - ast_mutex_lock(&p->lock); - isoutbound = IS_OUTBOUND(ast, p); - f.subclass = digit; - if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0))) - ast_mutex_unlock(&p->lock); - - return res; -} - -static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ - struct local_pvt *p = ast->tech_pvt; - int res = -1; - struct ast_frame f = { AST_FRAME_DTMF_END, }; - int isoutbound; - - if (!p) - return -1; - - ast_mutex_lock(&p->lock); - isoutbound = IS_OUTBOUND(ast, p); - f.subclass = digit; - f.len = duration; - if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0))) - ast_mutex_unlock(&p->lock); - - return res; -} - -static int local_sendtext(struct ast_channel *ast, const char *text) -{ - struct local_pvt *p = ast->tech_pvt; - int res = -1; - struct ast_frame f = { AST_FRAME_TEXT, }; - int isoutbound; - - if (!p) - return -1; - - ast_mutex_lock(&p->lock); - isoutbound = IS_OUTBOUND(ast, p); - f.data = (char *) text; - f.datalen = strlen(text) + 1; - if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0))) - ast_mutex_unlock(&p->lock); - return res; -} - -static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen) -{ - struct local_pvt *p = ast->tech_pvt; - int res = -1; - struct ast_frame f = { AST_FRAME_HTML, }; - int isoutbound; - - if (!p) - return -1; - - ast_mutex_lock(&p->lock); - isoutbound = IS_OUTBOUND(ast, p); - f.subclass = subclass; - f.data = (char *)data; - f.datalen = datalen; - if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0))) - ast_mutex_unlock(&p->lock); - return res; -} - -/*! \brief Initiate new call, part of PBX interface - * dest is the dial string */ -static int local_call(struct ast_channel *ast, char *dest, int timeout) -{ - struct local_pvt *p = ast->tech_pvt; - int res; - struct ast_var_t *varptr = NULL, *new; - size_t len, namelen; - - if (!p) - return -1; - - ast_mutex_lock(&p->lock); - - /* - * Note that cid_num and cid_name aren't passed in the ast_channel_alloc - * call, so it's done here instead. - */ - p->chan->cid.cid_dnid = ast_strdup(p->owner->cid.cid_dnid); - p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num); - p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name); - p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis); - p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani); - p->chan->cid.cid_pres = p->owner->cid.cid_pres; - p->chan->cid.cid_ani2 = p->owner->cid.cid_ani2; - p->chan->cid.cid_ton = p->owner->cid.cid_ton; - p->chan->cid.cid_tns = p->owner->cid.cid_tns; - ast_string_field_set(p->chan, language, p->owner->language); - ast_string_field_set(p->chan, accountcode, p->owner->accountcode); - ast_string_field_set(p->chan, musicclass, p->owner->musicclass); - ast_cdr_update(p->chan); - p->chan->cdrflags = p->owner->cdrflags; - - if (!ast_exists_extension(NULL, p->chan->context, p->chan->exten, 1, p->owner->cid.cid_num)) { - ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context); - ast_mutex_unlock(&p->lock); - return -1; - } - - /* copy the channel variables from the incoming channel to the outgoing channel */ - /* Note that due to certain assumptions, they MUST be in the same order */ - AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) { - namelen = strlen(varptr->name); - len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2; - if ((new = ast_calloc(1, len))) { - memcpy(new, varptr, len); - new->value = &(new->name[0]) + namelen + 1; - AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries); - } - } - ast_channel_datastore_inherit(p->owner, p->chan); - - /* Start switch on sub channel */ - if (!(res = ast_pbx_start(p->chan))) - ast_set_flag(p, LOCAL_LAUNCHED_PBX); - - ast_mutex_unlock(&p->lock); - return res; -} - -/*! \brief Hangup a call through the local proxy channel */ -static int local_hangup(struct ast_channel *ast) -{ - struct local_pvt *p = ast->tech_pvt; - int isoutbound; - struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP }; - struct ast_channel *ochan = NULL; - int glaredetect = 0, res = 0; - - if (!p) - return -1; - - while (ast_mutex_trylock(&p->lock)) { - ast_channel_unlock(ast); - usleep(1); - ast_channel_lock(ast); - } - - isoutbound = IS_OUTBOUND(ast, p); - if (isoutbound) { - const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS"); - if ((status) && (p->owner)) { - /* Deadlock avoidance */ - while (p->owner && ast_channel_trylock(p->owner)) { - ast_mutex_unlock(&p->lock); - if (ast) { - ast_channel_unlock(ast); - } - usleep(1); - if (ast) { - ast_channel_lock(ast); - } - ast_mutex_lock(&p->lock); - } - if (p->owner) { - pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status); - ast_channel_unlock(p->owner); - } - } - p->chan = NULL; - ast_clear_flag(p, LOCAL_LAUNCHED_PBX); - ast_module_user_remove(p->u_chan); - } else { - p->owner = NULL; - ast_module_user_remove(p->u_owner); - while (p->chan && ast_channel_trylock(p->chan)) { - DEADLOCK_AVOIDANCE(&p->lock); - } - if (p->chan) { - ast_queue_hangup(p->chan); - ast_channel_unlock(p->chan); - } - } - - ast->tech_pvt = NULL; - - if (!p->owner && !p->chan) { - /* Okay, done with the private part now, too. */ - glaredetect = ast_test_flag(p, LOCAL_GLARE_DETECT); - /* If we have a queue holding, don't actually destroy p yet, but - let local_queue do it. */ - if (glaredetect) - ast_set_flag(p, LOCAL_CANCEL_QUEUE); - ast_mutex_unlock(&p->lock); - /* Remove from list */ - AST_LIST_LOCK(&locals); - AST_LIST_REMOVE(&locals, p, list); - AST_LIST_UNLOCK(&locals); - /* Grab / release lock just in case */ - ast_mutex_lock(&p->lock); - ast_mutex_unlock(&p->lock); - /* And destroy */ - if (!glaredetect) { - p = local_pvt_destroy(p); - } - return 0; - } - if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX)) - /* Need to actually hangup since there is no PBX */ - ochan = p->chan; - else - res = local_queue_frame(p, isoutbound, &f, NULL, 1); - if (!res) - ast_mutex_unlock(&p->lock); - if (ochan) - ast_hangup(ochan); - return 0; -} - -/*! \brief Create a call structure */ -static struct local_pvt *local_alloc(const char *data, int format) -{ - struct local_pvt *tmp = NULL; - char *c = NULL, *opts = NULL; - - if (!(tmp = ast_calloc(1, sizeof(*tmp)))) - return NULL; - - /* Initialize private structure information */ - ast_mutex_init(&tmp->lock); - ast_copy_string(tmp->exten, data, sizeof(tmp->exten)); - - /* Look for options */ - if ((opts = strchr(tmp->exten, '/'))) { - *opts++ = '\0'; - if (strchr(opts, 'n')) - ast_set_flag(tmp, LOCAL_NO_OPTIMIZATION); - } - - /* Look for a context */ - if ((c = strchr(tmp->exten, '@'))) - *c++ = '\0'; - - ast_copy_string(tmp->context, c ? c : "default", sizeof(tmp->context)); - - tmp->reqformat = format; - -#if 0 - /* We can't do this check here, because we don't know the CallerID yet, and - * the CallerID could potentially affect what step is actually taken (or - * even if that step exists). */ - if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) { - ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context); - tmp = local_pvt_destroy(tmp); - } else { -#endif - /* Add to list */ - AST_LIST_LOCK(&locals); - AST_LIST_INSERT_HEAD(&locals, tmp, list); - AST_LIST_UNLOCK(&locals); -#if 0 - } -#endif - - return tmp; -} - -/*! \brief Start new local channel */ -static struct ast_channel *local_new(struct local_pvt *p, int state) -{ - struct ast_channel *tmp = NULL, *tmp2 = NULL; - int randnum = ast_random() & 0xffff, fmt = 0; - const char *t; - int ama; - - /* Allocate two new Asterisk channels */ - /* safe accountcode */ - if (p->owner && p->owner->accountcode) - t = p->owner->accountcode; - else - t = ""; - - if (p->owner) - ama = p->owner->amaflags; - else - ama = 0; - if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x,1", p->exten, p->context, randnum)) - || !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x,2", p->exten, p->context, randnum))) { - if (tmp) - ast_channel_free(tmp); - if (tmp2) - ast_channel_free(tmp2); - ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n"); - return NULL; - } - - tmp2->tech = tmp->tech = &local_tech; - - tmp->nativeformats = p->reqformat; - tmp2->nativeformats = p->reqformat; - - /* Determine our read/write format and set it on each channel */ - fmt = ast_best_codec(p->reqformat); - tmp->writeformat = fmt; - tmp2->writeformat = fmt; - tmp->rawwriteformat = fmt; - tmp2->rawwriteformat = fmt; - tmp->readformat = fmt; - tmp2->readformat = fmt; - tmp->rawreadformat = fmt; - tmp2->rawreadformat = fmt; - - tmp->tech_pvt = p; - tmp2->tech_pvt = p; - - p->owner = tmp; - p->chan = tmp2; - p->u_owner = ast_module_user_add(p->owner); - p->u_chan = ast_module_user_add(p->chan); - - ast_copy_string(tmp->context, p->context, sizeof(tmp->context)); - ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context)); - ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten)); - tmp->priority = 1; - tmp2->priority = 1; - - return tmp; -} - -/*! \brief Part of PBX interface */ -static struct ast_channel *local_request(const char *type, int format, void *data, int *cause) -{ - struct local_pvt *p = NULL; - struct ast_channel *chan = NULL; - - /* Allocate a new private structure and then Asterisk channel */ - if ((p = local_alloc(data, format))) { - if (!(chan = local_new(p, AST_STATE_DOWN))) { - AST_LIST_LOCK(&locals); - AST_LIST_REMOVE(&locals, p, list); - AST_LIST_UNLOCK(&locals); - p = local_pvt_destroy(p); - } - } - - return chan; -} - -/*! \brief CLI command "local show channels" */ -static int locals_show(int fd, int argc, char **argv) -{ - struct local_pvt *p = NULL; - - if (argc != 3) - return RESULT_SHOWUSAGE; - - AST_LIST_LOCK(&locals); - if (!AST_LIST_EMPTY(&locals)) { - AST_LIST_TRAVERSE(&locals, p, list) { - ast_mutex_lock(&p->lock); - ast_cli(fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context); - ast_mutex_unlock(&p->lock); - } - } else - ast_cli(fd, "No local channels in use\n"); - AST_LIST_UNLOCK(&locals); - - return RESULT_SUCCESS; -} - -static char show_locals_usage[] = -"Usage: local show channels\n" -" Provides summary information on active local proxy channels.\n"; - -static struct ast_cli_entry cli_local[] = { - { { "local", "show", "channels", NULL }, - locals_show, "List status of local channels", - show_locals_usage }, -}; - -/*! \brief Load module into PBX, register channel */ -static int load_module(void) -{ - /* Make sure we can register our channel type */ - if (ast_channel_register(&local_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n"); - return -1; - } - ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry)); - return 0; -} - -/*! \brief Unload the local proxy channel from Asterisk */ -static int unload_module(void) -{ - struct local_pvt *p = NULL; - - /* First, take us out of the channel loop */ - ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry)); - ast_channel_unregister(&local_tech); - if (!AST_LIST_LOCK(&locals)) { - /* Hangup all interfaces if they have an owner */ - AST_LIST_TRAVERSE(&locals, p, list) { - if (p->owner) - ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD); - } - AST_LIST_UNLOCK(&locals); - } else { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - return -1; - } - return 0; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Local Proxy Channel (Note: used internally by other modules)"); diff --git a/1.4.23-rc4/channels/chan_mgcp.c b/1.4.23-rc4/channels/chan_mgcp.c deleted file mode 100644 index 1d5114bee..000000000 --- a/1.4.23-rc4/channels/chan_mgcp.c +++ /dev/null @@ -1,4430 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief Implementation of Media Gateway Control Protocol - * - * \author Mark Spencer <markster@digium.com> - * - * \par See also - * \arg \ref Config_mgcp - * - * \ingroup channel_drivers - */ -/*** MODULEINFO - <depend>res_features</depend> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <net/if.h> -#include <errno.h> -#include <stdlib.h> -#include <fcntl.h> -#include <netdb.h> -#include <sys/signal.h> -#include <signal.h> -#include <netinet/in.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <arpa/inet.h> -#include <ctype.h> - -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/lock.h" -#include "asterisk/sched.h" -#include "asterisk/io.h" -#include "asterisk/rtp.h" -#include "asterisk/acl.h" -#include "asterisk/callerid.h" -#include "asterisk/cli.h" -#include "asterisk/say.h" -#include "asterisk/cdr.h" -#include "asterisk/astdb.h" -#include "asterisk/features.h" -#include "asterisk/app.h" -#include "asterisk/musiconhold.h" -#include "asterisk/utils.h" -#include "asterisk/causes.h" -#include "asterisk/dsp.h" -#include "asterisk/devicestate.h" -#include "asterisk/stringfields.h" -#include "asterisk/abstract_jb.h" - -#ifndef IPTOS_MINCOST -#define IPTOS_MINCOST 0x02 -#endif - -/* - * Define to work around buggy dlink MGCP phone firmware which - * appears not to know that "rt" is part of the "G" package. - */ -/* #define DLINK_BUGGY_FIRMWARE */ - -#define MGCPDUMPER -#define DEFAULT_EXPIRY 120 -#define MAX_EXPIRY 3600 -#define CANREINVITE 1 - -#ifndef INADDR_NONE -#define INADDR_NONE (in_addr_t)(-1) -#endif - -/*! Global jitterbuffer configuration - by default, jb is disabled */ -static struct ast_jb_conf default_jbconf = -{ - .flags = 0, - .max_size = -1, - .resync_threshold = -1, - .impl = "" -}; -static struct ast_jb_conf global_jbconf; - -static const char tdesc[] = "Media Gateway Control Protocol (MGCP)"; -static const char config[] = "mgcp.conf"; - -#define MGCP_DTMF_RFC2833 (1 << 0) -#define MGCP_DTMF_INBAND (1 << 1) -#define MGCP_DTMF_HYBRID (1 << 2) - -#define DEFAULT_MGCP_GW_PORT 2427 /*!< From RFC 2705 */ -#define DEFAULT_MGCP_CA_PORT 2727 /*!< From RFC 2705 */ -#define MGCP_MAX_PACKET 1500 /*!< Also from RFC 2543, should sub headers tho */ -#define DEFAULT_RETRANS 1000 /*!< How frequently to retransmit */ -#define MAX_RETRANS 5 /*!< Try only 5 times for retransmissions */ - -/*! MGCP rtp stream modes { */ -#define MGCP_CX_SENDONLY 0 -#define MGCP_CX_RECVONLY 1 -#define MGCP_CX_SENDRECV 2 -#define MGCP_CX_CONF 3 -#define MGCP_CX_CONFERENCE 3 -#define MGCP_CX_MUTE 4 -#define MGCP_CX_INACTIVE 4 -/*! } */ - -static char *mgcp_cxmodes[] = { - "sendonly", - "recvonly", - "sendrecv", - "confrnce", - "inactive" -}; - -enum { - MGCP_CMD_EPCF, - MGCP_CMD_CRCX, - MGCP_CMD_MDCX, - MGCP_CMD_DLCX, - MGCP_CMD_RQNT, - MGCP_CMD_NTFY, - MGCP_CMD_AUEP, - MGCP_CMD_AUCX, - MGCP_CMD_RSIP -}; - -static char context[AST_MAX_EXTENSION] = "default"; - -static char language[MAX_LANGUAGE] = ""; -static char musicclass[MAX_MUSICCLASS] = ""; -static char cid_num[AST_MAX_EXTENSION] = ""; -static char cid_name[AST_MAX_EXTENSION] = ""; - -static int dtmfmode = 0; -static int nat = 0; - -static ast_group_t cur_callergroup = 0; -static ast_group_t cur_pickupgroup = 0; - -static int tos = 0; - -static int immediate = 0; - -static int callwaiting = 0; - -static int callreturn = 0; - -static int slowsequence = 0; - -static int threewaycalling = 0; - -/*! This is for flashhook transfers */ -static int transfer = 0; - -static int cancallforward = 0; - -static int singlepath = 0; - -static int canreinvite = CANREINVITE; - -static char accountcode[AST_MAX_ACCOUNT_CODE] = ""; - -static char mailbox[AST_MAX_EXTENSION]; - -static int amaflags = 0; - -static int adsi = 0; - -static unsigned int oseq; - -/*! Wait up to 16 seconds for first digit (FXO logic) */ -static int firstdigittimeout = 16000; - -/*! How long to wait for following digits (FXO logic) */ -static int gendigittimeout = 8000; - -/*! How long to wait for an extra digit, if there is an ambiguous match */ -static int matchdigittimeout = 3000; - -/*! Protect the monitoring thread, so only one process can kill or start it, and not - when it's doing something critical. */ -AST_MUTEX_DEFINE_STATIC(netlock); - -AST_MUTEX_DEFINE_STATIC(monlock); - -/*! This is the thread for the monitor which checks for input on the channels - which are not currently in use. */ -static pthread_t monitor_thread = AST_PTHREADT_NULL; - -static int restart_monitor(void); - -static int capability = AST_FORMAT_ULAW; -static int nonCodecCapability = AST_RTP_DTMF; - -static char ourhost[MAXHOSTNAMELEN]; -static struct in_addr __ourip; -static int ourport; - -static int mgcpdebug = 0; - -static struct sched_context *sched; -static struct io_context *io; -/*! The private structures of the mgcp channels are linked for - ! selecting outgoing channels */ - -#define MGCP_MAX_HEADERS 64 -#define MGCP_MAX_LINES 64 - -struct mgcp_request { - int len; - char *verb; - char *identifier; - char *endpoint; - char *version; - int headers; /*!< MGCP Headers */ - char *header[MGCP_MAX_HEADERS]; - int lines; /*!< SDP Content */ - char *line[MGCP_MAX_LINES]; - char data[MGCP_MAX_PACKET]; - int cmd; /*!< int version of verb = command */ - unsigned int trid; /*!< int version of identifier = transaction id */ - struct mgcp_request *next; /*!< next in the queue */ -}; - -/*! \brief mgcp_message: MGCP message for queuing up */ -struct mgcp_message { - struct mgcp_endpoint *owner_ep; - struct mgcp_subchannel *owner_sub; - int retrans; - unsigned long expire; - unsigned int seqno; - int len; - struct mgcp_message *next; - char buf[0]; -}; - -#define RESPONSE_TIMEOUT 30 /*!< in seconds */ - -struct mgcp_response { - time_t whensent; - int len; - int seqno; - struct mgcp_response *next; - char buf[0]; -}; - -#define MAX_SUBS 2 - -#define SUB_REAL 0 -#define SUB_ALT 1 - -struct mgcp_subchannel { - /*! subchannel magic string. - Needed to prove that any subchannel pointer passed by asterisk - really points to a valid subchannel memory area. - Ugly.. But serves the purpose for the time being. - */ -#define MGCP_SUBCHANNEL_MAGIC "!978!" - char magic[6]; - ast_mutex_t lock; - int id; - struct ast_channel *owner; - struct mgcp_endpoint *parent; - struct ast_rtp *rtp; - struct sockaddr_in tmpdest; - char txident[80]; /*! \todo FIXME txident is replaced by rqnt_ident in endpoint. - This should be obsoleted */ - char cxident[80]; - char callid[80]; - int cxmode; - struct mgcp_request *cx_queue; /*!< pending CX commands */ - ast_mutex_t cx_queue_lock; /*!< CX queue lock */ - int nat; - int iseq; /*!< Not used? RTP? */ - int outgoing; - int alreadygone; - struct mgcp_subchannel *next; /*!< for out circular linked list */ -}; - -#define MGCP_ONHOOK 1 -#define MGCP_OFFHOOK 2 - -#define TYPE_TRUNK 1 -#define TYPE_LINE 2 - -struct mgcp_endpoint { - ast_mutex_t lock; - char name[80]; - struct mgcp_subchannel *sub; /*!< Pointer to our current connection, channel and stuff */ - char accountcode[AST_MAX_ACCOUNT_CODE]; - char exten[AST_MAX_EXTENSION]; /*!< Extention where to start */ - char context[AST_MAX_EXTENSION]; - char language[MAX_LANGUAGE]; - char cid_num[AST_MAX_EXTENSION]; /*!< Caller*ID number */ - char cid_name[AST_MAX_EXTENSION]; /*!< Caller*ID name */ - char lastcallerid[AST_MAX_EXTENSION]; /*!< Last Caller*ID */ - char call_forward[AST_MAX_EXTENSION]; /*!< Last Caller*ID */ - char mailbox[AST_MAX_EXTENSION]; - char musicclass[MAX_MUSICCLASS]; - char curtone[80]; /*!< Current tone */ - char dtmf_buf[AST_MAX_EXTENSION]; /*!< place to collect digits be */ - ast_group_t callgroup; - ast_group_t pickupgroup; - int callwaiting; - int hascallwaiting; - int transfer; - int threewaycalling; - int singlepath; - int cancallforward; - int canreinvite; - int callreturn; - int dnd; /* How does this affect callwait? Do we just deny a mgcp_request if we're dnd? */ - int hascallerid; - int hidecallerid; - int dtmfmode; - int amaflags; - int type; - int slowsequence; /*!< MS: Sequence the endpoint as a whole */ - int group; - int iseq; /*!< Not used? */ - int lastout; /*!< tracking this on the subchannels. Is it needed here? */ - int needdestroy; /*!< Not used? */ - int capability; - int nonCodecCapability; - int onhooktime; - int msgstate; /*!< voicemail message state */ - int immediate; - int hookstate; - int adsi; - char rqnt_ident[80]; /*!< request identifier */ - struct mgcp_request *rqnt_queue; /*!< pending RQNT commands */ - ast_mutex_t rqnt_queue_lock; - struct mgcp_request *cmd_queue; /*!< pending commands other than RQNT */ - ast_mutex_t cmd_queue_lock; - int delme; /*!< needed for reload */ - int needaudit; /*!< needed for reload */ - struct ast_dsp *dsp; /*!< XXX Should there be a dsp/subchannel? XXX */ - /* owner is tracked on the subchannels, and the *sub indicates whos in charge */ - /* struct ast_channel *owner; */ - /* struct ast_rtp *rtp; */ - /* struct sockaddr_in tmpdest; */ - /* message go the the endpoint and not the channel so they stay here */ - struct mgcp_endpoint *next; - struct mgcp_gateway *parent; -}; - -static struct mgcp_gateway { - /* A gateway containing one or more endpoints */ - char name[80]; - int isnamedottedip; /*!< is the name FQDN or dotted ip */ - struct sockaddr_in addr; - struct sockaddr_in defaddr; - struct in_addr ourip; - int dynamic; - int expire; /*!< XXX Should we ever expire dynamic registrations? XXX */ - struct mgcp_endpoint *endpoints; - struct ast_ha *ha; -/* obsolete - time_t lastouttime; - int lastout; - int messagepending; -*/ -/* Wildcard endpoint name */ - char wcardep[30]; - struct mgcp_message *msgs; /*!< gw msg queue */ - ast_mutex_t msgs_lock; /*!< queue lock */ - int retransid; /*!< retrans timer id */ - int delme; /*!< needed for reload */ - struct mgcp_response *responses; - struct mgcp_gateway *next; -} *gateways; - -AST_MUTEX_DEFINE_STATIC(mgcp_reload_lock); -static int mgcp_reloading = 0; - -/*! \brief gatelock: mutex for gateway/endpoint lists */ -AST_MUTEX_DEFINE_STATIC(gatelock); - -static int mgcpsock = -1; - -static struct sockaddr_in bindaddr; - -static struct ast_frame *mgcp_read(struct ast_channel *ast); -static int transmit_response(struct mgcp_subchannel *sub, char *msg, struct mgcp_request *req, char *msgrest); -static int transmit_notify_request(struct mgcp_subchannel *sub, char *tone); -static int transmit_modify_request(struct mgcp_subchannel *sub); -static int transmit_notify_request_with_callerid(struct mgcp_subchannel *sub, char *tone, char *callernum, char *callername); -static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs); -static int transmit_connection_del(struct mgcp_subchannel *sub); -static int transmit_audit_endpoint(struct mgcp_endpoint *p); -static void start_rtp(struct mgcp_subchannel *sub); -static void handle_response(struct mgcp_endpoint *p, struct mgcp_subchannel *sub, - int result, unsigned int ident, struct mgcp_request *resp); -static void dump_cmd_queues(struct mgcp_endpoint *p, struct mgcp_subchannel *sub); -static int mgcp_do_reload(void); -static int mgcp_reload(int fd, int argc, char *argv[]); - -static struct ast_channel *mgcp_request(const char *type, int format, void *data, int *cause); -static int mgcp_call(struct ast_channel *ast, char *dest, int timeout); -static int mgcp_hangup(struct ast_channel *ast); -static int mgcp_answer(struct ast_channel *ast); -static struct ast_frame *mgcp_read(struct ast_channel *ast); -static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame); -static int mgcp_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen); -static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -static int mgcp_senddigit_begin(struct ast_channel *ast, char digit); -static int mgcp_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int mgcp_devicestate(void *data); -static void add_header_offhook(struct mgcp_subchannel *sub, struct mgcp_request *resp); - -static const struct ast_channel_tech mgcp_tech = { - .type = "MGCP", - .description = tdesc, - .capabilities = AST_FORMAT_ULAW, - .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER, - .requester = mgcp_request, - .devicestate = mgcp_devicestate, - .call = mgcp_call, - .hangup = mgcp_hangup, - .answer = mgcp_answer, - .read = mgcp_read, - .write = mgcp_write, - .indicate = mgcp_indicate, - .fixup = mgcp_fixup, - .send_digit_begin = mgcp_senddigit_begin, - .send_digit_end = mgcp_senddigit_end, - .bridge = ast_rtp_bridge, -}; - -static int has_voicemail(struct mgcp_endpoint *p) -{ - return ast_app_has_voicemail(p->mailbox, NULL); -} - -static int unalloc_sub(struct mgcp_subchannel *sub) -{ - struct mgcp_endpoint *p = sub->parent; - if (p->sub == sub) { - ast_log(LOG_WARNING, "Trying to unalloc the real channel %s@%s?!?\n", p->name, p->parent->name); - return -1; - } - ast_log(LOG_DEBUG, "Released sub %d of channel %s@%s\n", sub->id, p->name, p->parent->name); - - sub->owner = NULL; - if (!ast_strlen_zero(sub->cxident)) { - transmit_connection_del(sub); - } - sub->cxident[0] = '\0'; - sub->callid[0] = '\0'; - sub->cxmode = MGCP_CX_INACTIVE; - sub->outgoing = 0; - sub->alreadygone = 0; - memset(&sub->tmpdest, 0, sizeof(sub->tmpdest)); - if (sub->rtp) { - ast_rtp_destroy(sub->rtp); - sub->rtp = NULL; - } - dump_cmd_queues(NULL, sub); /* SC */ - return 0; -} - -/* modified for new transport mechanism */ -static int __mgcp_xmit(struct mgcp_gateway *gw, char *data, int len) -{ - int res; - if (gw->addr.sin_addr.s_addr) - res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&gw->addr, sizeof(struct sockaddr_in)); - else - res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&gw->defaddr, sizeof(struct sockaddr_in)); - if (res != len) { - ast_log(LOG_WARNING, "mgcp_xmit returned %d: %s\n", res, strerror(errno)); - } - return res; -} - -static int resend_response(struct mgcp_subchannel *sub, struct mgcp_response *resp) -{ - struct mgcp_endpoint *p = sub->parent; - int res; - if (mgcpdebug) { - ast_verbose("Retransmitting:\n%s\n to %s:%d\n", resp->buf, ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port)); - } - res = __mgcp_xmit(p->parent, resp->buf, resp->len); - if (res > 0) - res = 0; - return res; -} - -static int send_response(struct mgcp_subchannel *sub, struct mgcp_request *req) -{ - struct mgcp_endpoint *p = sub->parent; - int res; - if (mgcpdebug) { - ast_verbose("Transmitting:\n%s\n to %s:%d\n", req->data, ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port)); - } - res = __mgcp_xmit(p->parent, req->data, req->len); - if (res > 0) - res = 0; - return res; -} - -/* modified for new transport framework */ -static void dump_queue(struct mgcp_gateway *gw, struct mgcp_endpoint *p) -{ - struct mgcp_message *cur, *q = NULL, *w, *prev; - - ast_mutex_lock(&gw->msgs_lock); - prev = NULL, cur = gw->msgs; - while (cur) { - if (!p || cur->owner_ep == p) { - if (prev) - prev->next = cur->next; - else - gw->msgs = cur->next; - - ast_log(LOG_NOTICE, "Removing message from %s transaction %u\n", - gw->name, cur->seqno); - - w = cur; - cur = cur->next; - if (q) { - w->next = q; - } else { - w->next = NULL; - } - q = w; - } else { - prev = cur, cur=cur->next; - } - } - ast_mutex_unlock(&gw->msgs_lock); - - while (q) { - cur = q; - q = q->next; - free(cur); - } -} - -static void mgcp_queue_frame(struct mgcp_subchannel *sub, struct ast_frame *f) -{ - for(;;) { - if (sub->owner) { - if (!ast_mutex_trylock(&sub->owner->lock)) { - ast_queue_frame(sub->owner, f); - ast_mutex_unlock(&sub->owner->lock); - break; - } else { - DEADLOCK_AVOIDANCE(&sub->lock); - } - } else - break; - } -} - -static void mgcp_queue_hangup(struct mgcp_subchannel *sub) -{ - for(;;) { - if (sub->owner) { - if (!ast_mutex_trylock(&sub->owner->lock)) { - ast_queue_hangup(sub->owner); - ast_mutex_unlock(&sub->owner->lock); - break; - } else { - DEADLOCK_AVOIDANCE(&sub->lock); - } - } else - break; - } -} - -static void mgcp_queue_control(struct mgcp_subchannel *sub, int control) -{ - struct ast_frame f = { AST_FRAME_CONTROL, }; - f.subclass = control; - return mgcp_queue_frame(sub, &f); -} - -static int retrans_pkt(const void *data) -{ - struct mgcp_gateway *gw = (struct mgcp_gateway *)data; - struct mgcp_message *cur, *exq = NULL, *w, *prev; - int res = 0; - - /* find out expired msgs */ - ast_mutex_lock(&gw->msgs_lock); - - prev = NULL, cur = gw->msgs; - while (cur) { - if (cur->retrans < MAX_RETRANS) { - cur->retrans++; - if (mgcpdebug) { - ast_verbose("Retransmitting #%d transaction %u on [%s]\n", - cur->retrans, cur->seqno, gw->name); - } - __mgcp_xmit(gw, cur->buf, cur->len); - - prev = cur; - cur = cur->next; - } else { - if (prev) - prev->next = cur->next; - else - gw->msgs = cur->next; - - ast_log(LOG_WARNING, "Maximum retries exceeded for transaction %u on [%s]\n", - cur->seqno, gw->name); - - w = cur; - cur = cur->next; - - if (exq) { - w->next = exq; - } else { - w->next = NULL; - } - exq = w; - } - } - - if (!gw->msgs) { - gw->retransid = -1; - res = 0; - } else { - res = 1; - } - ast_mutex_unlock(&gw->msgs_lock); - - while (exq) { - cur = exq; - /* time-out transaction */ - handle_response(cur->owner_ep, cur->owner_sub, 406, cur->seqno, NULL); - exq = exq->next; - free(cur); - } - - return res; -} - -/* modified for the new transaction mechanism */ -static int mgcp_postrequest(struct mgcp_endpoint *p, struct mgcp_subchannel *sub, - char *data, int len, unsigned int seqno) -{ - struct mgcp_message *msg = malloc(sizeof(struct mgcp_message) + len); - struct mgcp_message *cur; - struct mgcp_gateway *gw = ((p && p->parent) ? p->parent : NULL); - struct timeval tv; - - if (!msg) { - return -1; - } - if (!gw) { - return -1; - } -/* SC - time(&t); - if (gw->messagepending && (gw->lastouttime + 20 < t)) { - ast_log(LOG_NOTICE, "Timeout waiting for response to message:%d, lastouttime: %ld, now: %ld. Dumping pending queue\n", - gw->msgs ? gw->msgs->seqno : -1, (long) gw->lastouttime, (long) t); - dump_queue(sub->parent); - } -*/ - msg->owner_sub = sub; - msg->owner_ep = p; - msg->seqno = seqno; - msg->next = NULL; - msg->len = len; - msg->retrans = 0; - memcpy(msg->buf, data, msg->len); - - ast_mutex_lock(&gw->msgs_lock); - cur = gw->msgs; - if (cur) { - while(cur->next) - cur = cur->next; - cur->next = msg; - } else { - gw->msgs = msg; - } - - if (gettimeofday(&tv, NULL) < 0) { - /* This shouldn't ever happen, but let's be sure */ - ast_log(LOG_NOTICE, "gettimeofday() failed!\n"); - } else { - msg->expire = tv.tv_sec * 1000 + tv.tv_usec / 1000 + DEFAULT_RETRANS; - - if (gw->retransid == -1) - gw->retransid = ast_sched_add(sched, DEFAULT_RETRANS, retrans_pkt, (void *)gw); - } - ast_mutex_unlock(&gw->msgs_lock); -/* SC - if (!gw->messagepending) { - gw->messagepending = 1; - gw->lastout = seqno; - gw->lastouttime = t; -*/ - __mgcp_xmit(gw, msg->buf, msg->len); - /* XXX Should schedule retransmission XXX */ -/* SC - } else - ast_log(LOG_DEBUG, "Deferring transmission of transaction %d\n", seqno); -*/ - return 0; -} - -/* modified for new transport */ -static int send_request(struct mgcp_endpoint *p, struct mgcp_subchannel *sub, - struct mgcp_request *req, unsigned int seqno) -{ - int res = 0; - struct mgcp_request **queue, *q, *r, *t; - ast_mutex_t *l; - - ast_log(LOG_DEBUG, "Slow sequence is %d\n", p->slowsequence); - if (p->slowsequence) { - queue = &p->cmd_queue; - l = &p->cmd_queue_lock; - ast_mutex_lock(l); - } else { - switch (req->cmd) { - case MGCP_CMD_DLCX: - queue = &sub->cx_queue; - l = &sub->cx_queue_lock; - ast_mutex_lock(l); - q = sub->cx_queue; - /* delete pending cx cmds */ - while (q) { - r = q->next; - free(q); - q = r; - } - *queue = NULL; - break; - - case MGCP_CMD_CRCX: - case MGCP_CMD_MDCX: - queue = &sub->cx_queue; - l = &sub->cx_queue_lock; - ast_mutex_lock(l); - break; - - case MGCP_CMD_RQNT: - queue = &p->rqnt_queue; - l = &p->rqnt_queue_lock; - ast_mutex_lock(l); - break; - - default: - queue = &p->cmd_queue; - l = &p->cmd_queue_lock; - ast_mutex_lock(l); - break; - } - } - - r = (struct mgcp_request *) malloc (sizeof(struct mgcp_request)); - if (!r) { - ast_log(LOG_WARNING, "Cannot post MGCP request: insufficient memory\n"); - ast_mutex_unlock(l); - return -1; - } - memcpy(r, req, sizeof(struct mgcp_request)); - - if (!(*queue)) { - if (mgcpdebug) { - ast_verbose("Posting Request:\n%s to %s:%d\n", req->data, - ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port)); - } - - res = mgcp_postrequest(p, sub, req->data, req->len, seqno); - } else { - if (mgcpdebug) { - ast_verbose("Queueing Request:\n%s to %s:%d\n", req->data, - ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port)); - } - } - - /* XXX find tail. We could also keep tail in the data struct for faster access */ - for (t = *queue; t && t->next; t = t->next); - - r->next = NULL; - if (t) - t->next = r; - else - *queue = r; - - ast_mutex_unlock(l); - - return res; -} - -static int mgcp_call(struct ast_channel *ast, char *dest, int timeout) -{ - int res; - struct mgcp_endpoint *p; - struct mgcp_subchannel *sub; - char tone[50] = ""; - const char *distinctive_ring = NULL; - struct varshead *headp; - struct ast_var_t *current; - - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_call(%s)\n", ast->name); - } - sub = ast->tech_pvt; - p = sub->parent; - headp = &ast->varshead; - AST_LIST_TRAVERSE(headp,current,entries) { - /* Check whether there is an ALERT_INFO variable */ - if (strcasecmp(ast_var_name(current),"ALERT_INFO") == 0) { - distinctive_ring = ast_var_value(current); - } - } - - ast_mutex_lock(&sub->lock); - switch (p->hookstate) { - case MGCP_OFFHOOK: - if (!ast_strlen_zero(distinctive_ring)) { - snprintf(tone, sizeof(tone), "L/wt%s", distinctive_ring); - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP distinctive callwait %s\n", tone); - } - } else { - snprintf(tone, sizeof(tone), "L/wt"); - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP normal callwait %s\n", tone); - } - } - break; - case MGCP_ONHOOK: - default: - if (!ast_strlen_zero(distinctive_ring)) { - snprintf(tone, sizeof(tone), "L/r%s", distinctive_ring); - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP distinctive ring %s\n", tone); - } - } else { - snprintf(tone, sizeof(tone), "L/rg"); - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP default ring\n"); - } - } - break; - } - - if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { - ast_log(LOG_WARNING, "mgcp_call called on %s, neither down nor reserved\n", ast->name); - ast_mutex_unlock(&sub->lock); - return -1; - } - - res = 0; - sub->outgoing = 1; - sub->cxmode = MGCP_CX_RECVONLY; - if (p->type == TYPE_LINE) { - if (!sub->rtp) { - start_rtp(sub); - } else { - transmit_modify_request(sub); - } - - if (sub->next->owner && !ast_strlen_zero(sub->next->cxident) && !ast_strlen_zero(sub->next->callid)) { - /* try to prevent a callwait from disturbing the other connection */ - sub->next->cxmode = MGCP_CX_RECVONLY; - transmit_modify_request(sub->next); - } - - transmit_notify_request_with_callerid(sub, tone, ast->cid.cid_num, ast->cid.cid_name); - ast_setstate(ast, AST_STATE_RINGING); - - if (sub->next->owner && !ast_strlen_zero(sub->next->cxident) && !ast_strlen_zero(sub->next->callid)) { - /* Put the connection back in sendrecv */ - sub->next->cxmode = MGCP_CX_SENDRECV; - transmit_modify_request(sub->next); - } - } else { - ast_log(LOG_NOTICE, "Don't know how to dial on trunks yet\n"); - res = -1; - } - ast_mutex_unlock(&sub->lock); - ast_queue_control(ast, AST_CONTROL_RINGING); - return res; -} - -static int mgcp_hangup(struct ast_channel *ast) -{ - struct mgcp_subchannel *sub = ast->tech_pvt; - struct mgcp_endpoint *p = sub->parent; - - if (option_debug) { - ast_log(LOG_DEBUG, "mgcp_hangup(%s)\n", ast->name); - } - if (!ast->tech_pvt) { - ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n"); - return 0; - } - if (strcmp(sub->magic, MGCP_SUBCHANNEL_MAGIC)) { - ast_log(LOG_DEBUG, "Invalid magic. MGCP subchannel freed up already.\n"); - return 0; - } - ast_mutex_lock(&sub->lock); - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s\n", ast->name, p->name, p->parent->name); - } - - if ((p->dtmfmode & MGCP_DTMF_INBAND) && p->dsp) { - /* check whether other channel is active. */ - if (!sub->next->owner) { - if (p->dtmfmode & MGCP_DTMF_HYBRID) - p->dtmfmode &= ~MGCP_DTMF_INBAND; - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_2 "MGCP free dsp on %s@%s\n", p->name, p->parent->name); - } - ast_dsp_free(p->dsp); - p->dsp = NULL; - } - } - - sub->owner = NULL; - if (!ast_strlen_zero(sub->cxident)) { - transmit_connection_del(sub); - } - sub->cxident[0] = '\0'; - if ((sub == p->sub) && sub->next->owner) { - if (p->hookstate == MGCP_OFFHOOK) { - if (sub->next->owner && ast_bridged_channel(sub->next->owner)) { - transmit_notify_request_with_callerid(p->sub, "L/wt", ast_bridged_channel(sub->next->owner)->cid.cid_num, ast_bridged_channel(sub->next->owner)->cid.cid_name); - } - } else { - /* set our other connection as the primary and swith over to it */ - p->sub = sub->next; - p->sub->cxmode = MGCP_CX_RECVONLY; - transmit_modify_request(p->sub); - if (sub->next->owner && ast_bridged_channel(sub->next->owner)) { - transmit_notify_request_with_callerid(p->sub, "L/rg", ast_bridged_channel(sub->next->owner)->cid.cid_num, ast_bridged_channel(sub->next->owner)->cid.cid_name); - } - } - - } else if ((sub == p->sub->next) && p->hookstate == MGCP_OFFHOOK) { - transmit_notify_request(sub, "L/v"); - } else if (p->hookstate == MGCP_OFFHOOK) { - transmit_notify_request(sub, "L/ro"); - } else { - transmit_notify_request(sub, ""); - } - - ast->tech_pvt = NULL; - sub->alreadygone = 0; - sub->outgoing = 0; - sub->cxmode = MGCP_CX_INACTIVE; - sub->callid[0] = '\0'; - if (p) { - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - } - /* Reset temporary destination */ - memset(&sub->tmpdest, 0, sizeof(sub->tmpdest)); - if (sub->rtp) { - ast_rtp_destroy(sub->rtp); - sub->rtp = NULL; - } - - ast_module_unref(ast_module_info->self); - - if ((p->hookstate == MGCP_ONHOOK) && (!sub->next->rtp)) { - p->hidecallerid = 0; - if (p->hascallwaiting && !p->callwaiting) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Enabling call waiting on %s\n", ast->name); - p->callwaiting = -1; - } - if (has_voicemail(p)) { - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s set vmwi(+)\n", - ast->name, p->name, p->parent->name); - } - transmit_notify_request(sub, "L/vmwi(+)"); - } else { - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s set vmwi(-)\n", - ast->name, p->name, p->parent->name); - } - transmit_notify_request(sub, "L/vmwi(-)"); - } - } - ast_mutex_unlock(&sub->lock); - return 0; -} - -static int mgcp_show_endpoints(int fd, int argc, char *argv[]) -{ - struct mgcp_gateway *g; - struct mgcp_endpoint *e; - int hasendpoints = 0; - - if (argc != 3) - return RESULT_SHOWUSAGE; - ast_mutex_lock(&gatelock); - g = gateways; - while(g) { - e = g->endpoints; - ast_cli(fd, "Gateway '%s' at %s (%s)\n", g->name, g->addr.sin_addr.s_addr ? ast_inet_ntoa(g->addr.sin_addr) : ast_inet_ntoa(g->defaddr.sin_addr), g->dynamic ? "Dynamic" : "Static"); - while(e) { - /* Don't show wilcard endpoint */ - if (strcmp(e->name, g->wcardep) !=0) - ast_cli(fd, " -- '%s@%s in '%s' is %s\n", e->name, g->name, e->context, e->sub->owner ? "active" : "idle"); - hasendpoints = 1; - e = e->next; - } - if (!hasendpoints) { - ast_cli(fd, " << No Endpoints Defined >> "); - } - g = g->next; - } - ast_mutex_unlock(&gatelock); - return RESULT_SUCCESS; -} - -static char show_endpoints_usage[] = -"Usage: mgcp show endpoints\n" -" Lists all endpoints known to the MGCP (Media Gateway Control Protocol) subsystem.\n"; - -static char audit_endpoint_usage[] = -"Usage: mgcp audit endpoint <endpointid>\n" -" Lists the capabilities of an endpoint in the MGCP (Media Gateway Control Protocol) subsystem.\n" -" mgcp debug MUST be on to see the results of this command.\n"; - -static char debug_usage[] = -"Usage: mgcp set debug\n" -" Enables dumping of MGCP packets for debugging purposes\n"; - -static char no_debug_usage[] = -"Usage: mgcp set debug off\n" -" Disables dumping of MGCP packets for debugging purposes\n"; - -static char mgcp_reload_usage[] = -"Usage: mgcp reload\n" -" Reloads MGCP configuration from mgcp.conf\n" -" Deprecated: please use 'reload chan_mgcp.so' instead.\n"; - -static int mgcp_audit_endpoint(int fd, int argc, char *argv[]) -{ - struct mgcp_gateway *g; - struct mgcp_endpoint *e; - int found = 0; - char *ename,*gname, *c; - - if (!mgcpdebug) { - return RESULT_SHOWUSAGE; - } - if (argc != 4) - return RESULT_SHOWUSAGE; - /* split the name into parts by null */ - ename = argv[3]; - gname = ename; - while (*gname) { - if (*gname == '@') { - *gname = 0; - gname++; - break; - } - gname++; - } - if (gname[0] == '[') - gname++; - if ((c = strrchr(gname, ']'))) - *c = '\0'; - ast_mutex_lock(&gatelock); - g = gateways; - while(g) { - if (!strcasecmp(g->name, gname)) { - e = g->endpoints; - while(e) { - if (!strcasecmp(e->name, ename)) { - found = 1; - transmit_audit_endpoint(e); - break; - } - e = e->next; - } - if (found) { - break; - } - } - g = g->next; - } - if (!found) { - ast_cli(fd, " << Could not find endpoint >> "); - } - ast_mutex_unlock(&gatelock); - return RESULT_SUCCESS; -} - -static int mgcp_do_debug(int fd, int argc, char *argv[]) -{ - if (argc != 3) - return RESULT_SHOWUSAGE; - mgcpdebug = 1; - ast_cli(fd, "MGCP Debugging Enabled\n"); - return RESULT_SUCCESS; -} - -static int mgcp_no_debug(int fd, int argc, char *argv[]) -{ - if (argc != 4) - return RESULT_SHOWUSAGE; - mgcpdebug = 0; - ast_cli(fd, "MGCP Debugging Disabled\n"); - return RESULT_SUCCESS; -} - -static struct ast_cli_entry cli_mgcp[] = { - { { "mgcp", "audit", "endpoint", NULL }, - mgcp_audit_endpoint, "Audit specified MGCP endpoint", - audit_endpoint_usage }, - - { { "mgcp", "show", "endpoints", NULL }, - mgcp_show_endpoints, "List defined MGCP endpoints", - show_endpoints_usage }, - - { { "mgcp", "set", "debug", NULL }, - mgcp_do_debug, "Enable MGCP debugging", - debug_usage }, - - { { "mgcp", "set", "debug", "off", NULL }, - mgcp_no_debug, "Disable MGCP debugging", - no_debug_usage }, - - { { "mgcp", "reload", NULL }, - mgcp_reload, "Reload MGCP configuration", - mgcp_reload_usage }, -}; - -static int mgcp_answer(struct ast_channel *ast) -{ - int res = 0; - struct mgcp_subchannel *sub = ast->tech_pvt; - struct mgcp_endpoint *p = sub->parent; - - ast_mutex_lock(&sub->lock); - sub->cxmode = MGCP_CX_SENDRECV; - if (!sub->rtp) { - start_rtp(sub); - } else { - transmit_modify_request(sub); - } - /* verbose level check */ - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_answer(%s) on %s@%s-%d\n", - ast->name, p->name, p->parent->name, sub->id); - } - if (ast->_state != AST_STATE_UP) { - ast_setstate(ast, AST_STATE_UP); - if (option_debug) - ast_log(LOG_DEBUG, "mgcp_answer(%s)\n", ast->name); - transmit_notify_request(sub, ""); - transmit_modify_request(sub); - } - ast_mutex_unlock(&sub->lock); - return res; -} - -static struct ast_frame *mgcp_rtp_read(struct mgcp_subchannel *sub) -{ - /* Retrieve audio/etc from channel. Assumes sub->lock is already held. */ - struct ast_frame *f; - - f = ast_rtp_read(sub->rtp); - /* Don't send RFC2833 if we're not supposed to */ - if (f && (f->frametype == AST_FRAME_DTMF) && !(sub->parent->dtmfmode & MGCP_DTMF_RFC2833)) - return &ast_null_frame; - if (sub->owner) { - /* We already hold the channel lock */ - if (f->frametype == AST_FRAME_VOICE) { - if (f->subclass != sub->owner->nativeformats) { - ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass); - sub->owner->nativeformats = f->subclass; - ast_set_read_format(sub->owner, sub->owner->readformat); - ast_set_write_format(sub->owner, sub->owner->writeformat); - } - /* Courtesy fearnor aka alex@pilosoft.com */ - if ((sub->parent->dtmfmode & MGCP_DTMF_INBAND) && (sub->parent->dsp)) { -#if 0 - ast_log(LOG_NOTICE, "MGCP ast_dsp_process\n"); -#endif - f = ast_dsp_process(sub->owner, sub->parent->dsp, f); - } - } - } - return f; -} - - -static struct ast_frame *mgcp_read(struct ast_channel *ast) -{ - struct ast_frame *f; - struct mgcp_subchannel *sub = ast->tech_pvt; - ast_mutex_lock(&sub->lock); - f = mgcp_rtp_read(sub); - ast_mutex_unlock(&sub->lock); - return f; -} - -static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame) -{ - struct mgcp_subchannel *sub = ast->tech_pvt; - int res = 0; - if (frame->frametype != AST_FRAME_VOICE) { - if (frame->frametype == AST_FRAME_IMAGE) - return 0; - else { - ast_log(LOG_WARNING, "Can't send %d type frames with MGCP write\n", frame->frametype); - return 0; - } - } else { - if (!(frame->subclass & ast->nativeformats)) { - ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n", - frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat); - return -1; - } - } - if (sub) { - ast_mutex_lock(&sub->lock); - if ((sub->parent->sub == sub) || !sub->parent->singlepath) { - if (sub->rtp) { - res = ast_rtp_write(sub->rtp, frame); - } - } - ast_mutex_unlock(&sub->lock); - } - return res; -} - -static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct mgcp_subchannel *sub = newchan->tech_pvt; - - ast_mutex_lock(&sub->lock); - ast_log(LOG_NOTICE, "mgcp_fixup(%s, %s)\n", oldchan->name, newchan->name); - if (sub->owner != oldchan) { - ast_mutex_unlock(&sub->lock); - ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner); - return -1; - } - sub->owner = newchan; - ast_mutex_unlock(&sub->lock); - return 0; -} - -static int mgcp_senddigit_begin(struct ast_channel *ast, char digit) -{ - struct mgcp_subchannel *sub = ast->tech_pvt; - struct mgcp_endpoint *p = sub->parent; - int res = 0; - - ast_mutex_lock(&sub->lock); - if (p->dtmfmode & MGCP_DTMF_INBAND || p->dtmfmode & MGCP_DTMF_HYBRID) { - ast_log(LOG_DEBUG, "Sending DTMF using inband/hybrid\n"); - res = -1; /* Let asterisk play inband indications */ - } else if (p->dtmfmode & MGCP_DTMF_RFC2833) { - ast_log(LOG_DEBUG, "Sending DTMF using RFC2833"); - ast_rtp_senddigit_begin(sub->rtp, digit); - } else { - ast_log(LOG_ERROR, "Don't know about DTMF_MODE %d\n", p->dtmfmode); - } - ast_mutex_unlock(&sub->lock); - - return res; -} - -static int mgcp_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ - struct mgcp_subchannel *sub = ast->tech_pvt; - struct mgcp_endpoint *p = sub->parent; - int res = 0; - char tmp[4]; - - ast_mutex_lock(&sub->lock); - if (p->dtmfmode & MGCP_DTMF_INBAND || p->dtmfmode & MGCP_DTMF_HYBRID) { - ast_log(LOG_DEBUG, "Stopping DTMF using inband/hybrid\n"); - res = -1; /* Tell Asterisk to stop inband indications */ - } else if (p->dtmfmode & MGCP_DTMF_RFC2833) { - ast_log(LOG_DEBUG, "Stopping DTMF using RFC2833\n"); - tmp[0] = 'D'; - tmp[1] = '/'; - tmp[2] = digit; - tmp[3] = '\0'; - transmit_notify_request(sub, tmp); - ast_rtp_senddigit_end(sub->rtp, digit); - } else { - ast_log(LOG_ERROR, "Don't know about DTMF_MODE %d\n", p->dtmfmode); - } - ast_mutex_unlock(&sub->lock); - - return res; -} - -/*! - * \brief mgcp_devicestate: channel callback for device status monitoring - * \param data tech/resource name of MGCP device to query - * - * Callback for device state management in channel subsystem - * to obtain device status (up/down) of a specific MGCP endpoint - * - * \return device status result (from devicestate.h) AST_DEVICE_INVALID (not available) or AST_DEVICE_UNKNOWN (available but unknown state) - */ -static int mgcp_devicestate(void *data) -{ - struct mgcp_gateway *g; - struct mgcp_endpoint *e = NULL; - char *tmp, *endpt, *gw; - int ret = AST_DEVICE_INVALID; - - endpt = ast_strdupa(data); - if ((tmp = strchr(endpt, '@'))) { - *tmp++ = '\0'; - gw = tmp; - } else - goto error; - - ast_mutex_lock(&gatelock); - g = gateways; - while (g) { - if (strcasecmp(g->name, gw) == 0) { - e = g->endpoints; - break; - } - g = g->next; - } - - if (!e) - goto error; - - while (e) { - if (strcasecmp(e->name, endpt) == 0) - break; - e = e->next; - } - - if (!e) - goto error; - - /* - * As long as the gateway/endpoint is valid, we'll - * assume that the device is available and its state - * can be tracked. - */ - ret = AST_DEVICE_UNKNOWN; - -error: - ast_mutex_unlock(&gatelock); - return ret; -} - -static char *control2str(int ind) { - switch (ind) { - case AST_CONTROL_HANGUP: - return "Other end has hungup"; - case AST_CONTROL_RING: - return "Local ring"; - case AST_CONTROL_RINGING: - return "Remote end is ringing"; - case AST_CONTROL_ANSWER: - return "Remote end has answered"; - case AST_CONTROL_BUSY: - return "Remote end is busy"; - case AST_CONTROL_TAKEOFFHOOK: - return "Make it go off hook"; - case AST_CONTROL_OFFHOOK: - return "Line is off hook"; - case AST_CONTROL_CONGESTION: - return "Congestion (circuits busy)"; - case AST_CONTROL_FLASH: - return "Flash hook"; - case AST_CONTROL_WINK: - return "Wink"; - case AST_CONTROL_OPTION: - return "Set a low-level option"; - case AST_CONTROL_RADIO_KEY: - return "Key Radio"; - case AST_CONTROL_RADIO_UNKEY: - return "Un-Key Radio"; - } - return "UNKNOWN"; -} - -static int mgcp_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen) -{ - struct mgcp_subchannel *sub = ast->tech_pvt; - int res = 0; - - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP asked to indicate %d '%s' condition on channel %s\n", - ind, control2str(ind), ast->name); - } - ast_mutex_lock(&sub->lock); - switch(ind) { - case AST_CONTROL_RINGING: -#ifdef DLINK_BUGGY_FIRMWARE - transmit_notify_request(sub, "rt"); -#else - transmit_notify_request(sub, "G/rt"); -#endif - break; - case AST_CONTROL_BUSY: - transmit_notify_request(sub, "L/bz"); - break; - case AST_CONTROL_CONGESTION: - transmit_notify_request(sub, "G/cg"); - break; - case AST_CONTROL_HOLD: - ast_moh_start(ast, data, NULL); - break; - case AST_CONTROL_UNHOLD: - ast_moh_stop(ast); - break; - case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(sub->rtp); - break; - case -1: - transmit_notify_request(sub, ""); - break; - default: - ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind); - res = -1; - } - ast_mutex_unlock(&sub->lock); - return res; -} - -static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state) -{ - struct ast_channel *tmp; - struct mgcp_endpoint *i = sub->parent; - int fmt; - - tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id); - if (tmp) { - tmp->tech = &mgcp_tech; - tmp->nativeformats = i->capability; - if (!tmp->nativeformats) - tmp->nativeformats = capability; - fmt = ast_best_codec(tmp->nativeformats); - ast_string_field_build(tmp, name, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id); - if (sub->rtp) - tmp->fds[0] = ast_rtp_fd(sub->rtp); - if (i->dtmfmode & (MGCP_DTMF_INBAND | MGCP_DTMF_HYBRID)) { - i->dsp = ast_dsp_new(); - ast_dsp_set_features(i->dsp,DSP_FEATURE_DTMF_DETECT); - /* this is to prevent clipping of dtmf tones during dsp processing */ - ast_dsp_digitmode(i->dsp, DSP_DIGITMODE_NOQUELCH); - } else { - i->dsp = NULL; - } - if (state == AST_STATE_RING) - tmp->rings = 1; - tmp->writeformat = fmt; - tmp->rawwriteformat = fmt; - tmp->readformat = fmt; - tmp->rawreadformat = fmt; - tmp->tech_pvt = sub; - if (!ast_strlen_zero(i->language)) - ast_string_field_set(tmp, language, i->language); - if (!ast_strlen_zero(i->accountcode)) - ast_string_field_set(tmp, accountcode, i->accountcode); - if (i->amaflags) - tmp->amaflags = i->amaflags; - sub->owner = tmp; - ast_module_ref(ast_module_info->self); - tmp->callgroup = i->callgroup; - tmp->pickupgroup = i->pickupgroup; - ast_string_field_set(tmp, call_forward, i->call_forward); - ast_copy_string(tmp->context, i->context, sizeof(tmp->context)); - ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten)); - - /* Don't use ast_set_callerid() here because it will - * generate a needless NewCallerID event */ - tmp->cid.cid_ani = ast_strdup(i->cid_num); - - if (!i->adsi) - tmp->adsicpe = AST_ADSI_UNAVAILABLE; - tmp->priority = 1; - if (sub->rtp) - ast_jb_configure(tmp, &global_jbconf); - if (state != AST_STATE_DOWN) { - if (ast_pbx_start(tmp)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); - ast_hangup(tmp); - tmp = NULL; - } - } - /* verbose level check */ - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_new(%s) created in state: %s\n", - tmp->name, ast_state2str(state)); - } - } else { - ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); - } - return tmp; -} - -static char* get_sdp_by_line(char* line, char *name, int nameLen) -{ - if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=') { - char* r = line + nameLen + 1; - while (*r && (*r < 33)) ++r; - return r; - } - return ""; -} - -static char *get_sdp(struct mgcp_request *req, char *name) -{ - int x; - int len = strlen(name); - char *r; - - for (x=0; x<req->lines; x++) { - r = get_sdp_by_line(req->line[x], name, len); - if (r[0] != '\0') return r; - } - return ""; -} - -static void sdpLineNum_iterator_init(int* iterator) -{ - *iterator = 0; -} - -static char* get_sdp_iterate(int* iterator, struct mgcp_request *req, char *name) -{ - int len = strlen(name); - char *r; - while (*iterator < req->lines) { - r = get_sdp_by_line(req->line[(*iterator)++], name, len); - if (r[0] != '\0') return r; - } - return ""; -} - -static char *__get_header(struct mgcp_request *req, char *name, int *start) -{ - int x; - int len = strlen(name); - char *r; - for (x=*start;x<req->headers;x++) { - if (!strncasecmp(req->header[x], name, len) && - (req->header[x][len] == ':')) { - r = req->header[x] + len + 1; - while(*r && (*r < 33)) - r++; - *start = x+1; - return r; - } - } - /* Don't return NULL, so get_header is always a valid pointer */ - return ""; -} - -static char *get_header(struct mgcp_request *req, char *name) -{ - int start = 0; - return __get_header(req, name, &start); -} - -/*! \brief get_csv: (SC:) get comma separated value */ -static char *get_csv(char *c, int *len, char **next) -{ - char *s; - - *next = NULL, *len = 0; - if (!c) return NULL; - - while (*c && (*c < 33 || *c == ',')) - c++; - - s = c; - while (*c && (*c >= 33 && *c != ',')) - c++, (*len)++; - *next = c; - - if (*len == 0) - s = NULL, *next = NULL; - - return s; -} - -static struct mgcp_subchannel *find_subchannel_and_lock(char *name, int msgid, struct sockaddr_in *sin) -{ - struct mgcp_endpoint *p = NULL; - struct mgcp_subchannel *sub = NULL; - struct mgcp_gateway *g; - char tmp[256] = ""; - char *at = NULL, *c; - int found = 0; - if (name) { - ast_copy_string(tmp, name, sizeof(tmp)); - at = strchr(tmp, '@'); - if (!at) { - ast_log(LOG_NOTICE, "Endpoint '%s' has no at sign!\n", name); - return NULL; - } - *at++ = '\0'; - } - ast_mutex_lock(&gatelock); - if (at && (at[0] == '[')) { - at++; - c = strrchr(at, ']'); - if (c) - *c = '\0'; - } - g = gateways; - while(g) { - if ((!name || !strcasecmp(g->name, at)) && - (sin || g->addr.sin_addr.s_addr || g->defaddr.sin_addr.s_addr)) { - /* Found the gateway. If it's dynamic, save it's address -- now for the endpoint */ - if (sin && g->dynamic && name) { - if ((g->addr.sin_addr.s_addr != sin->sin_addr.s_addr) || - (g->addr.sin_port != sin->sin_port)) { - memcpy(&g->addr, sin, sizeof(g->addr)); - if (ast_ouraddrfor(&g->addr.sin_addr, &g->ourip)) - memcpy(&g->ourip, &__ourip, sizeof(g->ourip)); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Registered MGCP gateway '%s' at %s port %d\n", g->name, ast_inet_ntoa(g->addr.sin_addr), ntohs(g->addr.sin_port)); - } - } - /* not dynamic, check if the name matches */ - else if (name) { - if (strcasecmp(g->name, at)) { - g = g->next; - continue; - } - } - /* not dynamic, no name, check if the addr matches */ - else if (!name && sin) { - if ((g->addr.sin_addr.s_addr != sin->sin_addr.s_addr) || - (g->addr.sin_port != sin->sin_port)) { - g = g->next; - continue; - } - } else { - g = g->next; - continue; - } - /* SC */ - p = g->endpoints; - while(p) { - if (option_debug) - ast_log(LOG_DEBUG, "Searching on %s@%s for subchannel\n", - p->name, g->name); - if (msgid) { -#if 0 /* new transport mech */ - sub = p->sub; - do { - if (option_debug) - ast_log(LOG_DEBUG, "Searching on %s@%s-%d for subchannel with lastout: %d\n", - p->name, g->name, sub->id, msgid); - if (sub->lastout == msgid) { - if (option_debug) - ast_log(LOG_DEBUG, "Found subchannel sub%d to handle request %d sub->lastout: %d\n", - sub->id, msgid, sub->lastout); - found = 1; - break; - } - sub = sub->next; - } while (sub != p->sub); - if (found) { - break; - } -#endif - /* SC */ - sub = p->sub; - found = 1; - /* SC */ - break; - } else if (name && !strcasecmp(p->name, tmp)) { - ast_log(LOG_DEBUG, "Coundn't determine subchannel, assuming current master %s@%s-%d\n", - p->name, g->name, p->sub->id); - sub = p->sub; - found = 1; - break; - } - p = p->next; - } - if (sub && found) { - ast_mutex_lock(&sub->lock); - break; - } - } - g = g->next; - } - ast_mutex_unlock(&gatelock); - if (!sub) { - if (name) { - if (g) - ast_log(LOG_NOTICE, "Endpoint '%s' not found on gateway '%s'\n", tmp, at); - else - ast_log(LOG_NOTICE, "Gateway '%s' (and thus its endpoint '%s') does not exist\n", at, tmp); - } - } - return sub; -} - -static void parse(struct mgcp_request *req) -{ - /* Divide fields by NULL's */ - char *c; - int f = 0; - c = req->data; - - /* First header starts immediately */ - req->header[f] = c; - while(*c) { - if (*c == '\n') { - /* We've got a new header */ - *c = 0; -#if 0 - printf("Header: %s (%d)\n", req->header[f], strlen(req->header[f])); -#endif - if (ast_strlen_zero(req->header[f])) { - /* Line by itself means we're now in content */ - c++; - break; - } - if (f >= MGCP_MAX_HEADERS - 1) { - ast_log(LOG_WARNING, "Too many MGCP headers...\n"); - } else - f++; - req->header[f] = c + 1; - } else if (*c == '\r') { - /* Ignore but eliminate \r's */ - *c = 0; - } - c++; - } - /* Check for last header */ - if (!ast_strlen_zero(req->header[f])) - f++; - req->headers = f; - /* Now we process any mime content */ - f = 0; - req->line[f] = c; - while(*c) { - if (*c == '\n') { - /* We've got a new line */ - *c = 0; -#if 0 - printf("Line: %s (%d)\n", req->line[f], strlen(req->line[f])); -#endif - if (f >= MGCP_MAX_LINES - 1) { - ast_log(LOG_WARNING, "Too many SDP lines...\n"); - } else - f++; - req->line[f] = c + 1; - } else if (*c == '\r') { - /* Ignore and eliminate \r's */ - *c = 0; - } - c++; - } - /* Check for last line */ - if (!ast_strlen_zero(req->line[f])) - f++; - req->lines = f; - /* Parse up the initial header */ - c = req->header[0]; - while(*c && *c < 33) c++; - /* First the verb */ - req->verb = c; - while(*c && (*c > 32)) c++; - if (*c) { - *c = '\0'; - c++; - while(*c && (*c < 33)) c++; - req->identifier = c; - while(*c && (*c > 32)) c++; - if (*c) { - *c = '\0'; - c++; - while(*c && (*c < 33)) c++; - req->endpoint = c; - while(*c && (*c > 32)) c++; - if (*c) { - *c = '\0'; - c++; - while(*c && (*c < 33)) c++; - req->version = c; - while(*c && (*c > 32)) c++; - while(*c && (*c < 33)) c++; - while(*c && (*c > 32)) c++; - *c = '\0'; - } - } - } - - if (mgcpdebug) { - ast_verbose("Verb: '%s', Identifier: '%s', Endpoint: '%s', Version: '%s'\n", - req->verb, req->identifier, req->endpoint, req->version); - ast_verbose("%d headers, %d lines\n", req->headers, req->lines); - } - if (*c) - ast_log(LOG_WARNING, "Odd content, extra stuff left over ('%s')\n", c); -} - -static int process_sdp(struct mgcp_subchannel *sub, struct mgcp_request *req) -{ - char *m; - char *c; - char *a; - char host[258]; - int len; - int portno; - int peercapability, peerNonCodecCapability; - struct sockaddr_in sin; - char *codecs; - struct ast_hostent ahp; struct hostent *hp; - int codec, codec_count=0; - int iterator; - struct mgcp_endpoint *p = sub->parent; - - /* Get codec and RTP info from SDP */ - m = get_sdp(req, "m"); - c = get_sdp(req, "c"); - if (ast_strlen_zero(m) || ast_strlen_zero(c)) { - ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c); - return -1; - } - if (sscanf(c, "IN IP4 %256s", host) != 1) { - ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c); - return -1; - } - /* XXX This could block for a long time, and block the main thread! XXX */ - hp = ast_gethostbyname(host, &ahp); - if (!hp) { - ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c); - return -1; - } - if (sscanf(m, "audio %d RTP/AVP %n", &portno, &len) != 1) { - ast_log(LOG_WARNING, "Unable to determine port number for RTP in '%s'\n", m); - return -1; - } - sin.sin_family = AF_INET; - memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); - sin.sin_port = htons(portno); - ast_rtp_set_peer(sub->rtp, &sin); -#if 0 - printf("Peer RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); -#endif - /* Scan through the RTP payload types specified in a "m=" line: */ - ast_rtp_pt_clear(sub->rtp); - codecs = ast_strdupa(m + len); - while (!ast_strlen_zero(codecs)) { - if (sscanf(codecs, "%d%n", &codec, &len) != 1) { - if (codec_count) - break; - ast_log(LOG_WARNING, "Error in codec string '%s' at '%s'\n", m, codecs); - return -1; - } - ast_rtp_set_m_type(sub->rtp, codec); - codec_count++; - codecs += len; - } - - /* Next, scan through each "a=rtpmap:" line, noting each */ - /* specified RTP payload type (with corresponding MIME subtype): */ - sdpLineNum_iterator_init(&iterator); - while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') { - char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */ - if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) - continue; - /* Note: should really look at the 'freq' and '#chans' params too */ - ast_rtp_set_rtpmap_type(sub->rtp, codec, "audio", mimeSubtype, 0); - } - - /* Now gather all of the codecs that were asked for: */ - ast_rtp_get_current_formats(sub->rtp, &peercapability, &peerNonCodecCapability); - p->capability = capability & peercapability; - if (mgcpdebug) { - ast_verbose("Capabilities: us - %d, them - %d, combined - %d\n", - capability, peercapability, p->capability); - ast_verbose("Non-codec capabilities: us - %d, them - %d, combined - %d\n", - nonCodecCapability, peerNonCodecCapability, p->nonCodecCapability); - } - if (!p->capability) { - ast_log(LOG_WARNING, "No compatible codecs!\n"); - return -1; - } - return 0; -} - -static int add_header(struct mgcp_request *req, char *var, char *value) -{ - if (req->len >= sizeof(req->data) - 4) { - ast_log(LOG_WARNING, "Out of space, can't add anymore\n"); - return -1; - } - if (req->lines) { - ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n"); - return -1; - } - req->header[req->headers] = req->data + req->len; - snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s: %s\r\n", var, value); - req->len += strlen(req->header[req->headers]); - if (req->headers < MGCP_MAX_HEADERS) - req->headers++; - else { - ast_log(LOG_WARNING, "Out of header space\n"); - return -1; - } - return 0; -} - -static int add_line(struct mgcp_request *req, char *line) -{ - if (req->len >= sizeof(req->data) - 4) { - ast_log(LOG_WARNING, "Out of space, can't add anymore\n"); - return -1; - } - if (!req->lines) { - /* Add extra empty return */ - snprintf(req->data + req->len, sizeof(req->data) - req->len, "\r\n"); - req->len += strlen(req->data + req->len); - } - req->line[req->lines] = req->data + req->len; - snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line); - req->len += strlen(req->line[req->lines]); - if (req->lines < MGCP_MAX_LINES) - req->lines++; - else { - ast_log(LOG_WARNING, "Out of line space\n"); - return -1; - } - return 0; -} - -static int init_resp(struct mgcp_request *req, char *resp, struct mgcp_request *orig, char *resprest) -{ - /* Initialize a response */ - if (req->headers || req->len) { - ast_log(LOG_WARNING, "Request already initialized?!?\n"); - return -1; - } - req->header[req->headers] = req->data + req->len; - snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %s %s\r\n", resp, orig->identifier, resprest); - req->len += strlen(req->header[req->headers]); - if (req->headers < MGCP_MAX_HEADERS) - req->headers++; - else - ast_log(LOG_WARNING, "Out of header space\n"); - return 0; -} - -static int init_req(struct mgcp_endpoint *p, struct mgcp_request *req, char *verb) -{ - /* Initialize a response */ - if (req->headers || req->len) { - ast_log(LOG_WARNING, "Request already initialized?!?\n"); - return -1; - } - req->header[req->headers] = req->data + req->len; - /* check if we need brackets around the gw name */ - if (p->parent->isnamedottedip) - snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@[%s] MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name); - else - snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@%s MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name); - req->len += strlen(req->header[req->headers]); - if (req->headers < MGCP_MAX_HEADERS) - req->headers++; - else - ast_log(LOG_WARNING, "Out of header space\n"); - return 0; -} - - -static int respprep(struct mgcp_request *resp, struct mgcp_endpoint *p, char *msg, struct mgcp_request *req, char *msgrest) -{ - memset(resp, 0, sizeof(*resp)); - init_resp(resp, msg, req, msgrest); - return 0; -} - -static int reqprep(struct mgcp_request *req, struct mgcp_endpoint *p, char *verb) -{ - memset(req, 0, sizeof(struct mgcp_request)); - oseq++; - if (oseq > 999999999) - oseq = 1; - init_req(p, req, verb); - return 0; -} - -static int transmit_response(struct mgcp_subchannel *sub, char *msg, struct mgcp_request *req, char *msgrest) -{ - struct mgcp_request resp; - struct mgcp_endpoint *p = sub->parent; - struct mgcp_response *mgr; - - respprep(&resp, p, msg, req, msgrest); - mgr = malloc(sizeof(struct mgcp_response) + resp.len + 1); - if (mgr) { - /* Store MGCP response in case we have to retransmit */ - memset(mgr, 0, sizeof(struct mgcp_response)); - sscanf(req->identifier, "%d", &mgr->seqno); - time(&mgr->whensent); - mgr->len = resp.len; - memcpy(mgr->buf, resp.data, resp.len); - mgr->buf[resp.len] = '\0'; - mgr->next = p->parent->responses; - p->parent->responses = mgr; - } - return send_response(sub, &resp); -} - - -static int add_sdp(struct mgcp_request *resp, struct mgcp_subchannel *sub, struct ast_rtp *rtp) -{ - int len; - int codec; - char costr[80]; - struct sockaddr_in sin; - char v[256]; - char s[256]; - char o[256]; - char c[256]; - char t[256]; - char m[256] = ""; - char a[1024] = ""; - int x; - struct sockaddr_in dest; - struct mgcp_endpoint *p = sub->parent; - /* XXX We break with the "recommendation" and send our IP, in order that our - peer doesn't have to ast_gethostbyname() us XXX */ - len = 0; - if (!sub->rtp) { - ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n"); - return -1; - } - ast_rtp_get_us(sub->rtp, &sin); - if (rtp) { - ast_rtp_get_peer(rtp, &dest); - } else { - if (sub->tmpdest.sin_addr.s_addr) { - dest.sin_addr = sub->tmpdest.sin_addr; - dest.sin_port = sub->tmpdest.sin_port; - /* Reset temporary destination */ - memset(&sub->tmpdest, 0, sizeof(sub->tmpdest)); - } else { - dest.sin_addr = p->parent->ourip; - dest.sin_port = sin.sin_port; - } - } - if (mgcpdebug) { - ast_verbose("We're at %s port %d\n", ast_inet_ntoa(p->parent->ourip), ntohs(sin.sin_port)); - } - snprintf(v, sizeof(v), "v=0\r\n"); - snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", (int)getpid(), (int)getpid(), ast_inet_ntoa(dest.sin_addr)); - snprintf(s, sizeof(s), "s=session\r\n"); - snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr)); - snprintf(t, sizeof(t), "t=0 0\r\n"); - snprintf(m, sizeof(m), "m=audio %d RTP/AVP", ntohs(dest.sin_port)); - for (x = 1; x <= AST_FORMAT_MAX_AUDIO; x <<= 1) { - if (p->capability & x) { - if (mgcpdebug) { - ast_verbose("Answering with capability %d\n", x); - } - codec = ast_rtp_lookup_code(sub->rtp, 1, x); - if (codec > -1) { - snprintf(costr, sizeof(costr), " %d", codec); - strncat(m, costr, sizeof(m) - strlen(m) - 1); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x, 0)); - strncat(a, costr, sizeof(a) - strlen(a) - 1); - } - } - } - for (x = 1; x <= AST_RTP_MAX; x <<= 1) { - if (p->nonCodecCapability & x) { - if (mgcpdebug) { - ast_verbose("Answering with non-codec capability %d\n", x); - } - codec = ast_rtp_lookup_code(sub->rtp, 0, x); - if (codec > -1) { - snprintf(costr, sizeof(costr), " %d", codec); - strncat(m, costr, sizeof(m) - strlen(m) - 1); - snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(0, x, 0)); - strncat(a, costr, sizeof(a) - strlen(a) - 1); - if (x == AST_RTP_DTMF) { - /* Indicate we support DTMF... Not sure about 16, - but MSN supports it so dang it, we will too... */ - snprintf(costr, sizeof costr, "a=fmtp:%d 0-16\r\n", codec); - strncat(a, costr, sizeof(a) - strlen(a) - 1); - } - } - } - } - strncat(m, "\r\n", sizeof(m) - strlen(m) - 1); - len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m) + strlen(a); - snprintf(costr, sizeof(costr), "%d", len); - add_line(resp, v); - add_line(resp, o); - add_line(resp, s); - add_line(resp, c); - add_line(resp, t); - add_line(resp, m); - add_line(resp, a); - return 0; -} - -static int transmit_modify_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp, int codecs) -{ - struct mgcp_request resp; - char local[256]; - char tmp[80]; - int x; - int capability; - struct mgcp_endpoint *p = sub->parent; - - capability = p->capability; - if (codecs) - capability = codecs; - if (ast_strlen_zero(sub->cxident) && rtp) { - /* We don't have a CXident yet, store the destination and - wait a bit */ - ast_rtp_get_peer(rtp, &sub->tmpdest); - return 0; - } - snprintf(local, sizeof(local), "p:20"); - for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) { - if (p->capability & x) { - snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0)); - strncat(local, tmp, sizeof(local) - strlen(local) - 1); - } - } - reqprep(&resp, p, "MDCX"); - add_header(&resp, "C", sub->callid); - add_header(&resp, "L", local); - add_header(&resp, "M", mgcp_cxmodes[sub->cxmode]); - /* X header should not be sent. kept for compatibility */ - add_header(&resp, "X", sub->txident); - add_header(&resp, "I", sub->cxident); - /*add_header(&resp, "S", "");*/ - add_sdp(&resp, sub, rtp); - /* fill in new fields */ - resp.cmd = MGCP_CMD_MDCX; - resp.trid = oseq; - return send_request(p, sub, &resp, oseq); /* SC */ -} - -static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp) -{ - struct mgcp_request resp; - char local[256]; - char tmp[80]; - int x; - struct mgcp_endpoint *p = sub->parent; - - snprintf(local, sizeof(local), "p:20"); - for (x=1;x<= AST_FORMAT_MAX_AUDIO; x <<= 1) { - if (p->capability & x) { - snprintf(tmp, sizeof(tmp), ", a:%s", ast_rtp_lookup_mime_subtype(1, x, 0)); - strncat(local, tmp, sizeof(local) - strlen(local) - 1); - } - } - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "Creating connection for %s@%s-%d in cxmode: %s callid: %s\n", - p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode], sub->callid); - } - reqprep(&resp, p, "CRCX"); - add_header(&resp, "C", sub->callid); - add_header(&resp, "L", local); - add_header(&resp, "M", mgcp_cxmodes[sub->cxmode]); - /* X header should not be sent. kept for compatibility */ - add_header(&resp, "X", sub->txident); - /*add_header(&resp, "S", "");*/ - add_sdp(&resp, sub, rtp); - /* fill in new fields */ - resp.cmd = MGCP_CMD_CRCX; - resp.trid = oseq; - return send_request(p, sub, &resp, oseq); /* SC */ -} - -static int transmit_notify_request(struct mgcp_subchannel *sub, char *tone) -{ - struct mgcp_request resp; - struct mgcp_endpoint *p = sub->parent; - - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP Asked to indicate tone: %s on %s@%s-%d in cxmode: %s\n", - tone, p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode]); - } - ast_copy_string(p->curtone, tone, sizeof(p->curtone)); - reqprep(&resp, p, "RQNT"); - add_header(&resp, "X", p->rqnt_ident); /* SC */ - switch (p->hookstate) { - case MGCP_ONHOOK: - add_header(&resp, "R", "L/hd(N)"); - break; - case MGCP_OFFHOOK: - add_header_offhook(sub, &resp); - break; - } - if (!ast_strlen_zero(tone)) { - add_header(&resp, "S", tone); - } - /* fill in new fields */ - resp.cmd = MGCP_CMD_RQNT; - resp.trid = oseq; - return send_request(p, NULL, &resp, oseq); /* SC */ -} - -static int transmit_notify_request_with_callerid(struct mgcp_subchannel *sub, char *tone, char *callernum, char *callername) -{ - struct mgcp_request resp; - char tone2[256]; - char *l, *n; - time_t t; - struct tm tm; - struct mgcp_endpoint *p = sub->parent; - - time(&t); - ast_localtime(&t, &tm, NULL); - n = callername; - l = callernum; - if (!n) - n = ""; - if (!l) - l = ""; - - /* Keep track of last callerid for blacklist and callreturn */ - ast_copy_string(p->lastcallerid, l, sizeof(p->lastcallerid)); - - snprintf(tone2, sizeof(tone2), "%s,L/ci(%02d/%02d/%02d/%02d,%s,%s)", tone, - tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, l, n); - ast_copy_string(p->curtone, tone, sizeof(p->curtone)); - reqprep(&resp, p, "RQNT"); - add_header(&resp, "X", p->rqnt_ident); /* SC */ - switch (p->hookstate) { - case MGCP_ONHOOK: - add_header(&resp, "R", "L/hd(N)"); - break; - case MGCP_OFFHOOK: - add_header_offhook(sub, &resp); - break; - } - if (!ast_strlen_zero(tone2)) { - add_header(&resp, "S", tone2); - } - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP Asked to indicate tone: %s on %s@%s-%d in cxmode: %s\n", - tone2, p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode]); - } - /* fill in new fields */ - resp.cmd = MGCP_CMD_RQNT; - resp.trid = oseq; - return send_request(p, NULL, &resp, oseq); /* SC */ -} - -static int transmit_modify_request(struct mgcp_subchannel *sub) -{ - struct mgcp_request resp; - struct mgcp_endpoint *p = sub->parent; - - if (ast_strlen_zero(sub->cxident)) { - /* We don't have a CXident yet, store the destination and - wait a bit */ - return 0; - } - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "Modified %s@%s-%d with new mode: %s on callid: %s\n", - p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode], sub->callid); - } - reqprep(&resp, p, "MDCX"); - add_header(&resp, "C", sub->callid); - add_header(&resp, "M", mgcp_cxmodes[sub->cxmode]); - /* X header should not be sent. kept for compatibility */ - add_header(&resp, "X", sub->txident); - add_header(&resp, "I", sub->cxident); - switch (sub->parent->hookstate) { - case MGCP_ONHOOK: - add_header(&resp, "R", "L/hd(N)"); - break; - case MGCP_OFFHOOK: - add_header_offhook(sub, &resp); - break; - } - /* fill in new fields */ - resp.cmd = MGCP_CMD_MDCX; - resp.trid = oseq; - return send_request(p, sub, &resp, oseq); /* SC */ -} - - -static void add_header_offhook(struct mgcp_subchannel *sub, struct mgcp_request *resp) -{ - struct mgcp_endpoint *p = sub->parent; - - if (p && p->sub && p->sub->owner && p->sub->owner->_state >= AST_STATE_RINGING && (p->dtmfmode & (MGCP_DTMF_INBAND | MGCP_DTMF_HYBRID))) - add_header(resp, "R", "L/hu(N),L/hf(N)"); - else - add_header(resp, "R", "L/hu(N),L/hf(N),D/[0-9#*](N)"); -} - -static int transmit_audit_endpoint(struct mgcp_endpoint *p) -{ - struct mgcp_request resp; - reqprep(&resp, p, "AUEP"); - /* removed unknown param VS */ - /*add_header(&resp, "F", "A,R,D,S,X,N,I,T,O,ES,E,MD,M");*/ - add_header(&resp, "F", "A"); - /* fill in new fields */ - resp.cmd = MGCP_CMD_AUEP; - resp.trid = oseq; - return send_request(p, NULL, &resp, oseq); /* SC */ -} - -static int transmit_connection_del(struct mgcp_subchannel *sub) -{ - struct mgcp_endpoint *p = sub->parent; - struct mgcp_request resp; - - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "Delete connection %s %s@%s-%d with new mode: %s on callid: %s\n", - sub->cxident, p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode], sub->callid); - } - reqprep(&resp, p, "DLCX"); - /* check if call id is avail */ - if (sub->callid[0]) - add_header(&resp, "C", sub->callid); - /* X header should not be sent. kept for compatibility */ - add_header(&resp, "X", sub->txident); - /* check if cxident is avail */ - if (sub->cxident[0]) - add_header(&resp, "I", sub->cxident); - /* fill in new fields */ - resp.cmd = MGCP_CMD_DLCX; - resp.trid = oseq; - return send_request(p, sub, &resp, oseq); /* SC */ -} - -static int transmit_connection_del_w_params(struct mgcp_endpoint *p, char *callid, char *cxident) -{ - struct mgcp_request resp; - - if (mgcpdebug) { - ast_verbose(VERBOSE_PREFIX_3 "Delete connection %s %s@%s on callid: %s\n", - cxident ? cxident : "", p->name, p->parent->name, callid ? callid : ""); - } - reqprep(&resp, p, "DLCX"); - /* check if call id is avail */ - if (callid && *callid) - add_header(&resp, "C", callid); - /* check if cxident is avail */ - if (cxident && *cxident) - add_header(&resp, "I", cxident); - /* fill in new fields */ - resp.cmd = MGCP_CMD_DLCX; - resp.trid = oseq; - return send_request(p, p->sub, &resp, oseq); -} - -/*! \brief dump_cmd_queues: (SC:) cleanup pending commands */ -static void dump_cmd_queues(struct mgcp_endpoint *p, struct mgcp_subchannel *sub) -{ - struct mgcp_request *t, *q; - - if (p) { - ast_mutex_lock(&p->rqnt_queue_lock); - for (q = p->rqnt_queue; q; t = q->next, free(q), q=t); - p->rqnt_queue = NULL; - ast_mutex_unlock(&p->rqnt_queue_lock); - - ast_mutex_lock(&p->cmd_queue_lock); - for (q = p->cmd_queue; q; t = q->next, free(q), q=t); - p->cmd_queue = NULL; - ast_mutex_unlock(&p->cmd_queue_lock); - - ast_mutex_lock(&p->sub->cx_queue_lock); - for (q = p->sub->cx_queue; q; t = q->next, free(q), q=t); - p->sub->cx_queue = NULL; - ast_mutex_unlock(&p->sub->cx_queue_lock); - - ast_mutex_lock(&p->sub->next->cx_queue_lock); - for (q = p->sub->next->cx_queue; q; t = q->next, free(q), q=t); - p->sub->next->cx_queue = NULL; - ast_mutex_unlock(&p->sub->next->cx_queue_lock); - } else if (sub) { - ast_mutex_lock(&sub->cx_queue_lock); - for (q = sub->cx_queue; q; t = q->next, free(q), q=t); - sub->cx_queue = NULL; - ast_mutex_unlock(&sub->cx_queue_lock); - } -} - - -/*! \brief find_command: (SC:) remove command transaction from queue */ -static struct mgcp_request *find_command(struct mgcp_endpoint *p, struct mgcp_subchannel *sub, - struct mgcp_request **queue, ast_mutex_t *l, int ident) -{ - struct mgcp_request *prev, *req; - - ast_mutex_lock(l); - for (prev = NULL, req = *queue; req; prev = req, req = req->next) { - if (req->trid == ident) { - /* remove from queue */ - if (!prev) - *queue = req->next; - else - prev->next = req->next; - - /* send next pending command */ - if (*queue) { - if (mgcpdebug) { - ast_verbose("Posting Queued Request:\n%s to %s:%d\n", (*queue)->data, - ast_inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port)); - } - - mgcp_postrequest(p, sub, (*queue)->data, (*queue)->len, (*queue)->trid); - } - break; - } - } - ast_mutex_unlock(l); - return req; -} - -/* modified for new transport mechanism */ -static void handle_response(struct mgcp_endpoint *p, struct mgcp_subchannel *sub, - int result, unsigned int ident, struct mgcp_request *resp) -{ - char *c; - struct mgcp_request *req; - struct mgcp_gateway *gw = p->parent; - - if (result < 200) { - /* provisional response */ - return; - } - - if (p->slowsequence) - req = find_command(p, sub, &p->cmd_queue, &p->cmd_queue_lock, ident); - else if (sub) - req = find_command(p, sub, &sub->cx_queue, &sub->cx_queue_lock, ident); - else if (!(req = find_command(p, sub, &p->rqnt_queue, &p->rqnt_queue_lock, ident))) - req = find_command(p, sub, &p->cmd_queue, &p->cmd_queue_lock, ident); - - if (!req) { - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "No command found on [%s] for transaction %d. Ignoring...\n", - gw->name, ident); - } - return; - } - - if (p && (result >= 400) && (result <= 599)) { - switch (result) { - case 401: - p->hookstate = MGCP_OFFHOOK; - break; - case 402: - p->hookstate = MGCP_ONHOOK; - break; - case 406: - ast_log(LOG_NOTICE, "Transaction %d timed out\n", ident); - break; - case 407: - ast_log(LOG_NOTICE, "Transaction %d aborted\n", ident); - break; - } - if (sub) { - if (sub->owner) { - ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s-%d\n", - result, p->name, p->parent->name, sub ? sub->id:-1); - mgcp_queue_hangup(sub); - } - } else { - if (p->sub->next->owner) { - ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s-%d\n", - result, p->name, p->parent->name, sub ? sub->id:-1); - mgcp_queue_hangup(p->sub); - } - - if (p->sub->owner) { - ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s-%d\n", - result, p->name, p->parent->name, sub ? sub->id:-1); - mgcp_queue_hangup(p->sub); - } - - dump_cmd_queues(p, NULL); - } - } - - if (resp) { - if (req->cmd == MGCP_CMD_CRCX) { - if ((c = get_header(resp, "I"))) { - if (!ast_strlen_zero(c) && sub) { - /* if we are hanging up do not process this conn. */ - if (sub->owner) { - if (!ast_strlen_zero(sub->cxident)) { - if (strcasecmp(c, sub->cxident)) { - ast_log(LOG_WARNING, "Subchannel already has a cxident. sub->cxident: %s requested %s\n", sub->cxident, c); - } - } - ast_copy_string(sub->cxident, c, sizeof(sub->cxident)); - if (sub->tmpdest.sin_addr.s_addr) { - transmit_modify_with_sdp(sub, NULL, 0); - } - } else { - /* XXX delete this one - callid and conn id may already be lost. - so the following del conn may have a side effect of - cleaning up the next subchannel */ - transmit_connection_del(sub); - } - } - } - } - - if (req->cmd == MGCP_CMD_AUEP) { - /* check stale connection ids */ - if ((c = get_header(resp, "I"))) { - char *v, *n; - int len; - while ((v = get_csv(c, &len, &n))) { - if (len) { - if (strncasecmp(v, p->sub->cxident, len) && - strncasecmp(v, p->sub->next->cxident, len)) { - /* connection id not found. delete it */ - char cxident[80] = ""; - - if (len > (sizeof(cxident) - 1)) - len = sizeof(cxident) - 1; - ast_copy_string(cxident, v, len); - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Non existing connection id %s on %s@%s \n", - cxident, p->name, gw->name); - } - transmit_connection_del_w_params(p, NULL, cxident); - } - } - c = n; - } - } - - /* Try to determine the hookstate returned from an audit endpoint command */ - if ((c = get_header(resp, "ES"))) { - if (!ast_strlen_zero(c)) { - if (strstr(c, "hu")) { - if (p->hookstate != MGCP_ONHOOK) { - /* XXX cleanup if we think we are offhook XXX */ - if ((p->sub->owner || p->sub->next->owner ) && - p->hookstate == MGCP_OFFHOOK) - mgcp_queue_hangup(sub); - p->hookstate = MGCP_ONHOOK; - - /* update the requested events according to the new hookstate */ - transmit_notify_request(p->sub, ""); - - /* verbose level check */ - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Setting hookstate of %s@%s to ONHOOK\n", p->name, gw->name); - } - } - } else if (strstr(c, "hd")) { - if (p->hookstate != MGCP_OFFHOOK) { - p->hookstate = MGCP_OFFHOOK; - - /* update the requested events according to the new hookstate */ - transmit_notify_request(p->sub, ""); - - /* verbose level check */ - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Setting hookstate of %s@%s to OFFHOOK\n", p->name, gw->name); - } - } - } - } - } - } - - if (resp && resp->lines) { - /* do not process sdp if we are hanging up. this may be a late response */ - if (sub && sub->owner) { - if (!sub->rtp) - start_rtp(sub); - if (sub->rtp) - process_sdp(sub, resp); - } - } - } - - free(req); -} - -static void start_rtp(struct mgcp_subchannel *sub) -{ - ast_mutex_lock(&sub->lock); - /* check again to be on the safe side */ - if (sub->rtp) { - ast_rtp_destroy(sub->rtp); - sub->rtp = NULL; - } - /* Allocate the RTP now */ - sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - if (sub->rtp && sub->owner) - sub->owner->fds[0] = ast_rtp_fd(sub->rtp); - if (sub->rtp) - ast_rtp_setnat(sub->rtp, sub->nat); -#if 0 - ast_rtp_set_callback(p->rtp, rtpready); - ast_rtp_set_data(p->rtp, p); -#endif - /* Make a call*ID */ - snprintf(sub->callid, sizeof(sub->callid), "%08lx%s", ast_random(), sub->txident); - /* Transmit the connection create */ - transmit_connect_with_sdp(sub, NULL); - ast_mutex_unlock(&sub->lock); -} - -static void *mgcp_ss(void *data) -{ - struct ast_channel *chan = data; - struct mgcp_subchannel *sub = chan->tech_pvt; - struct mgcp_endpoint *p = sub->parent; - /* char exten[AST_MAX_EXTENSION] = ""; */ - int len = 0; - int timeout = firstdigittimeout; - int res= 0; - int getforward = 0; - int loop_pause = 100; - - len = strlen(p->dtmf_buf); - - while(len < AST_MAX_EXTENSION-1) { - res = 1; /* Assume that we will get a digit */ - while (strlen(p->dtmf_buf) == len){ - ast_safe_sleep(chan, loop_pause); - timeout -= loop_pause; - if (timeout <= 0){ - res = 0; - break; - } - res = 1; - } - - timeout = 0; - len = strlen(p->dtmf_buf); - - if (!ast_ignore_pattern(chan->context, p->dtmf_buf)) { - /*res = tone_zone_play_tone(p->subs[index].zfd, -1);*/ - ast_indicate(chan, -1); - } else { - /* XXX Redundant? We should already be playing dialtone */ - /*tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALTONE);*/ - transmit_notify_request(sub, "L/dl"); - } - if (ast_exists_extension(chan, chan->context, p->dtmf_buf, 1, p->cid_num)) { - if (!res || !ast_matchmore_extension(chan, chan->context, p->dtmf_buf, 1, p->cid_num)) { - if (getforward) { - /* Record this as the forwarding extension */ - ast_copy_string(p->call_forward, p->dtmf_buf, sizeof(p->call_forward)); - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Setting call forward to '%s' on channel %s\n", - p->call_forward, chan->name); - } - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALRECALL);*/ - transmit_notify_request(sub, "L/sl"); - if (res) - break; - usleep(500000); - /*res = tone_zone_play_tone(p->subs[index].zfd, -1);*/ - ast_indicate(chan, -1); - sleep(1); - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALTONE);*/ - transmit_notify_request(sub, "L/dl"); - len = 0; - getforward = 0; - } else { - /*res = tone_zone_play_tone(p->subs[index].zfd, -1);*/ - ast_indicate(chan, -1); - ast_copy_string(chan->exten, p->dtmf_buf, sizeof(chan->exten)); - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - ast_set_callerid(chan, - p->hidecallerid ? "" : p->cid_num, - p->hidecallerid ? "" : p->cid_name, - chan->cid.cid_ani ? NULL : p->cid_num); - ast_setstate(chan, AST_STATE_RING); - /*zt_enable_ec(p);*/ - if (p->dtmfmode & MGCP_DTMF_HYBRID) { - p->dtmfmode |= MGCP_DTMF_INBAND; - ast_indicate(chan, -1); - } - res = ast_pbx_run(chan); - if (res) { - ast_log(LOG_WARNING, "PBX exited non-zero\n"); - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_CONGESTION);*/ - /*transmit_notify_request(p, "nbz", 1);*/ - transmit_notify_request(sub, "G/cg"); - } - return NULL; - } - } else { - /* It's a match, but they just typed a digit, and there is an ambiguous match, - so just set the timeout to matchdigittimeout and wait some more */ - timeout = matchdigittimeout; - } - } else if (res == 0) { - ast_log(LOG_DEBUG, "not enough digits (and no ambiguous match)...\n"); - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_CONGESTION);*/ - transmit_notify_request(sub, "G/cg"); - /*zt_wait_event(p->subs[index].zfd);*/ - ast_hangup(chan); - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - return NULL; - } else if (p->hascallwaiting && p->callwaiting && !strcmp(p->dtmf_buf, "*70")) { - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Disabling call waiting on %s\n", chan->name); - } - /* Disable call waiting if enabled */ - p->callwaiting = 0; - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALRECALL);*/ - transmit_notify_request(sub, "L/sl"); - len = 0; - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - timeout = firstdigittimeout; - } else if (!strcmp(p->dtmf_buf,ast_pickup_ext())) { - /* Scan all channels and see if any there - * ringing channqels with that have call groups - * that equal this channels pickup group - */ - if (ast_pickup_call(chan)) { - ast_log(LOG_WARNING, "No call pickup possible...\n"); - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_CONGESTION);*/ - transmit_notify_request(sub, "G/cg"); - } - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - ast_hangup(chan); - return NULL; - } else if (!p->hidecallerid && !strcmp(p->dtmf_buf, "*67")) { - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Disabling Caller*ID on %s\n", chan->name); - } - /* Disable Caller*ID if enabled */ - p->hidecallerid = 1; - ast_set_callerid(chan, "", "", NULL); - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALRECALL);*/ - transmit_notify_request(sub, "L/sl"); - len = 0; - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - timeout = firstdigittimeout; - } else if (p->callreturn && !strcmp(p->dtmf_buf, "*69")) { - res = 0; - if (!ast_strlen_zero(p->lastcallerid)) { - res = ast_say_digit_str(chan, p->lastcallerid, "", chan->language); - } - if (!res) - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALRECALL);*/ - transmit_notify_request(sub, "L/sl"); - break; - } else if (!strcmp(p->dtmf_buf, "*78")) { - /* Do not disturb */ - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Enabled DND on channel %s\n", chan->name); - } - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALRECALL);*/ - transmit_notify_request(sub, "L/sl"); - p->dnd = 1; - getforward = 0; - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - len = 0; - } else if (!strcmp(p->dtmf_buf, "*79")) { - /* Do not disturb */ - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Disabled DND on channel %s\n", chan->name); - } - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALRECALL);*/ - transmit_notify_request(sub, "L/sl"); - p->dnd = 0; - getforward = 0; - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - len = 0; - } else if (p->cancallforward && !strcmp(p->dtmf_buf, "*72")) { - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALRECALL);*/ - transmit_notify_request(sub, "L/sl"); - getforward = 1; - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - len = 0; - } else if (p->cancallforward && !strcmp(p->dtmf_buf, "*73")) { - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Cancelling call forwarding on channel %s\n", chan->name); - } - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALRECALL);*/ - transmit_notify_request(sub, "L/sl"); - memset(p->call_forward, 0, sizeof(p->call_forward)); - getforward = 0; - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - len = 0; - } else if (!strcmp(p->dtmf_buf, ast_parking_ext()) && - sub->next->owner && ast_bridged_channel(sub->next->owner)) { - /* This is a three way call, the main call being a real channel, - and we're parking the first call. */ - ast_masq_park_call(ast_bridged_channel(sub->next->owner), chan, 0, NULL); - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Parking call to '%s'\n", chan->name); - } - break; - } else if (!ast_strlen_zero(p->lastcallerid) && !strcmp(p->dtmf_buf, "*60")) { - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Blacklisting number %s\n", p->lastcallerid); - } - res = ast_db_put("blacklist", p->lastcallerid, "1"); - if (!res) { - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALRECALL);*/ - transmit_notify_request(sub, "L/sl"); - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - len = 0; - } - } else if (p->hidecallerid && !strcmp(p->dtmf_buf, "*82")) { - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Enabling Caller*ID on %s\n", chan->name); - } - /* Enable Caller*ID if enabled */ - p->hidecallerid = 0; - ast_set_callerid(chan, p->cid_num, p->cid_name, NULL); - /*res = tone_zone_play_tone(p->subs[index].zfd, DAHDI_TONE_DIALRECALL);*/ - transmit_notify_request(sub, "L/sl"); - len = 0; - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - timeout = firstdigittimeout; - } else if (!ast_canmatch_extension(chan, chan->context, p->dtmf_buf, 1, chan->cid.cid_num) && - ((p->dtmf_buf[0] != '*') || (strlen(p->dtmf_buf) > 2))) { - if (option_debug) - ast_log(LOG_DEBUG, "Can't match %s from '%s' in context %s\n", p->dtmf_buf, chan->cid.cid_num ? chan->cid.cid_num : "<Unknown Caller>", chan->context); - break; - } - if (!timeout) - timeout = gendigittimeout; - if (len && !ast_ignore_pattern(chan->context, p->dtmf_buf)) - /*tone_zone_play_tone(p->subs[index].zfd, -1);*/ - ast_indicate(chan, -1); - } -#if 0 - for (;;) { - res = ast_waitfordigit(chan, to); - if (!res) { - ast_log(LOG_DEBUG, "Timeout...\n"); - break; - } - if (res < 0) { - ast_log(LOG_DEBUG, "Got hangup...\n"); - ast_hangup(chan); - break; - } - exten[pos++] = res; - if (!ast_ignore_pattern(chan->context, exten)) - ast_indicate(chan, -1); - if (ast_matchmore_extension(chan, chan->context, exten, 1, chan->callerid)) { - if (ast_exists_extension(chan, chan->context, exten, 1, chan->callerid)) - to = 3000; - else - to = 8000; - } else - break; - } - if (ast_exists_extension(chan, chan->context, exten, 1, chan->callerid)) { - ast_copy_string(chan->exten, exten, sizeof(chan->exten)1); - if (!p->rtp) { - start_rtp(p); - } - ast_setstate(chan, AST_STATE_RING); - chan->rings = 1; - if (ast_pbx_run(chan)) { - ast_log(LOG_WARNING, "Unable to launch PBX on %s\n", chan->name); - } else { - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - return NULL; - } - } -#endif - ast_hangup(chan); - memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf)); - return NULL; -} - -static int attempt_transfer(struct mgcp_endpoint *p) -{ - /* ************************* - * I hope this works. - * Copied out of chan_zap - * Cross your fingers - * *************************/ - - /* In order to transfer, we need at least one of the channels to - actually be in a call bridge. We can't conference two applications - together (but then, why would we want to?) */ - if (ast_bridged_channel(p->sub->owner)) { - /* The three-way person we're about to transfer to could still be in MOH, so - stop if now if appropriate */ - if (ast_bridged_channel(p->sub->next->owner)) - ast_queue_control(p->sub->next->owner, AST_CONTROL_UNHOLD); - if (p->sub->owner->_state == AST_STATE_RINGING) { - ast_indicate(ast_bridged_channel(p->sub->next->owner), AST_CONTROL_RINGING); - } - if (ast_channel_masquerade(p->sub->next->owner, ast_bridged_channel(p->sub->owner))) { - ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", - ast_bridged_channel(p->sub->owner)->name, p->sub->next->owner->name); - return -1; - } - /* Orphan the channel */ - unalloc_sub(p->sub->next); - } else if (ast_bridged_channel(p->sub->next->owner)) { - if (p->sub->owner->_state == AST_STATE_RINGING) { - ast_indicate(ast_bridged_channel(p->sub->next->owner), AST_CONTROL_RINGING); - } - ast_queue_control(p->sub->next->owner, AST_CONTROL_UNHOLD); - if (ast_channel_masquerade(p->sub->owner, ast_bridged_channel(p->sub->next->owner))) { - ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n", - ast_bridged_channel(p->sub->next->owner)->name, p->sub->owner->name); - return -1; - } - /*swap_subs(p, SUB_THREEWAY, SUB_REAL);*/ - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Swapping %d for %d on %s@%s\n", p->sub->id, p->sub->next->id, p->name, p->parent->name); - } - p->sub = p->sub->next; - unalloc_sub(p->sub->next); - /* Tell the caller not to hangup */ - return 1; - } else { - ast_log(LOG_DEBUG, "Neither %s nor %s are in a bridge, nothing to transfer\n", - p->sub->owner->name, p->sub->next->owner->name); - p->sub->next->owner->_softhangup |= AST_SOFTHANGUP_DEV; - if (p->sub->next->owner) { - p->sub->next->alreadygone = 1; - mgcp_queue_hangup(p->sub->next); - } - } - return 0; -} - -static void handle_hd_hf(struct mgcp_subchannel *sub, char *ev) -{ - struct mgcp_endpoint *p = sub->parent; - struct ast_channel *c; - pthread_t t; - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - /* Off hook / answer */ - if (sub->outgoing) { - /* Answered */ - if (sub->owner) { - if (ast_bridged_channel(sub->owner)) - ast_queue_control(sub->owner, AST_CONTROL_UNHOLD); - sub->cxmode = MGCP_CX_SENDRECV; - if (!sub->rtp) { - start_rtp(sub); - } else { - transmit_modify_request(sub); - } - /*transmit_notify_request(sub, "aw");*/ - transmit_notify_request(sub, ""); - mgcp_queue_control(sub, AST_CONTROL_ANSWER); - } - } else { - /* Start switch */ - /*sub->cxmode = MGCP_CX_SENDRECV;*/ - if (!sub->owner) { - if (!sub->rtp) { - start_rtp(sub); - } else { - transmit_modify_request(sub); - } - if (p->immediate) { - /* The channel is immediately up. Start right away */ -#ifdef DLINK_BUGGY_FIRMWARE - transmit_notify_request(sub, "rt"); -#else - transmit_notify_request(sub, "G/rt"); -#endif - c = mgcp_new(sub, AST_STATE_RING); - if (!c) { - ast_log(LOG_WARNING, "Unable to start PBX on channel %s@%s\n", p->name, p->parent->name); - transmit_notify_request(sub, "G/cg"); - ast_hangup(c); - } - } else { - if (has_voicemail(p)) { - transmit_notify_request(sub, "L/sl"); - } else { - transmit_notify_request(sub, "L/dl"); - } - c = mgcp_new(sub, AST_STATE_DOWN); - if (c) { - if (ast_pthread_create(&t, &attr, mgcp_ss, c)) { - ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno)); - ast_hangup(c); - } - } else { - ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", p->name, p->parent->name); - } - } - } else { - if (p->hookstate == MGCP_OFFHOOK) { - ast_log(LOG_WARNING, "Off hook, but already have owner on %s@%s\n", p->name, p->parent->name); - } else { - ast_log(LOG_WARNING, "On hook, but already have owner on %s@%s\n", p->name, p->parent->name); - ast_log(LOG_WARNING, "If we're onhook why are we here trying to handle a hd or hf?\n"); - } - if (ast_bridged_channel(sub->owner)) - ast_queue_control(sub->owner, AST_CONTROL_UNHOLD); - sub->cxmode = MGCP_CX_SENDRECV; - if (!sub->rtp) { - start_rtp(sub); - } else { - transmit_modify_request(sub); - } - /*transmit_notify_request(sub, "aw");*/ - transmit_notify_request(sub, ""); - /*ast_queue_control(sub->owner, AST_CONTROL_ANSWER);*/ - } - } - pthread_attr_destroy(&attr); -} - -static int handle_request(struct mgcp_subchannel *sub, struct mgcp_request *req, struct sockaddr_in *sin) -{ - char *ev, *s; - struct ast_frame f = { 0, }; - struct mgcp_endpoint *p = sub->parent; - struct mgcp_gateway *g = NULL; - int res; - - if (mgcpdebug) { - ast_verbose("Handling request '%s' on %s@%s\n", req->verb, p->name, p->parent->name); - } - /* Clear out potential response */ - if (!strcasecmp(req->verb, "RSIP")) { - /* Test if this RSIP request is just a keepalive */ - if(!strcasecmp( get_header(req, "RM"), "X-keepalive")) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Received keepalive request from %s@%s\n", p->name, p->parent->name); - transmit_response(sub, "200", req, "OK"); - } else { - dump_queue(p->parent, p); - dump_cmd_queues(p, NULL); - - if (option_verbose > 2 && (strcmp(p->name, p->parent->wcardep) != 0)) { - ast_verbose(VERBOSE_PREFIX_3 "Resetting interface %s@%s\n", p->name, p->parent->name); - } - /* For RSIP on wildcard we reset all endpoints */ - if (!strcmp(p->name, p->parent->wcardep)) { - /* Reset all endpoints */ - struct mgcp_endpoint *tmp_ep; - - g = p->parent; - tmp_ep = g->endpoints; - while (tmp_ep) { - /*if ((strcmp(tmp_ep->name, "*") != 0) && (strcmp(tmp_ep->name, "aaln/" "*") != 0)) {*/ - if (strcmp(tmp_ep->name, g->wcardep) != 0) { - struct mgcp_subchannel *tmp_sub, *first_sub; - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Resetting interface %s@%s\n", tmp_ep->name, p->parent->name); - } - - first_sub = tmp_ep->sub; - tmp_sub = tmp_ep->sub; - while (tmp_sub) { - mgcp_queue_hangup(tmp_sub); - tmp_sub = tmp_sub->next; - if (tmp_sub == first_sub) - break; - } - } - tmp_ep = tmp_ep->next; - } - } else if (sub->owner) { - mgcp_queue_hangup(sub); - } - transmit_response(sub, "200", req, "OK"); - /* We dont send NTFY or AUEP to wildcard ep */ - if (strcmp(p->name, p->parent->wcardep) != 0) { - transmit_notify_request(sub, ""); - /* Audit endpoint. - Idea is to prevent lost lines due to race conditions - */ - transmit_audit_endpoint(p); - } - } - } else if (!strcasecmp(req->verb, "NTFY")) { - /* Acknowledge and be sure we keep looking for the same things */ - transmit_response(sub, "200", req, "OK"); - /* Notified of an event */ - ev = get_header(req, "O"); - s = strchr(ev, '/'); - if (s) ev = s + 1; - ast_log(LOG_DEBUG, "Endpoint '%s@%s-%d' observed '%s'\n", p->name, p->parent->name, sub->id, ev); - /* Keep looking for events unless this was a hangup */ - if (strcasecmp(ev, "hu") && strcasecmp(ev, "hd") && strcasecmp(ev, "ping")) { - transmit_notify_request(sub, p->curtone); - } - if (!strcasecmp(ev, "hd")) { - p->hookstate = MGCP_OFFHOOK; - sub->cxmode = MGCP_CX_SENDRECV; - handle_hd_hf(sub, ev); - } else if (!strcasecmp(ev, "hf")) { - /* We can assume we are offhook if we received a hookflash */ - /* First let's just do call wait and ignore threeway */ - /* We're currently in charge */ - if (p->hookstate != MGCP_OFFHOOK) { - /* Cisco c7940 sends hf even if the phone is onhook */ - /* Thanks to point on IRC for pointing this out */ - return -1; - } - /* do not let * conference two down channels */ - if (sub->owner && sub->owner->_state == AST_STATE_DOWN && !sub->next->owner) - return -1; - - if (p->callwaiting || p->transfer || p->threewaycalling) { - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Swapping %d for %d on %s@%s\n", p->sub->id, p->sub->next->id, p->name, p->parent->name); - } - p->sub = p->sub->next; - - /* transfer control to our next subchannel */ - if (!sub->next->owner) { - /* plave the first call on hold and start up a new call */ - sub->cxmode = MGCP_CX_MUTE; - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP Muting %d on %s@%s\n", sub->id, p->name, p->parent->name); - } - transmit_modify_request(sub); - if (sub->owner && ast_bridged_channel(sub->owner)) - ast_queue_control(sub->owner, AST_CONTROL_HOLD); - sub->next->cxmode = MGCP_CX_RECVONLY; - handle_hd_hf(sub->next, ev); - } else if (sub->owner && sub->next->owner) { - /* We've got two active calls lets decide whether or not to conference or just flip flop */ - if ((!sub->outgoing) && (!sub->next->outgoing)) { - /* We made both calls lets conferenct */ - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP Conferencing %d and %d on %s@%s\n", - sub->id, sub->next->id, p->name, p->parent->name); - } - sub->cxmode = MGCP_CX_CONF; - sub->next->cxmode = MGCP_CX_CONF; - if (ast_bridged_channel(sub->next->owner)) - ast_queue_control(sub->next->owner, AST_CONTROL_UNHOLD); - transmit_modify_request(sub); - transmit_modify_request(sub->next); - } else { - /* Let's flipflop between calls */ - /* XXX Need to check for state up ??? */ - /* XXX Need a way to indicate the current call, or maybe the call that's waiting */ - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "We didn't make one of the calls FLIPFLOP %d and %d on %s@%s\n", - sub->id, sub->next->id, p->name, p->parent->name); - } - sub->cxmode = MGCP_CX_MUTE; - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP Muting %d on %s@%s\n", sub->id, p->name, p->parent->name); - } - transmit_modify_request(sub); - if (ast_bridged_channel(sub->owner)) - ast_queue_control(sub->owner, AST_CONTROL_HOLD); - - if (ast_bridged_channel(sub->next->owner)) - ast_queue_control(sub->next->owner, AST_CONTROL_HOLD); - - handle_hd_hf(sub->next, ev); - } - } else { - /* We've most likely lost one of our calls find an active call and bring it up */ - if (sub->owner) { - p->sub = sub; - } else if (sub->next->owner) { - p->sub = sub->next; - } else { - /* We seem to have lost both our calls */ - /* XXX - What do we do now? */ - return -1; - } - if (ast_bridged_channel(p->sub->owner)) - ast_queue_control(p->sub->owner, AST_CONTROL_UNHOLD); - p->sub->cxmode = MGCP_CX_SENDRECV; - transmit_modify_request(p->sub); - } - } else { - ast_log(LOG_WARNING, "Callwaiting, call transfer or threeway calling not enabled on endpoint %s@%s\n", - p->name, p->parent->name); - } - } else if (!strcasecmp(ev, "hu")) { - p->hookstate = MGCP_ONHOOK; - sub->cxmode = MGCP_CX_RECVONLY; - ast_log(LOG_DEBUG, "MGCP %s@%s Went on hook\n", p->name, p->parent->name); - /* Do we need to send MDCX before a DLCX ? - if (sub->rtp) { - transmit_modify_request(sub); - } - */ - if (p->transfer && (sub->owner && sub->next->owner) && ((!sub->outgoing) || (!sub->next->outgoing))) { - /* We're allowed to transfer, we have two avtive calls and */ - /* we made at least one of the calls. Let's try and transfer */ - ast_mutex_lock(&p->sub->next->lock); - res = attempt_transfer(p); - if (res < 0) { - if (p->sub->next->owner) { - sub->next->alreadygone = 1; - mgcp_queue_hangup(sub->next); - } - } else if (res) { - ast_log(LOG_WARNING, "Transfer attempt failed\n"); - ast_mutex_unlock(&p->sub->next->lock); - return -1; - } - ast_mutex_unlock(&p->sub->next->lock); - } else { - /* Hangup the current call */ - /* If there is another active call, mgcp_hangup will ring the phone with the other call */ - if (sub->owner) { - sub->alreadygone = 1; - mgcp_queue_hangup(sub); - } else { - /* verbose level check */ - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP handle_request(%s@%s-%d) ast_channel already destroyed, resending DLCX.\n", - p->name, p->parent->name, sub->id); - } - /* Instruct the other side to remove the connection since it apparently * - * still thinks the channel is active. * - * For Cisco IAD2421 /BAK/ */ - transmit_connection_del(sub); - } - } - if ((p->hookstate == MGCP_ONHOOK) && (!sub->rtp) && (!sub->next->rtp)) { - p->hidecallerid = 0; - if (p->hascallwaiting && !p->callwaiting) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Enabling call waiting on MGCP/%s@%s-%d\n", p->name, p->parent->name, sub->id); - p->callwaiting = -1; - } - if (has_voicemail(p)) { - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP handle_request(%s@%s) set vmwi(+)\n", p->name, p->parent->name); - } - transmit_notify_request(sub, "L/vmwi(+)"); - } else { - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP handle_request(%s@%s) set vmwi(-)\n", p->name, p->parent->name); - } - transmit_notify_request(sub, "L/vmwi(-)"); - } - } - } else if ((strlen(ev) == 1) && - (((ev[0] >= '0') && (ev[0] <= '9')) || - ((ev[0] >= 'A') && (ev[0] <= 'D')) || - (ev[0] == '*') || (ev[0] == '#'))) { - if (sub && sub->owner && (sub->owner->_state >= AST_STATE_UP)) { - f.frametype = AST_FRAME_DTMF; - f.subclass = ev[0]; - f.src = "mgcp"; - /* XXX MUST queue this frame to all subs in threeway call if threeway call is active */ - mgcp_queue_frame(sub, &f); - ast_mutex_lock(&sub->next->lock); - if (sub->next->owner) - mgcp_queue_frame(sub->next, &f); - ast_mutex_unlock(&sub->next->lock); - if (strstr(p->curtone, "wt") && (ev[0] == 'A')) { - memset(p->curtone, 0, sizeof(p->curtone)); - } - } else { - p->dtmf_buf[strlen(p->dtmf_buf)] = ev[0]; - p->dtmf_buf[strlen(p->dtmf_buf)] = '\0'; - } - } else if (!strcasecmp(ev, "T")) { - /* Digit timeout -- unimportant */ - } else if (!strcasecmp(ev, "ping")) { - /* ping -- unimportant */ - } else { - ast_log(LOG_NOTICE, "Received unknown event '%s' from %s@%s\n", ev, p->name, p->parent->name); - } - } else { - ast_log(LOG_WARNING, "Unknown verb '%s' received from %s\n", req->verb, ast_inet_ntoa(sin->sin_addr)); - transmit_response(sub, "510", req, "Unknown verb"); - } - return 0; -} - -static int find_and_retrans(struct mgcp_subchannel *sub, struct mgcp_request *req) -{ - int seqno=0; - time_t now; - struct mgcp_response *prev = NULL, *cur, *next, *answer=NULL; - time(&now); - if (sscanf(req->identifier, "%d", &seqno) != 1) - seqno = 0; - cur = sub->parent->parent->responses; - while(cur) { - next = cur->next; - if (now - cur->whensent > RESPONSE_TIMEOUT) { - /* Delete this entry */ - if (prev) - prev->next = next; - else - sub->parent->parent->responses = next; - free(cur); - } else { - if (seqno == cur->seqno) - answer = cur; - prev = cur; - } - cur = next; - } - if (answer) { - resend_response(sub, answer); - return 1; - } - return 0; -} - -static int mgcpsock_read(int *id, int fd, short events, void *ignore) -{ - struct mgcp_request req; - struct sockaddr_in sin; - struct mgcp_subchannel *sub; - int res; - socklen_t len; - int result; - int ident; - len = sizeof(sin); - memset(&req, 0, sizeof(req)); - res = recvfrom(mgcpsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len); - if (res < 0) { - if (errno != ECONNREFUSED) - ast_log(LOG_WARNING, "Recv error: %s\n", strerror(errno)); - return 1; - } - req.data[res] = '\0'; - req.len = res; - if (mgcpdebug) { - ast_verbose("MGCP read: \n%s\nfrom %s:%d\n", req.data, ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - } - parse(&req); - if (req.headers < 1) { - /* Must have at least one header */ - return 1; - } - if (ast_strlen_zero(req.identifier)) { - ast_log(LOG_NOTICE, "Message from %s missing identifier\n", ast_inet_ntoa(sin.sin_addr)); - return 1; - } - - if (sscanf(req.verb, "%d", &result) && sscanf(req.identifier, "%d", &ident)) { - /* Try to find who this message is for, if it's important */ - sub = find_subchannel_and_lock(NULL, ident, &sin); - if (sub) { - struct mgcp_gateway *gw = sub->parent->parent; - struct mgcp_message *cur, *prev; - - ast_mutex_unlock(&sub->lock); - ast_mutex_lock(&gw->msgs_lock); - for (prev = NULL, cur = gw->msgs; cur; prev = cur, cur = cur->next) { - if (cur->seqno == ident) { - ast_log(LOG_DEBUG, "Got response back on transaction %d\n", ident); - if (prev) - prev->next = cur->next; - else - gw->msgs = cur->next; - break; - } - } - - /* stop retrans timer if the queue is empty */ - if (!gw->msgs) { - AST_SCHED_DEL(sched, gw->retransid); - } - - ast_mutex_unlock(&gw->msgs_lock); - if (cur) { - handle_response(cur->owner_ep, cur->owner_sub, result, ident, &req); - free(cur); - return 1; - } - - ast_log(LOG_NOTICE, "Got response back on [%s] for transaction %d we aren't sending?\n", - gw->name, ident); - } - } else { - if (ast_strlen_zero(req.endpoint) || - ast_strlen_zero(req.version) || - ast_strlen_zero(req.verb)) { - ast_log(LOG_NOTICE, "Message must have a verb, an idenitifier, version, and endpoint\n"); - return 1; - } - /* Process request, with iflock held */ - sub = find_subchannel_and_lock(req.endpoint, 0, &sin); - if (sub) { - /* look first to find a matching response in the queue */ - if (!find_and_retrans(sub, &req)) - /* pass the request off to the currently mastering subchannel */ - handle_request(sub, &req, &sin); - ast_mutex_unlock(&sub->lock); - } - } - return 1; -} - -static int *mgcpsock_read_id = NULL; - -static void *do_monitor(void *data) -{ - int res; - int reloading; - /*struct mgcp_gateway *g;*/ - /*struct mgcp_endpoint *e;*/ - /*time_t thispass = 0, lastpass = 0;*/ - - /* Add an I/O event to our UDP socket */ - if (mgcpsock > -1) - mgcpsock_read_id = ast_io_add(io, mgcpsock, mgcpsock_read, AST_IO_IN, NULL); - - /* This thread monitors all the frame relay interfaces which are not yet in use - (and thus do not have a separate thread) indefinitely */ - /* From here on out, we die whenever asked */ - for(;;) { - /* Check for a reload request */ - ast_mutex_lock(&mgcp_reload_lock); - reloading = mgcp_reloading; - mgcp_reloading = 0; - ast_mutex_unlock(&mgcp_reload_lock); - if (reloading) { - if (option_verbose > 0) - ast_verbose(VERBOSE_PREFIX_1 "Reloading MGCP\n"); - mgcp_do_reload(); - /* Add an I/O event to our UDP socket */ - if (mgcpsock > -1) - mgcpsock_read_id = ast_io_add(io, mgcpsock, mgcpsock_read, AST_IO_IN, NULL); - } - - /* Check for interfaces needing to be killed */ - /* Don't let anybody kill us right away. Nobody should lock the interface list - and wait for the monitor list, but the other way around is okay. */ - ast_mutex_lock(&monlock); - /* Lock the network interface */ - ast_mutex_lock(&netlock); - -#if 0 - /* XXX THIS IS COMPLETELY HOSED */ - /* The gateway goes into a state of panic */ - /* If the vmwi indicator is sent while it is reseting interfaces */ - lastpass = thispass; - thispass = time(NULL); - g = gateways; - while(g) { - if (thispass != lastpass) { - e = g->endpoints; - while(e) { - if (e->type == TYPE_LINE) { - res = has_voicemail(e); - if ((e->msgstate != res) && (e->hookstate == MGCP_ONHOOK) && (!e->rtp)){ - if (res) { - transmit_notify_request(e, "L/vmwi(+)"); - } else { - transmit_notify_request(e, "L/vmwi(-)"); - } - e->msgstate = res; - e->onhooktime = thispass; - } - } - e = e->next; - } - } - g = g->next; - } -#endif - /* Okay, now that we know what to do, release the network lock */ - ast_mutex_unlock(&netlock); - /* And from now on, we're okay to be killed, so release the monitor lock as well */ - ast_mutex_unlock(&monlock); - pthread_testcancel(); - /* Wait for sched or io */ - res = ast_sched_wait(sched); - /* copied from chan_sip.c */ - if ((res < 0) || (res > 1000)) - res = 1000; - res = ast_io_wait(io, res); - ast_mutex_lock(&monlock); - if (res >= 0) - ast_sched_runq(sched); - ast_mutex_unlock(&monlock); - } - /* Never reached */ - return NULL; -} - -static int restart_monitor(void) -{ - /* If we're supposed to be stopped -- stay stopped */ - if (monitor_thread == AST_PTHREADT_STOP) - return 0; - if (ast_mutex_lock(&monlock)) { - ast_log(LOG_WARNING, "Unable to lock monitor\n"); - return -1; - } - if (monitor_thread == pthread_self()) { - ast_mutex_unlock(&monlock); - ast_log(LOG_WARNING, "Cannot kill myself\n"); - return -1; - } - if (monitor_thread != AST_PTHREADT_NULL) { - /* Wake up the thread */ - pthread_kill(monitor_thread, SIGURG); - } else { - /* Start a new monitor */ - if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) { - ast_mutex_unlock(&monlock); - ast_log(LOG_ERROR, "Unable to start monitor thread.\n"); - return -1; - } - } - ast_mutex_unlock(&monlock); - return 0; -} - -static struct ast_channel *mgcp_request(const char *type, int format, void *data, int *cause) -{ - int oldformat; - struct mgcp_subchannel *sub; - struct ast_channel *tmpc = NULL; - char tmp[256]; - char *dest = data; - - oldformat = format; - format &= capability; - if (!format) { - ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format); - return NULL; - } - ast_copy_string(tmp, dest, sizeof(tmp)); - if (ast_strlen_zero(tmp)) { - ast_log(LOG_NOTICE, "MGCP Channels require an endpoint\n"); - return NULL; - } - sub = find_subchannel_and_lock(tmp, 0, NULL); - if (!sub) { - ast_log(LOG_WARNING, "Unable to find MGCP endpoint '%s'\n", tmp); - *cause = AST_CAUSE_UNREGISTERED; - return NULL; - } - - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_request(%s)\n", tmp); - ast_verbose(VERBOSE_PREFIX_3 "MGCP cw: %d, dnd: %d, so: %d, sno: %d\n", - sub->parent->callwaiting, sub->parent->dnd, sub->owner ? 1 : 0, sub->next->owner ? 1: 0); - } - /* Must be busy */ - if (((sub->parent->callwaiting) && ((sub->owner) && (sub->next->owner))) || - ((!sub->parent->callwaiting) && (sub->owner)) || - (sub->parent->dnd && (ast_strlen_zero(sub->parent->call_forward)))) { - if (sub->parent->hookstate == MGCP_ONHOOK) { - if (has_voicemail(sub->parent)) { - transmit_notify_request(sub,"L/vmwi(+)"); - } else { - transmit_notify_request(sub,"L/vmwi(-)"); - } - } - *cause = AST_CAUSE_BUSY; - ast_mutex_unlock(&sub->lock); - return NULL; - } - tmpc = mgcp_new(sub->owner ? sub->next : sub, AST_STATE_DOWN); - ast_mutex_unlock(&sub->lock); - if (!tmpc) - ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp); - restart_monitor(); - return tmpc; -} - -/* modified for reload support */ -/*! \brief build_gateway: parse mgcp.conf and create gateway/endpoint structures */ -static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v) -{ - struct mgcp_gateway *gw; - struct mgcp_endpoint *e; - struct mgcp_subchannel *sub; - /*char txident[80];*/ - int i=0, y=0; - int gw_reload = 0; - int ep_reload = 0; - canreinvite = CANREINVITE; - - /* locate existing gateway */ - gw = gateways; - while (gw) { - if (!strcasecmp(cat, gw->name)) { - /* gateway already exists */ - gw->delme = 0; - gw_reload = 1; - break; - } - gw = gw->next; - } - - if (!gw) - gw = malloc(sizeof(struct mgcp_gateway)); - - if (gw) { - if (!gw_reload) { - memset(gw, 0, sizeof(struct mgcp_gateway)); - gw->expire = -1; - gw->retransid = -1; /* SC */ - ast_mutex_init(&gw->msgs_lock); - ast_copy_string(gw->name, cat, sizeof(gw->name)); - /* check if the name is numeric ip */ - if ((strchr(gw->name, '.')) && inet_addr(gw->name) != INADDR_NONE) - gw->isnamedottedip = 1; - } - while(v) { - if (!strcasecmp(v->name, "host")) { - if (!strcasecmp(v->value, "dynamic")) { - /* They'll register with us */ - gw->dynamic = 1; - memset(&gw->addr.sin_addr, 0, 4); - if (gw->addr.sin_port) { - /* If we've already got a port, make it the default rather than absolute */ - gw->defaddr.sin_port = gw->addr.sin_port; - gw->addr.sin_port = 0; - } - } else { - /* Non-dynamic. Make sure we become that way if we're not */ - AST_SCHED_DEL(sched, gw->expire); - gw->dynamic = 0; - if (ast_get_ip(&gw->addr, v->value)) { - if (!gw_reload) { - ast_mutex_destroy(&gw->msgs_lock); - free(gw); - } - return NULL; - } - } - } else if (!strcasecmp(v->name, "defaultip")) { - if (ast_get_ip(&gw->defaddr, v->value)) { - if (!gw_reload) { - ast_mutex_destroy(&gw->msgs_lock); - free(gw); - } - return NULL; - } - } else if (!strcasecmp(v->name, "permit") || - !strcasecmp(v->name, "deny")) { - gw->ha = ast_append_ha(v->name, v->value, gw->ha); - } else if (!strcasecmp(v->name, "port")) { - gw->addr.sin_port = htons(atoi(v->value)); - } else if (!strcasecmp(v->name, "context")) { - ast_copy_string(context, v->value, sizeof(context)); - } else if (!strcasecmp(v->name, "dtmfmode")) { - if (!strcasecmp(v->value, "inband")) - dtmfmode = MGCP_DTMF_INBAND; - else if (!strcasecmp(v->value, "rfc2833")) - dtmfmode = MGCP_DTMF_RFC2833; - else if (!strcasecmp(v->value, "hybrid")) - dtmfmode = MGCP_DTMF_HYBRID; - else if (!strcasecmp(v->value, "none")) - dtmfmode = 0; - else - ast_log(LOG_WARNING, "'%s' is not a valid DTMF mode at line %d\n", v->value, v->lineno); - } else if (!strcasecmp(v->name, "nat")) { - nat = ast_true(v->value); - } else if (!strcasecmp(v->name, "callerid")) { - if (!strcasecmp(v->value, "asreceived")) { - cid_num[0] = '\0'; - cid_name[0] = '\0'; - } else { - ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num)); - } - } else if (!strcasecmp(v->name, "language")) { - ast_copy_string(language, v->value, sizeof(language)); - } else if (!strcasecmp(v->name, "accountcode")) { - ast_copy_string(accountcode, v->value, sizeof(accountcode)); - } else if (!strcasecmp(v->name, "amaflags")) { - y = ast_cdr_amaflags2int(v->value); - if (y < 0) { - ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno); - } else { - amaflags = y; - } - } else if (!strcasecmp(v->name, "musiconhold")) { - ast_copy_string(musicclass, v->value, sizeof(musicclass)); - } else if (!strcasecmp(v->name, "callgroup")) { - cur_callergroup = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "pickupgroup")) { - cur_pickupgroup = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "immediate")) { - immediate = ast_true(v->value); - } else if (!strcasecmp(v->name, "cancallforward")) { - cancallforward = ast_true(v->value); - } else if (!strcasecmp(v->name, "singlepath")) { - singlepath = ast_true(v->value); - } else if (!strcasecmp(v->name, "canreinvite")) { - canreinvite = ast_true(v->value); - } else if (!strcasecmp(v->name, "mailbox")) { - ast_copy_string(mailbox, v->value, sizeof(mailbox)); - } else if (!strcasecmp(v->name, "hasvoicemail")) { - if (ast_true(v->value) && ast_strlen_zero(mailbox)) { - ast_copy_string(mailbox, gw->name, sizeof(mailbox)); - } - } else if (!strcasecmp(v->name, "adsi")) { - adsi = ast_true(v->value); - } else if (!strcasecmp(v->name, "callreturn")) { - callreturn = ast_true(v->value); - } else if (!strcasecmp(v->name, "callwaiting")) { - callwaiting = ast_true(v->value); - } else if (!strcasecmp(v->name, "slowsequence")) { - slowsequence = ast_true(v->value); - } else if (!strcasecmp(v->name, "transfer")) { - transfer = ast_true(v->value); - } else if (!strcasecmp(v->name, "threewaycalling")) { - threewaycalling = ast_true(v->value); - } else if (!strcasecmp(v->name, "wcardep")) { - /* locate existing endpoint */ - e = gw->endpoints; - while (e) { - if (!strcasecmp(v->value, e->name)) { - /* endpoint already exists */ - e->delme = 0; - ep_reload = 1; - break; - } - e = e->next; - } - - if (!e) { - /* Allocate wildcard endpoint */ - e = malloc(sizeof(struct mgcp_endpoint)); - ep_reload = 0; - } - - if (e) { - if (!ep_reload) { - memset(e, 0, sizeof(struct mgcp_endpoint)); - ast_mutex_init(&e->lock); - ast_mutex_init(&e->rqnt_queue_lock); - ast_mutex_init(&e->cmd_queue_lock); - ast_copy_string(e->name, v->value, sizeof(e->name)); - e->needaudit = 1; - } - ast_copy_string(gw->wcardep, v->value, sizeof(gw->wcardep)); - /* XXX Should we really check for uniqueness?? XXX */ - ast_copy_string(e->accountcode, accountcode, sizeof(e->accountcode)); - ast_copy_string(e->context, context, sizeof(e->context)); - ast_copy_string(e->cid_num, cid_num, sizeof(e->cid_num)); - ast_copy_string(e->cid_name, cid_name, sizeof(e->cid_name)); - ast_copy_string(e->language, language, sizeof(e->language)); - ast_copy_string(e->musicclass, musicclass, sizeof(e->musicclass)); - ast_copy_string(e->mailbox, mailbox, sizeof(e->mailbox)); - snprintf(e->rqnt_ident, sizeof(e->rqnt_ident), "%08lx", ast_random()); - e->msgstate = -1; - e->amaflags = amaflags; - e->capability = capability; - e->parent = gw; - e->dtmfmode = dtmfmode; - if (!ep_reload && e->sub && e->sub->rtp) - e->dtmfmode |= MGCP_DTMF_INBAND; - e->adsi = adsi; - e->type = TYPE_LINE; - e->immediate = immediate; - e->callgroup=cur_callergroup; - e->pickupgroup=cur_pickupgroup; - e->callreturn = callreturn; - e->cancallforward = cancallforward; - e->singlepath = singlepath; - e->canreinvite = canreinvite; - e->callwaiting = callwaiting; - e->hascallwaiting = callwaiting; - e->slowsequence = slowsequence; - e->transfer = transfer; - e->threewaycalling = threewaycalling; - e->onhooktime = time(NULL); - /* ASSUME we're onhook */ - e->hookstate = MGCP_ONHOOK; - if (!ep_reload) { - /*snprintf(txident, sizeof(txident), "%08lx", ast_random());*/ - for (i = 0; i < MAX_SUBS; i++) { - sub = malloc(sizeof(struct mgcp_subchannel)); - if (sub) { - ast_verbose(VERBOSE_PREFIX_3 "Allocating subchannel '%d' on %s@%s\n", i, e->name, gw->name); - memset(sub, 0, sizeof(struct mgcp_subchannel)); - ast_mutex_init(&sub->lock); - ast_mutex_init(&sub->cx_queue_lock); - sub->parent = e; - sub->id = i; - snprintf(sub->txident, sizeof(sub->txident), "%08lx", ast_random()); - /*stnrcpy(sub->txident, txident, sizeof(sub->txident) - 1);*/ - sub->cxmode = MGCP_CX_INACTIVE; - sub->nat = nat; - sub->next = e->sub; - e->sub = sub; - } else { - /* XXX Should find a way to clean up our memory */ - ast_log(LOG_WARNING, "Out of memory allocating subchannel\n"); - return NULL; - } - } - /* Make out subs a circular linked list so we can always sping through the whole bunch */ - sub = e->sub; - /* find the end of the list */ - while(sub->next){ - sub = sub->next; - } - /* set the last sub->next to the first sub */ - sub->next = e->sub; - - e->next = gw->endpoints; - gw->endpoints = e; - } - } - } else if (!strcasecmp(v->name, "trunk") || - !strcasecmp(v->name, "line")) { - - /* locate existing endpoint */ - e = gw->endpoints; - while (e) { - if (!strcasecmp(v->value, e->name)) { - /* endpoint already exists */ - e->delme = 0; - ep_reload = 1; - break; - } - e = e->next; - } - - if (!e) { - e = malloc(sizeof(struct mgcp_endpoint)); - ep_reload = 0; - } - - if (e) { - if (!ep_reload) { - memset(e, 0, sizeof(struct mgcp_endpoint)); - ast_mutex_init(&e->lock); - ast_mutex_init(&e->rqnt_queue_lock); - ast_mutex_init(&e->cmd_queue_lock); - ast_copy_string(e->name, v->value, sizeof(e->name)); - e->needaudit = 1; - } - /* XXX Should we really check for uniqueness?? XXX */ - ast_copy_string(e->accountcode, accountcode, sizeof(e->accountcode)); - ast_copy_string(e->context, context, sizeof(e->context)); - ast_copy_string(e->cid_num, cid_num, sizeof(e->cid_num)); - ast_copy_string(e->cid_name, cid_name, sizeof(e->cid_name)); - ast_copy_string(e->language, language, sizeof(e->language)); - ast_copy_string(e->musicclass, musicclass, sizeof(e->musicclass)); - ast_copy_string(e->mailbox, mailbox, sizeof(e->mailbox)); - if (!ast_strlen_zero(mailbox)) { - ast_verbose(VERBOSE_PREFIX_3 "Setting mailbox '%s' on %s@%s\n", mailbox, gw->name, e->name); - } - if (!ep_reload) { - /* XXX potential issue due to reload */ - e->msgstate = -1; - e->parent = gw; - } - e->amaflags = amaflags; - e->capability = capability; - e->dtmfmode = dtmfmode; - e->adsi = adsi; - if (!strcasecmp(v->name, "trunk")) - e->type = TYPE_TRUNK; - else - e->type = TYPE_LINE; - - e->immediate = immediate; - e->callgroup=cur_callergroup; - e->pickupgroup=cur_pickupgroup; - e->callreturn = callreturn; - e->cancallforward = cancallforward; - e->canreinvite = canreinvite; - e->singlepath = singlepath; - e->callwaiting = callwaiting; - e->hascallwaiting = callwaiting; - e->slowsequence = slowsequence; - e->transfer = transfer; - e->threewaycalling = threewaycalling; - if (!ep_reload) { - e->onhooktime = time(NULL); - /* ASSUME we're onhook */ - e->hookstate = MGCP_ONHOOK; - snprintf(e->rqnt_ident, sizeof(e->rqnt_ident), "%08lx", ast_random()); - } - - for (i = 0, sub = NULL; i < MAX_SUBS; i++) { - if (!ep_reload) { - sub = malloc(sizeof(struct mgcp_subchannel)); - } else { - if (!sub) - sub = e->sub; - else - sub = sub->next; - } - - if (sub) { - if (!ep_reload) { - ast_verbose(VERBOSE_PREFIX_3 "Allocating subchannel '%d' on %s@%s\n", i, e->name, gw->name); - memset(sub, 0, sizeof(struct mgcp_subchannel)); - ast_mutex_init(&sub->lock); - ast_mutex_init(&sub->cx_queue_lock); - ast_copy_string(sub->magic, MGCP_SUBCHANNEL_MAGIC, sizeof(sub->magic)); - sub->parent = e; - sub->id = i; - snprintf(sub->txident, sizeof(sub->txident), "%08lx", ast_random()); - sub->cxmode = MGCP_CX_INACTIVE; - sub->next = e->sub; - e->sub = sub; - } - sub->nat = nat; - } else { - /* XXX Should find a way to clean up our memory */ - ast_log(LOG_WARNING, "Out of memory allocating subchannel\n"); - return NULL; - } - } - if (!ep_reload) { - /* Make out subs a circular linked list so we can always sping through the whole bunch */ - sub = e->sub; - /* find the end of the list */ - while (sub->next) { - sub = sub->next; - } - /* set the last sub->next to the first sub */ - sub->next = e->sub; - - e->next = gw->endpoints; - gw->endpoints = e; - } - } - } else - ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno); - v = v->next; - } - } - if (!ntohl(gw->addr.sin_addr.s_addr) && !gw->dynamic) { - ast_log(LOG_WARNING, "Gateway '%s' lacks IP address and isn't dynamic\n", gw->name); - if (!gw_reload) { - ast_mutex_destroy(&gw->msgs_lock); - free(gw); - } - return NULL; - } - gw->defaddr.sin_family = AF_INET; - gw->addr.sin_family = AF_INET; - if (gw->defaddr.sin_addr.s_addr && !ntohs(gw->defaddr.sin_port)) - gw->defaddr.sin_port = htons(DEFAULT_MGCP_GW_PORT); - if (gw->addr.sin_addr.s_addr && !ntohs(gw->addr.sin_port)) - gw->addr.sin_port = htons(DEFAULT_MGCP_GW_PORT); - if (gw->addr.sin_addr.s_addr) - if (ast_ouraddrfor(&gw->addr.sin_addr, &gw->ourip)) - memcpy(&gw->ourip, &__ourip, sizeof(gw->ourip)); - - return (gw_reload ? NULL : gw); -} - -static enum ast_rtp_get_result mgcp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) -{ - struct mgcp_subchannel *sub = NULL; - - if (!(sub = chan->tech_pvt) || !(sub->rtp)) - return AST_RTP_GET_FAILED; - - *rtp = sub->rtp; - - if (sub->parent->canreinvite) - return AST_RTP_TRY_NATIVE; - else - return AST_RTP_TRY_PARTIAL; -} - -static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active) -{ - /* XXX Is there such thing as video support with MGCP? XXX */ - struct mgcp_subchannel *sub; - sub = chan->tech_pvt; - if (sub && !sub->alreadygone) { - transmit_modify_with_sdp(sub, rtp, codecs); - return 0; - } - return -1; -} - -static struct ast_rtp_protocol mgcp_rtp = { - .type = "MGCP", - .get_rtp_info = mgcp_get_rtp_peer, - .set_rtp_peer = mgcp_set_rtp_peer, -}; - -static void destroy_endpoint(struct mgcp_endpoint *e) -{ - struct mgcp_subchannel *sub = e->sub->next, *s; - int i; - - for (i = 0; i < MAX_SUBS; i++) { - ast_mutex_lock(&sub->lock); - if (!ast_strlen_zero(sub->cxident)) { - transmit_connection_del(sub); - } - if (sub->rtp) { - ast_rtp_destroy(sub->rtp); - sub->rtp = NULL; - } - memset(sub->magic, 0, sizeof(sub->magic)); - mgcp_queue_hangup(sub); - dump_cmd_queues(NULL, sub); - ast_mutex_unlock(&sub->lock); - sub = sub->next; - } - - if (e->dsp) { - ast_dsp_free(e->dsp); - } - - dump_queue(e->parent, e); - dump_cmd_queues(e, NULL); - - sub = e->sub; - for (i = 0; (i < MAX_SUBS) && sub; i++) { - s = sub; - sub = sub->next; - ast_mutex_destroy(&s->lock); - ast_mutex_destroy(&s->cx_queue_lock); - free(s); - } - ast_mutex_destroy(&e->lock); - ast_mutex_destroy(&e->rqnt_queue_lock); - ast_mutex_destroy(&e->cmd_queue_lock); - free(e); -} - -static void destroy_gateway(struct mgcp_gateway *g) -{ - if (g->ha) - ast_free_ha(g->ha); - - dump_queue(g, NULL); - - free (g); -} - -static void prune_gateways(void) -{ - struct mgcp_gateway *g, *z, *r; - struct mgcp_endpoint *e, *p, *t; - - ast_mutex_lock(&gatelock); - - /* prune gateways */ - for (z = NULL, g = gateways; g;) { - /* prune endpoints */ - for (p = NULL, e = g->endpoints; e; ) { - if (e->delme || g->delme) { - t = e; - e = e->next; - if (!p) - g->endpoints = e; - else - p->next = e; - destroy_endpoint(t); - } else { - p = e; - e = e->next; - } - } - - if (g->delme) { - r = g; - g = g->next; - if (!z) - gateways = g; - else - z->next = g; - - destroy_gateway(r); - } else { - z = g; - g = g->next; - } - } - - ast_mutex_unlock(&gatelock); -} - -static int reload_config(void) -{ - struct ast_config *cfg; - struct ast_variable *v; - struct mgcp_gateway *g; - struct mgcp_endpoint *e; - char *cat; - struct ast_hostent ahp; - struct hostent *hp; - int format; - - if (gethostname(ourhost, sizeof(ourhost)-1)) { - ast_log(LOG_WARNING, "Unable to get hostname, MGCP disabled\n"); - return 0; - } - cfg = ast_config_load(config); - - /* We *must* have a config file otherwise stop immediately */ - if (!cfg) { - ast_log(LOG_NOTICE, "Unable to load config %s, MGCP disabled\n", config); - return 0; - } - memset(&bindaddr, 0, sizeof(bindaddr)); - dtmfmode = 0; - - /* Copy the default jb config over global_jbconf */ - memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); - - v = ast_variable_browse(cfg, "general"); - while (v) { - /* handle jb conf */ - if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) { - v = v->next; - continue; - } - - /* Create the interface list */ - if (!strcasecmp(v->name, "bindaddr")) { - if (!(hp = ast_gethostbyname(v->value, &ahp))) { - ast_log(LOG_WARNING, "Invalid address: %s\n", v->value); - } else { - memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr)); - } - } else if (!strcasecmp(v->name, "allow")) { - format = ast_getformatbyname(v->value); - if (format < 1) - ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value); - else - capability |= format; - } else if (!strcasecmp(v->name, "disallow")) { - format = ast_getformatbyname(v->value); - if (format < 1) - ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value); - else - capability &= ~format; - } else if (!strcasecmp(v->name, "tos")) { - if (sscanf(v->value, "%d", &format) == 1) - tos = format & 0xff; - else if (!strcasecmp(v->value, "lowdelay")) - tos = IPTOS_LOWDELAY; - else if (!strcasecmp(v->value, "throughput")) - tos = IPTOS_THROUGHPUT; - else if (!strcasecmp(v->value, "reliability")) - tos = IPTOS_RELIABILITY; - else if (!strcasecmp(v->value, "mincost")) - tos = IPTOS_MINCOST; - else if (!strcasecmp(v->value, "none")) - tos = 0; - else - ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno); - } else if (!strcasecmp(v->name, "port")) { - if (sscanf(v->value, "%d", &ourport) == 1) { - bindaddr.sin_port = htons(ourport); - } else { - ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config); - } - } - v = v->next; - } - - /* mark existing entries for deletion */ - ast_mutex_lock(&gatelock); - g = gateways; - while (g) { - g->delme = 1; - e = g->endpoints; - while (e) { - e->delme = 1; - e = e->next; - } - g = g->next; - } - ast_mutex_unlock(&gatelock); - - cat = ast_category_browse(cfg, NULL); - while(cat) { - if (strcasecmp(cat, "general")) { - ast_mutex_lock(&gatelock); - g = build_gateway(cat, ast_variable_browse(cfg, cat)); - if (g) { - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Added gateway '%s'\n", g->name); - } - g->next = gateways; - gateways = g; - } - ast_mutex_unlock(&gatelock); - - /* FS: process queue and IO */ - if (monitor_thread == pthread_self()) { - if (sched) ast_sched_runq(sched); - if (io) ast_io_wait(io, 10); - } - } - cat = ast_category_browse(cfg, cat); - } - - /* prune deleted entries etc. */ - prune_gateways(); - - if (ntohl(bindaddr.sin_addr.s_addr)) { - memcpy(&__ourip, &bindaddr.sin_addr, sizeof(__ourip)); - } else { - hp = ast_gethostbyname(ourhost, &ahp); - if (!hp) { - ast_log(LOG_WARNING, "Unable to get our IP address, MGCP disabled\n"); - ast_config_destroy(cfg); - return 0; - } - memcpy(&__ourip, hp->h_addr, sizeof(__ourip)); - } - if (!ntohs(bindaddr.sin_port)) - bindaddr.sin_port = ntohs(DEFAULT_MGCP_CA_PORT); - bindaddr.sin_family = AF_INET; - ast_mutex_lock(&netlock); - if (mgcpsock > -1) - close(mgcpsock); - - if (mgcpsock_read_id != NULL) - ast_io_remove(io, mgcpsock_read_id); - mgcpsock_read_id = NULL; - - mgcpsock = socket(AF_INET, SOCK_DGRAM, 0); - if (mgcpsock < 0) { - ast_log(LOG_WARNING, "Unable to create MGCP socket: %s\n", strerror(errno)); - } else { - if (bind(mgcpsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { - ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n", - ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port), - strerror(errno)); - close(mgcpsock); - mgcpsock = -1; - } else { - if (option_verbose > 1) { - ast_verbose(VERBOSE_PREFIX_2 "MGCP Listening on %s:%d\n", - ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port)); - ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos); - } - if (setsockopt(mgcpsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) - ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos); - } - } - ast_mutex_unlock(&netlock); - ast_config_destroy(cfg); - - /* send audit only to the new endpoints */ - g = gateways; - while (g) { - e = g->endpoints; - while (e && e->needaudit) { - e->needaudit = 0; - transmit_audit_endpoint(e); - ast_verbose(VERBOSE_PREFIX_3 "MGCP Auditing endpoint %s@%s for hookstate\n", e->name, g->name); - e = e->next; - } - g = g->next; - } - - return 0; -} - -/*! \brief load_module: PBX load module - initialization ---*/ -static int load_module(void) -{ - if (!(sched = sched_context_create())) { - ast_log(LOG_WARNING, "Unable to create schedule context\n"); - return AST_MODULE_LOAD_FAILURE; - } - - if (!(io = io_context_create())) { - ast_log(LOG_WARNING, "Unable to create I/O context\n"); - sched_context_destroy(sched); - return AST_MODULE_LOAD_FAILURE; - } - - if (reload_config()) - return AST_MODULE_LOAD_DECLINE; - - /* Make sure we can register our mgcp channel type */ - if (ast_channel_register(&mgcp_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class 'MGCP'\n"); - io_context_destroy(io); - sched_context_destroy(sched); - return AST_MODULE_LOAD_FAILURE; - } - - ast_rtp_proto_register(&mgcp_rtp); - ast_cli_register_multiple(cli_mgcp, sizeof(cli_mgcp) / sizeof(struct ast_cli_entry)); - - /* And start the monitor for the first time */ - restart_monitor(); - - return AST_MODULE_LOAD_SUCCESS; -} - -/*! \brief mgcp_do_reload: Reload module */ -static int mgcp_do_reload(void) -{ - reload_config(); - return 0; -} - -static int mgcp_reload(int fd, int argc, char *argv[]) -{ - static int deprecated = 0; - if (!deprecated && argc > 0) { - ast_log(LOG_WARNING, "'mgcp reload' is deprecated. Please use 'reload chan_mgcp.so' instead.\n"); - deprecated = 1; - } - - ast_mutex_lock(&mgcp_reload_lock); - if (mgcp_reloading) { - ast_verbose("Previous mgcp reload not yet done\n"); - } else - mgcp_reloading = 1; - ast_mutex_unlock(&mgcp_reload_lock); - restart_monitor(); - return 0; -} - -static int reload(void) -{ - mgcp_reload(0, 0, NULL); - return 0; -} - -static int unload_module(void) -{ - struct mgcp_endpoint *e; - struct mgcp_gateway *g; - - /* Check to see if we're reloading */ - if (ast_mutex_trylock(&mgcp_reload_lock)) { - ast_log(LOG_WARNING, "MGCP is currently reloading. Unable to remove module.\n"); - return -1; - } else { - mgcp_reloading = 1; - ast_mutex_unlock(&mgcp_reload_lock); - } - - /* First, take us out of the channel loop */ - ast_channel_unregister(&mgcp_tech); - - /* Shut down the monitoring thread */ - if (!ast_mutex_lock(&monlock)) { - if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP)) { - pthread_cancel(monitor_thread); - pthread_kill(monitor_thread, SIGURG); - pthread_join(monitor_thread, NULL); - } - monitor_thread = AST_PTHREADT_STOP; - ast_mutex_unlock(&monlock); - } else { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - /* We always want to leave this in a consistent state */ - ast_channel_register(&mgcp_tech); - mgcp_reloading = 0; - mgcp_reload(0, 0, NULL); - return -1; - } - - if (!ast_mutex_lock(&gatelock)) { - for (g = gateways; g; g = g->next) { - g->delme = 1; - for (e = g->endpoints; e; e = e->next) - e->delme = 1; - } - - prune_gateways(); - ast_mutex_unlock(&gatelock); - } else { - ast_log(LOG_WARNING, "Unable to lock the gateways list.\n"); - /* We always want to leave this in a consistent state */ - ast_channel_register(&mgcp_tech); - /* Allow the monitor to restart */ - monitor_thread = AST_PTHREADT_NULL; - mgcp_reloading = 0; - mgcp_reload(0, 0, NULL); - return -1; - } - - close(mgcpsock); - ast_rtp_proto_unregister(&mgcp_rtp); - ast_cli_unregister_multiple(cli_mgcp, sizeof(cli_mgcp) / sizeof(struct ast_cli_entry)); - sched_context_destroy(sched); - - return 0; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Media Gateway Control Protocol (MGCP)", - .load = load_module, - .unload = unload_module, - .reload = reload, - ); diff --git a/1.4.23-rc4/channels/chan_misdn.c b/1.4.23-rc4/channels/chan_misdn.c deleted file mode 100644 index c5965a1c1..000000000 --- a/1.4.23-rc4/channels/chan_misdn.c +++ /dev/null @@ -1,5775 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2004 - 2006, Christian Richter - * - * Christian Richter <crich@beronet.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - * - */ - -/*! - * \file - * - * \brief the chan_misdn channel driver for Asterisk - * - * \author Christian Richter <crich@beronet.com> - * - * \extref MISDN http://www.misdn.org/ - * - * \ingroup channel_drivers - */ - -/*** MODULEINFO - <depend>isdnnet</depend> - <depend>misdn</depend> - <depend>suppserv</depend> - ***/ -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <pthread.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> -#include <arpa/inet.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <signal.h> -#include <sys/file.h> -#include <semaphore.h> - -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/io.h" -#include "asterisk/frame.h" -#include "asterisk/translate.h" -#include "asterisk/cli.h" -#include "asterisk/musiconhold.h" -#include "asterisk/dsp.h" -#include "asterisk/translate.h" -#include "asterisk/config.h" -#include "asterisk/file.h" -#include "asterisk/callerid.h" -#include "asterisk/indications.h" -#include "asterisk/app.h" -#include "asterisk/features.h" -#include "asterisk/term.h" -#include "asterisk/sched.h" -#include "asterisk/stringfields.h" -#include "asterisk/causes.h" - -#include "chan_misdn_config.h" -#include "isdn_lib.h" - -char global_tracefile[BUFFERSIZE + 1]; - -static int g_config_initialized = 0; - -struct misdn_jb{ - int size; - int upper_threshold; - char *samples, *ok; - int wp,rp; - int state_empty; - int state_full; - int state_buffer; - int bytes_wrote; - ast_mutex_t mutexjb; -}; - - - -/*! \brief allocates the jb-structure and initialize the elements */ -struct misdn_jb *misdn_jb_init(int size, int upper_threshold); - -/*! \brief frees the data and destroys the given jitterbuffer struct */ -void misdn_jb_destroy(struct misdn_jb *jb); - -/*! \brief fills the jitterbuffer with len data returns < 0 if there was an -error (buffer overrun). */ -int misdn_jb_fill(struct misdn_jb *jb, const char *data, int len); - -/*! \brief gets len bytes out of the jitterbuffer if available, else only the -available data is returned and the return value indicates the number -of data. */ -int misdn_jb_empty(struct misdn_jb *jb, char *data, int len); - - -/* BEGIN: chan_misdn.h */ - -ast_mutex_t release_lock; - -enum misdn_chan_state { - MISDN_NOTHING=0, /*!< at beginning */ - MISDN_WAITING4DIGS, /*!< when waiting for infos */ - MISDN_EXTCANTMATCH, /*!< when asterisk couldn't match our ext */ - MISDN_INCOMING_SETUP, /*!< for incoming setups*/ - MISDN_DIALING, /*!< when pbx_start */ - MISDN_PROGRESS, /*!< we got a progress */ - MISDN_PROCEEDING, /*!< we got a progress */ - MISDN_CALLING, /*!< when misdn_call is called */ - MISDN_CALLING_ACKNOWLEDGE, /*!< when we get SETUP_ACK */ - MISDN_ALERTING, /*!< when Alerting */ - MISDN_BUSY, /*!< when BUSY */ - MISDN_CONNECTED, /*!< when connected */ - MISDN_PRECONNECTED, /*!< when connected */ - MISDN_DISCONNECTED, /*!< when connected */ - MISDN_RELEASED, /*!< when connected */ - MISDN_BRIDGED, /*!< when bridged */ - MISDN_CLEANING, /*!< when hangup from * but we were connected before */ - MISDN_HUNGUP_FROM_MISDN, /*!< when DISCONNECT/RELEASE/REL_COMP came from misdn */ - MISDN_HUNGUP_FROM_AST, /*!< when DISCONNECT/RELEASE/REL_COMP came out of misdn_hangup */ - MISDN_HOLDED, /*!< if this chan is holded */ - MISDN_HOLD_DISCONNECT, /*!< if this chan is holded */ - -}; - -#define ORG_AST 1 -#define ORG_MISDN 2 - -struct hold_info { - /*! - * \brief Logical port the channel call record is HOLDED on - * because the B channel is no longer associated. - */ - int port; - - /*! - * \brief Original B channel number the HOLDED call was using. - * \note Used only for debug display messages. - */ - int channel; -}; - -/*! - * \brief Channel call record structure - */ -struct chan_list { - /*! - * \brief The "allowed_bearers" string read in from /etc/asterisk/misdn.conf - */ - char allowed_bearers[BUFFERSIZE + 1]; - - /*! - * \brief State of the channel - */ - enum misdn_chan_state state; - - /*! - * \brief TRUE if a hangup needs to be queued - * \note This is a debug flag only used to catch calls to hangup_chan() that are already hungup. - */ - int need_queue_hangup; - - /*! - * \brief TRUE if a channel can be hung up by calling asterisk directly when done. - */ - int need_hangup; - - /*! - * \brief TRUE if we could send an AST_CONTROL_BUSY if needed. - */ - int need_busy; - - /*! - * \brief Who originally created this channel. ORG_AST or ORG_MISDN - */ - int originator; - - /*! - * \brief TRUE of we are not to respond immediately to a SETUP message. Check the dialplan first. - * \note The "noautorespond_on_setup" boolean read in from /etc/asterisk/misdn.conf - */ - int noautorespond_on_setup; - - int norxtone; /* Boolean assigned values but the value is not used. */ - - /*! - * \brief TRUE if we are not to generate tones (Playtones) - */ - int notxtone; - - /*! - * \brief TRUE if echo canceller is enabled. Value is toggled. - */ - int toggle_ec; - - /*! - * \brief TRUE if you want to send Tone Indications to an incoming - * ISDN channel on a TE Port. - * \note The "incoming_early_audio" boolean read in from /etc/asterisk/misdn.conf - */ - int incoming_early_audio; - - /*! - * \brief TRUE if DTMF digits are to be passed inband only. - * \note It is settable by the misdn_set_opt() application. - */ - int ignore_dtmf; - - /*! - * \brief Pipe file descriptor handles array. - * Read from pipe[0], write to pipe[1] - */ - int pipe[2]; - - /*! - * \brief Read buffer for inbound audio from pipe[0] - */ - char ast_rd_buf[4096]; - - /*! - * \brief Inbound audio frame returned by misdn_read(). - */ - struct ast_frame frame; - - /*! - * \brief Fax detection option. (0:no 1:yes 2:yes+nojump) - * \note The "faxdetect" option string read in from /etc/asterisk/misdn.conf - * \note It is settable by the misdn_set_opt() application. - */ - int faxdetect; - - /*! - * \brief Number of seconds to detect a Fax machine when detection enabled. - * \note 0 disables the timeout. - * \note The "faxdetect_timeout" value read in from /etc/asterisk/misdn.conf - */ - int faxdetect_timeout; - - /*! - * \brief Starting time of fax detection with timeout when nonzero. - */ - struct timeval faxdetect_tv; - - /*! - * \brief TRUE if a fax has been detected. - */ - int faxhandled; - - /*! - * \brief TRUE if we will use the Asterisk DSP to detect DTMF/Fax - * \note The "astdtmf" boolean read in from /etc/asterisk/misdn.conf - */ - int ast_dsp; - - /*! - * \brief Jitterbuffer length - * \note The "jitterbuffer" value read in from /etc/asterisk/misdn.conf - */ - int jb_len; - - /*! - * \brief Jitterbuffer upper threshold - * \note The "jitterbuffer_upper_threshold" value read in from /etc/asterisk/misdn.conf - */ - int jb_upper_threshold; - - /*! - * \brief Allocated jitterbuffer controller - * \note misdn_jb_init() creates the jitterbuffer. - * \note Must use misdn_jb_destroy() to clean up. - */ - struct misdn_jb *jb; - - /*! - * \brief Allocated DSP controller - * \note ast_dsp_new() creates the DSP controller. - * \note Must use ast_dsp_free() to clean up. - */ - struct ast_dsp *dsp; - - /*! - * \brief Allocated audio frame sample translator - * \note ast_translator_build_path() creates the translator path. - * \note Must use ast_translator_free_path() to clean up. - */ - struct ast_trans_pvt *trans; - - /*! - * \brief Associated Asterisk channel structure. - */ - struct ast_channel * ast; - - //int dummy; /* Not used */ - - /*! - * \brief Associated B channel structure. - */ - struct misdn_bchannel *bc; - - /*! - * \brief HOLDED channel information - */ - struct hold_info hold_info; - - /*! - * \brief From associated B channel: Layer 3 process ID - * \note Used to find the HOLDED channel call record when retrieving a call. - */ - unsigned int l3id; - - /*! - * \brief From associated B channel: B Channel mISDN driver layer ID from mISDN_get_layerid() - * \note Used only for debug display messages. - */ - int addr; - - /*! - * \brief Incoming call dialplan context identifier. - * \note The "context" string read in from /etc/asterisk/misdn.conf - */ - char context[AST_MAX_CONTEXT]; - - /*! - * \brief The configured music-on-hold class to use for this call. - * \note The "musicclass" string read in from /etc/asterisk/misdn.conf - */ - char mohinterpret[MAX_MUSICCLASS]; - - //int zero_read_cnt; /* Not used */ - - /*! - * \brief Number of outgoing audio frames dropped since last debug gripe message. - */ - int dropped_frame_cnt; - - /*! - * \brief TRUE if we must do the ringback tones. - * \note The "far_alerting" boolean read in from /etc/asterisk/misdn.conf - */ - int far_alerting; - - /*! - * \brief TRUE if NT should disconnect an overlap dialing call when a timeout occurs. - * \note The "nttimeout" boolean read in from /etc/asterisk/misdn.conf - */ - int nttimeout; - - /*! - * \brief Other channel call record PID - * \note Value imported from Asterisk environment variable MISDN_PID - */ - int other_pid; - - /*! - * \brief Bridged other channel call record - * \note Pointer set when other_pid imported from Asterisk environment - * variable MISDN_PID by either side. - */ - struct chan_list *other_ch; - - /*! - * \brief Tone zone sound used for dialtone generation. - * \note Used as a boolean. Non-NULL to prod generation if enabled. - */ - const struct tone_zone_sound *ts; - - /*! - * \brief Enables overlap dialing for the set amount of seconds. (0 = Disabled) - * \note The "overlapdial" value read in from /etc/asterisk/misdn.conf - */ - int overlap_dial; - - /*! - * \brief Overlap dialing timeout Task ID. -1 if not running. - */ - int overlap_dial_task; - - /*! - * \brief overlap_tv access lock. - */ - ast_mutex_t overlap_tv_lock; - - /*! - * \brief Overlap timer start time. Timer restarted for every digit received. - */ - struct timeval overlap_tv; - - //struct chan_list *peer; /* Not used */ - - /*! - * \brief Next channel call record in the list. - */ - struct chan_list *next; - //struct chan_list *prev; /* Not used */ - //struct chan_list *first; /* Not used */ -}; - - - -void export_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch); -void import_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch); - -struct robin_list { - char *group; - int port; - int channel; - struct robin_list *next; - struct robin_list *prev; -}; -static struct robin_list *robin = NULL; - - - -static struct ast_frame *process_ast_dsp(struct chan_list *tmp, struct ast_frame *frame); - - - -static inline void free_robin_list_r (struct robin_list *r) -{ - if (r) { - if (r->next) - free_robin_list_r(r->next); - if (r->group) - free(r->group); - free(r); - } -} - -static void free_robin_list ( void ) -{ - free_robin_list_r(robin); - robin = NULL; -} - -static struct robin_list* get_robin_position (char *group) -{ - struct robin_list *new; - struct robin_list *iter = robin; - for (; iter; iter = iter->next) { - if (!strcasecmp(iter->group, group)) - return iter; - } - new = (struct robin_list *) calloc(1, sizeof(struct robin_list)); - new->group = strndup(group, strlen(group)); - new->port = 0; - new->channel = 0; - if (robin) { - new->next = robin; - robin->prev = new; - } - robin = new; - return robin; -} - - -/*! \brief the main schedule context for stuff like l1 watcher, overlap dial, ... */ -static struct sched_context *misdn_tasks = NULL; -static pthread_t misdn_tasks_thread; - -static int *misdn_ports; - -static void chan_misdn_log(int level, int port, char *tmpl, ...) - __attribute__((format(printf, 3, 4))); - -static struct ast_channel *misdn_new(struct chan_list *cl, int state, char *exten, char *callerid, int format, int port, int c); -static void send_digit_to_chan(struct chan_list *cl, char digit ); - -static void hangup_chan(struct chan_list *ch); -static int pbx_start_chan(struct chan_list *ch); - -#define MISDN_ASTERISK_TECH_PVT(ast) ast->tech_pvt -#define MISDN_ASTERISK_PVT(ast) 1 - -#include "asterisk/strings.h" - -/* #define MISDN_DEBUG 1 */ - -static const char misdn_type[] = "mISDN"; - -static int tracing = 0 ; - -/*! \brief Only alaw and mulaw is allowed for now */ -static int prefformat = AST_FORMAT_ALAW ; /* AST_FORMAT_SLINEAR ; AST_FORMAT_ULAW | */ - -static int *misdn_debug; -static int *misdn_debug_only; -static int max_ports; - -static int *misdn_in_calls; -static int *misdn_out_calls; - - -struct chan_list dummy_cl; - -/*! - * \brief Global channel call record list head. - */ -struct chan_list *cl_te=NULL; -ast_mutex_t cl_te_lock; - -static enum event_response_e -cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data); - -static void send_cause2ast(struct ast_channel *ast, struct misdn_bchannel*bc, struct chan_list *ch); - -static void cl_queue_chan(struct chan_list **list, struct chan_list *chan); -static void cl_dequeue_chan(struct chan_list **list, struct chan_list *chan); -static struct chan_list *find_chan_by_bc(struct chan_list *list, struct misdn_bchannel *bc); -static struct chan_list *find_chan_by_pid(struct chan_list *list, int pid); - - - -static int dialtone_indicate(struct chan_list *cl); -static int hanguptone_indicate(struct chan_list *cl); -static int stop_indicate(struct chan_list *cl); - -static int start_bc_tones(struct chan_list *cl); -static int stop_bc_tones(struct chan_list *cl); -static void release_chan(struct misdn_bchannel *bc); - -static int misdn_check_l2l1(struct ast_channel *chan, void *data); -static int misdn_set_opt_exec(struct ast_channel *chan, void *data); -static int misdn_facility_exec(struct ast_channel *chan, void *data); - -int chan_misdn_jb_empty(struct misdn_bchannel *bc, char *buf, int len); - - -void debug_numplan(int port, int numplan, char *type); - - -int add_out_calls(int port); -int add_in_calls(int port); - - -#ifdef MISDN_1_2 -static int update_pipeline_config(struct misdn_bchannel *bc); -#else -static int update_ec_config(struct misdn_bchannel *bc); -#endif - - - -/*************** Helpers *****************/ - -static struct chan_list * get_chan_by_ast(struct ast_channel *ast) -{ - struct chan_list *tmp; - - for (tmp=cl_te; tmp; tmp = tmp->next) { - if ( tmp->ast == ast ) return tmp; - } - - return NULL; -} - -static struct chan_list * get_chan_by_ast_name(char *name) -{ - struct chan_list *tmp; - - for (tmp=cl_te; tmp; tmp = tmp->next) { - if ( tmp->ast && strcmp(tmp->ast->name,name) == 0) return tmp; - } - - return NULL; -} - - - -struct allowed_bearers { - char *name; /*!< Bearer capability name string used in /etc/misdn.conf allowed_bearers */ - char *display; /*!< Bearer capability displayable name */ - int cap; /*!< SETUP message bearer capability field code value */ - int deprecated; /*!< TRUE if this entry is deprecated. (Misspelled or bad name to use) */ -}; - -/* *INDENT-OFF* */ -static const struct allowed_bearers allowed_bearers_array[]= { - /* Name, Displayable Name Bearer Capability, Deprecated */ - { "speech", "Speech", INFO_CAPABILITY_SPEECH, 0 }, - { "3_1khz", "3.1KHz Audio", INFO_CAPABILITY_AUDIO_3_1K, 0 }, - { "digital_unrestricted", "Unrestricted Digital", INFO_CAPABILITY_DIGITAL_UNRESTRICTED, 0 }, - { "digital_restricted", "Restricted Digital", INFO_CAPABILITY_DIGITAL_RESTRICTED, 0 }, - { "digital_restriced", "Restricted Digital", INFO_CAPABILITY_DIGITAL_RESTRICTED, 1 }, /* Allow misspelling for backwards compatibility */ - { "video", "Video", INFO_CAPABILITY_VIDEO, 0 } -}; -/* *INDENT-ON* */ - -static const char *bearer2str(int cap) -{ - unsigned index; - - for (index = 0; index < ARRAY_LEN(allowed_bearers_array); ++index) { - if (allowed_bearers_array[index].cap == cap) { - return allowed_bearers_array[index].display; - } - } /* end for */ - - return "Unknown Bearer"; -} - - -static void print_facility(struct FacParm *fac, struct misdn_bchannel *bc) -{ - switch (fac->Function) { - case Fac_CD: - chan_misdn_log(1,bc->port," --> calldeflect to: %s, presentable: %s\n", fac->u.CDeflection.DeflectedToNumber, - fac->u.CDeflection.PresentationAllowed ? "yes" : "no"); - break; - case Fac_AOCDCurrency: - if (fac->u.AOCDcur.chargeNotAvailable) - chan_misdn_log(1,bc->port," --> AOCD currency: charge not available\n"); - else if (fac->u.AOCDcur.freeOfCharge) - chan_misdn_log(1,bc->port," --> AOCD currency: free of charge\n"); - else if (fac->u.AOCDchu.billingId >= 0) - chan_misdn_log(1,bc->port," --> AOCD currency: currency:%s amount:%d multiplier:%d typeOfChargingInfo:%s billingId:%d\n", - fac->u.AOCDcur.currency, fac->u.AOCDcur.currencyAmount, fac->u.AOCDcur.multiplier, - (fac->u.AOCDcur.typeOfChargingInfo == 0) ? "subTotal" : "total", fac->u.AOCDcur.billingId); - else - chan_misdn_log(1,bc->port," --> AOCD currency: currency:%s amount:%d multiplier:%d typeOfChargingInfo:%s\n", - fac->u.AOCDcur.currency, fac->u.AOCDcur.currencyAmount, fac->u.AOCDcur.multiplier, - (fac->u.AOCDcur.typeOfChargingInfo == 0) ? "subTotal" : "total"); - break; - case Fac_AOCDChargingUnit: - if (fac->u.AOCDchu.chargeNotAvailable) - chan_misdn_log(1,bc->port," --> AOCD charging unit: charge not available\n"); - else if (fac->u.AOCDchu.freeOfCharge) - chan_misdn_log(1,bc->port," --> AOCD charging unit: free of charge\n"); - else if (fac->u.AOCDchu.billingId >= 0) - chan_misdn_log(1,bc->port," --> AOCD charging unit: recordedUnits:%d typeOfChargingInfo:%s billingId:%d\n", - fac->u.AOCDchu.recordedUnits, (fac->u.AOCDchu.typeOfChargingInfo == 0) ? "subTotal" : "total", fac->u.AOCDchu.billingId); - else - chan_misdn_log(1,bc->port," --> AOCD charging unit: recordedUnits:%d typeOfChargingInfo:%s\n", - fac->u.AOCDchu.recordedUnits, (fac->u.AOCDchu.typeOfChargingInfo == 0) ? "subTotal" : "total"); - break; - default: - chan_misdn_log(1,bc->port," --> unknown facility\n"); - break; - } -} - -static void print_bearer(struct misdn_bchannel *bc) -{ - - chan_misdn_log(2, bc->port, " --> Bearer: %s\n",bearer2str(bc->capability)); - - switch(bc->law) { - case INFO_CODEC_ALAW: - chan_misdn_log(2, bc->port, " --> Codec: Alaw\n"); - break; - case INFO_CODEC_ULAW: - chan_misdn_log(2, bc->port, " --> Codec: Ulaw\n"); - break; - } -} - -static void export_aoc_vars(int originator, struct ast_channel *ast, struct misdn_bchannel *bc) -{ - char buf[128]; - - if (!ast) - return; - - if (originator == ORG_AST) { - ast = ast_bridged_channel(ast); - if (!ast) - return; - } - - switch (bc->AOCDtype) { - case Fac_AOCDCurrency: - pbx_builtin_setvar_helper(ast, "AOCD_Type", "currency"); - if (bc->AOCD.currency.chargeNotAvailable) - pbx_builtin_setvar_helper(ast, "AOCD_ChargeAvailable", "no"); - else { - pbx_builtin_setvar_helper(ast, "AOCD_ChargeAvailable", "yes"); - if (bc->AOCD.currency.freeOfCharge) - pbx_builtin_setvar_helper(ast, "AOCD_FreeOfCharge", "yes"); - else { - pbx_builtin_setvar_helper(ast, "AOCD_FreeOfCharge", "no"); - if (snprintf(buf, sizeof(buf), "%d %s", bc->AOCD.currency.currencyAmount * bc->AOCD.currency.multiplier, bc->AOCD.currency.currency) < sizeof(buf)) { - pbx_builtin_setvar_helper(ast, "AOCD_Amount", buf); - if (bc->AOCD.currency.billingId >= 0 && snprintf(buf, sizeof(buf), "%d", bc->AOCD.currency.billingId) < sizeof(buf)) - pbx_builtin_setvar_helper(ast, "AOCD_BillingId", buf); - } - } - } - break; - case Fac_AOCDChargingUnit: - pbx_builtin_setvar_helper(ast, "AOCD_Type", "charging_unit"); - if (bc->AOCD.chargingUnit.chargeNotAvailable) - pbx_builtin_setvar_helper(ast, "AOCD_ChargeAvailable", "no"); - else { - pbx_builtin_setvar_helper(ast, "AOCD_ChargeAvailable", "yes"); - if (bc->AOCD.chargingUnit.freeOfCharge) - pbx_builtin_setvar_helper(ast, "AOCD_FreeOfCharge", "yes"); - else { - pbx_builtin_setvar_helper(ast, "AOCD_FreeOfCharge", "no"); - if (snprintf(buf, sizeof(buf), "%d", bc->AOCD.chargingUnit.recordedUnits) < sizeof(buf)) { - pbx_builtin_setvar_helper(ast, "AOCD_RecordedUnits", buf); - if (bc->AOCD.chargingUnit.billingId >= 0 && snprintf(buf, sizeof(buf), "%d", bc->AOCD.chargingUnit.billingId) < sizeof(buf)) - pbx_builtin_setvar_helper(ast, "AOCD_BillingId", buf); - } - } - } - break; - default: - break; - } -} - -/*************** Helpers END *************/ - -static void sighandler(int sig) -{} - -static void* misdn_tasks_thread_func (void *data) -{ - int wait; - struct sigaction sa; - - sa.sa_handler = sighandler; - sa.sa_flags = SA_NODEFER; - sigemptyset(&sa.sa_mask); - sigaddset(&sa.sa_mask, SIGUSR1); - sigaction(SIGUSR1, &sa, NULL); - - sem_post((sem_t *)data); - - while (1) { - wait = ast_sched_wait(misdn_tasks); - if (wait < 0) - wait = 8000; - if (poll(NULL, 0, wait) < 0) - chan_misdn_log(4, 0, "Waking up misdn_tasks thread\n"); - ast_sched_runq(misdn_tasks); - } - return NULL; -} - -static void misdn_tasks_init (void) -{ - sem_t blocker; - int i = 5; - - if (sem_init(&blocker, 0, 0)) { - perror("chan_misdn: Failed to initialize semaphore!"); - exit(1); - } - - chan_misdn_log(4, 0, "Starting misdn_tasks thread\n"); - - misdn_tasks = sched_context_create(); - pthread_create(&misdn_tasks_thread, NULL, misdn_tasks_thread_func, &blocker); - - while (sem_wait(&blocker) && --i); - sem_destroy(&blocker); -} - -static void misdn_tasks_destroy (void) -{ - if (misdn_tasks) { - chan_misdn_log(4, 0, "Killing misdn_tasks thread\n"); - if ( pthread_cancel(misdn_tasks_thread) == 0 ) { - cb_log(4, 0, "Joining misdn_tasks thread\n"); - pthread_join(misdn_tasks_thread, NULL); - } - sched_context_destroy(misdn_tasks); - } -} - -static inline void misdn_tasks_wakeup (void) -{ - pthread_kill(misdn_tasks_thread, SIGUSR1); -} - -static inline int _misdn_tasks_add_variable (int timeout, ast_sched_cb callback, const void *data, int variable) -{ - int task_id; - - if (!misdn_tasks) { - misdn_tasks_init(); - } - task_id = ast_sched_add_variable(misdn_tasks, timeout, callback, data, variable); - misdn_tasks_wakeup(); - - return task_id; -} - -static int misdn_tasks_add (int timeout, ast_sched_cb callback, const void *data) -{ - return _misdn_tasks_add_variable(timeout, callback, data, 0); -} - -static int misdn_tasks_add_variable (int timeout, ast_sched_cb callback, const void *data) -{ - return _misdn_tasks_add_variable(timeout, callback, data, 1); -} - -static void misdn_tasks_remove (int task_id) -{ - AST_SCHED_DEL(misdn_tasks, task_id); -} - -static int misdn_l1_task (const void *data) -{ - misdn_lib_isdn_l1watcher(*(int *)data); - chan_misdn_log(5, *(int *)data, "L1watcher timeout\n"); - return 1; -} - -static int misdn_overlap_dial_task (const void *data) -{ - struct timeval tv_end, tv_now; - int diff; - struct chan_list *ch = (struct chan_list *)data; - - chan_misdn_log(4, ch->bc->port, "overlap dial task, chan_state: %d\n", ch->state); - - if (ch->state != MISDN_WAITING4DIGS) { - ch->overlap_dial_task = -1; - return 0; - } - - ast_mutex_lock(&ch->overlap_tv_lock); - tv_end = ch->overlap_tv; - ast_mutex_unlock(&ch->overlap_tv_lock); - - tv_end.tv_sec += ch->overlap_dial; - tv_now = ast_tvnow(); - - diff = ast_tvdiff_ms(tv_end, tv_now); - - if (diff <= 100) { - char *dad=ch->bc->dad, sexten[]="s"; - /* if we are 100ms near the timeout, we are satisfied.. */ - stop_indicate(ch); - - if (ast_strlen_zero(ch->bc->dad)) { - dad=sexten; - strcpy(ch->ast->exten, sexten); - } - - if (ast_exists_extension(ch->ast, ch->context, dad, 1, ch->bc->oad)) { - ch->state=MISDN_DIALING; - if (pbx_start_chan(ch) < 0) { - chan_misdn_log(-1, ch->bc->port, "ast_pbx_start returned < 0 in misdn_overlap_dial_task\n"); - goto misdn_overlap_dial_task_disconnect; - } - } else { -misdn_overlap_dial_task_disconnect: - hanguptone_indicate(ch); - ch->bc->out_cause = AST_CAUSE_UNALLOCATED; - ch->state=MISDN_CLEANING; - misdn_lib_send_event(ch->bc, EVENT_DISCONNECT); - } - ch->overlap_dial_task = -1; - return 0; - } else - return diff; -} - -static void send_digit_to_chan(struct chan_list *cl, char digit ) -{ - static const char* dtmf_tones[] = { - "!941+1336/100,!0/100", /* 0 */ - "!697+1209/100,!0/100", /* 1 */ - "!697+1336/100,!0/100", /* 2 */ - "!697+1477/100,!0/100", /* 3 */ - "!770+1209/100,!0/100", /* 4 */ - "!770+1336/100,!0/100", /* 5 */ - "!770+1477/100,!0/100", /* 6 */ - "!852+1209/100,!0/100", /* 7 */ - "!852+1336/100,!0/100", /* 8 */ - "!852+1477/100,!0/100", /* 9 */ - "!697+1633/100,!0/100", /* A */ - "!770+1633/100,!0/100", /* B */ - "!852+1633/100,!0/100", /* C */ - "!941+1633/100,!0/100", /* D */ - "!941+1209/100,!0/100", /* * */ - "!941+1477/100,!0/100" }; /* # */ - struct ast_channel *chan=cl->ast; - - if (digit >= '0' && digit <='9') - ast_playtones_start(chan,0,dtmf_tones[digit-'0'], 0); - else if (digit >= 'A' && digit <= 'D') - ast_playtones_start(chan,0,dtmf_tones[digit-'A'+10], 0); - else if (digit == '*') - ast_playtones_start(chan,0,dtmf_tones[14], 0); - else if (digit == '#') - ast_playtones_start(chan,0,dtmf_tones[15], 0); - else { - /* not handled */ - ast_log(LOG_DEBUG, "Unable to handle DTMF tone '%c' for '%s'\n", digit, chan->name); - } -} - -/*** CLI HANDLING ***/ -static int misdn_set_debug(int fd, int argc, char *argv[]) -{ - int level; - - if (argc != 4 && argc != 5 && argc != 6 && argc != 7) - return RESULT_SHOWUSAGE; - - level = atoi(argv[3]); - - switch (argc) { - case 4: - case 5: - { - int i; - int only = 0; - if (argc == 5) { - if (strncasecmp(argv[4], "only", strlen(argv[4]))) - return RESULT_SHOWUSAGE; - else - only = 1; - } - - for (i = 0; i <= max_ports; i++) { - misdn_debug[i] = level; - misdn_debug_only[i] = only; - } - ast_cli(fd, "changing debug level for all ports to %d%s\n",misdn_debug[0], only?" (only)":""); - } - break; - case 6: - case 7: - { - int port; - if (strncasecmp(argv[4], "port", strlen(argv[4]))) - return RESULT_SHOWUSAGE; - port = atoi(argv[5]); - if (port <= 0 || port > max_ports) { - switch (max_ports) { - case 0: - ast_cli(fd, "port number not valid! no ports available so you won't get lucky with any number here...\n"); - break; - case 1: - ast_cli(fd, "port number not valid! only port 1 is available.\n"); - break; - default: - ast_cli(fd, "port number not valid! only ports 1 to %d are available.\n", max_ports); - } - return 0; - } - if (argc == 7) { - if (strncasecmp(argv[6], "only", strlen(argv[6]))) - return RESULT_SHOWUSAGE; - else - misdn_debug_only[port] = 1; - } else - misdn_debug_only[port] = 0; - misdn_debug[port] = level; - ast_cli(fd, "changing debug level to %d%s for port %d\n", misdn_debug[port], misdn_debug_only[port]?" (only)":"", port); - } - } - return 0; -} - -static int misdn_set_crypt_debug(int fd, int argc, char *argv[]) -{ - if (argc != 5) return RESULT_SHOWUSAGE; - - return 0; -} - -static int misdn_port_block(int fd, int argc, char *argv[]) -{ - int port; - - if (argc != 4) - return RESULT_SHOWUSAGE; - - port = atoi(argv[3]); - - misdn_lib_port_block(port); - - return 0; -} - -static int misdn_port_unblock(int fd, int argc, char *argv[]) -{ - int port; - - if (argc != 4) - return RESULT_SHOWUSAGE; - - port = atoi(argv[3]); - - misdn_lib_port_unblock(port); - - return 0; -} - - -static int misdn_restart_port (int fd, int argc, char *argv[]) -{ - int port; - - if (argc != 4) - return RESULT_SHOWUSAGE; - - port = atoi(argv[3]); - - misdn_lib_port_restart(port); - - return 0; -} - -static int misdn_restart_pid (int fd, int argc, char *argv[]) -{ - int pid; - - if (argc != 4) - return RESULT_SHOWUSAGE; - - pid = atoi(argv[3]); - - misdn_lib_pid_restart(pid); - - return 0; -} - -static int misdn_port_up (int fd, int argc, char *argv[]) -{ - int port; - - if (argc != 4) - return RESULT_SHOWUSAGE; - - port = atoi(argv[3]); - - misdn_lib_get_port_up(port); - - return 0; -} - -static int misdn_port_down (int fd, int argc, char *argv[]) -{ - int port; - - if (argc != 4) - return RESULT_SHOWUSAGE; - - port = atoi(argv[3]); - - misdn_lib_get_port_down(port); - - return 0; -} - -static inline void show_config_description (int fd, enum misdn_cfg_elements elem) -{ - char section[BUFFERSIZE]; - char name[BUFFERSIZE]; - char desc[BUFFERSIZE]; - char def[BUFFERSIZE]; - char tmp[BUFFERSIZE]; - - misdn_cfg_get_name(elem, tmp, sizeof(tmp)); - term_color(name, tmp, COLOR_BRWHITE, 0, sizeof(tmp)); - misdn_cfg_get_desc(elem, desc, sizeof(desc), def, sizeof(def)); - - if (elem < MISDN_CFG_LAST) - term_color(section, "PORTS SECTION", COLOR_YELLOW, 0, sizeof(section)); - else - term_color(section, "GENERAL SECTION", COLOR_YELLOW, 0, sizeof(section)); - - if (*def) - ast_cli(fd, "[%s] %s (Default: %s)\n\t%s\n", section, name, def, desc); - else - ast_cli(fd, "[%s] %s\n\t%s\n", section, name, desc); -} - -static int misdn_show_config (int fd, int argc, char *argv[]) -{ - char buffer[BUFFERSIZE]; - enum misdn_cfg_elements elem; - int linebreak; - int onlyport = -1; - int ok = 0; - - if (argc >= 4) { - if (!strcmp(argv[3], "description")) { - if (argc == 5) { - enum misdn_cfg_elements elem = misdn_cfg_get_elem(argv[4]); - if (elem == MISDN_CFG_FIRST) - ast_cli(fd, "Unknown element: %s\n", argv[4]); - else - show_config_description(fd, elem); - return 0; - } - return RESULT_SHOWUSAGE; - } - if (!strcmp(argv[3], "descriptions")) { - if ((argc == 4) || ((argc == 5) && !strcmp(argv[4], "general"))) { - for (elem = MISDN_GEN_FIRST + 1; elem < MISDN_GEN_LAST; ++elem) { - show_config_description(fd, elem); - ast_cli(fd, "\n"); - } - ok = 1; - } - if ((argc == 4) || ((argc == 5) && !strcmp(argv[4], "ports"))) { - for (elem = MISDN_CFG_FIRST + 1; elem < MISDN_CFG_LAST - 1 /* the ptp hack, remove the -1 when ptp is gone */; ++elem) { - show_config_description(fd, elem); - ast_cli(fd, "\n"); - } - ok = 1; - } - return ok ? 0 : RESULT_SHOWUSAGE; - } - if (!sscanf(argv[3], "%d", &onlyport) || onlyport < 0) { - ast_cli(fd, "Unknown option: %s\n", argv[3]); - return RESULT_SHOWUSAGE; - } - } - - if (argc == 3 || onlyport == 0) { - ast_cli(fd, "Misdn General-Config:\n"); - for (elem = MISDN_GEN_FIRST + 1, linebreak = 1; elem < MISDN_GEN_LAST; elem++, linebreak++) { - misdn_cfg_get_config_string(0, elem, buffer, BUFFERSIZE); - ast_cli(fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : ""); - } - ast_cli(fd, "\n"); - } - - if (onlyport < 0) { - int port = misdn_cfg_get_next_port(0); - for (; port > 0; port = misdn_cfg_get_next_port(port)) { - ast_cli(fd, "\n[PORT %d]\n", port); - for (elem = MISDN_CFG_FIRST + 1, linebreak = 1; elem < MISDN_CFG_LAST; elem++, linebreak++) { - misdn_cfg_get_config_string(port, elem, buffer, BUFFERSIZE); - ast_cli(fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : ""); - } - ast_cli(fd, "\n"); - } - } - - if (onlyport > 0) { - if (misdn_cfg_is_port_valid(onlyport)) { - ast_cli(fd, "[PORT %d]\n", onlyport); - for (elem = MISDN_CFG_FIRST + 1, linebreak = 1; elem < MISDN_CFG_LAST; elem++, linebreak++) { - misdn_cfg_get_config_string(onlyport, elem, buffer, BUFFERSIZE); - ast_cli(fd, "%-36s%s", buffer, !(linebreak % 2) ? "\n" : ""); - } - ast_cli(fd, "\n"); - } else { - ast_cli(fd, "Port %d is not active!\n", onlyport); - } - } - - return 0; -} - -struct state_struct { - enum misdn_chan_state state; - char txt[255]; -}; - -static struct state_struct state_array[] = { - {MISDN_NOTHING,"NOTHING"}, /* at beginning */ - {MISDN_WAITING4DIGS,"WAITING4DIGS"}, /* when waiting for infos */ - {MISDN_EXTCANTMATCH,"EXTCANTMATCH"}, /* when asterisk couldn't match our ext */ - {MISDN_INCOMING_SETUP,"INCOMING SETUP"}, /* when pbx_start */ - {MISDN_DIALING,"DIALING"}, /* when pbx_start */ - {MISDN_PROGRESS,"PROGRESS"}, /* when pbx_start */ - {MISDN_PROCEEDING,"PROCEEDING"}, /* when pbx_start */ - {MISDN_CALLING,"CALLING"}, /* when misdn_call is called */ - {MISDN_CALLING_ACKNOWLEDGE,"CALLING_ACKNOWLEDGE"}, /* when misdn_call is called */ - {MISDN_ALERTING,"ALERTING"}, /* when Alerting */ - {MISDN_BUSY,"BUSY"}, /* when BUSY */ - {MISDN_CONNECTED,"CONNECTED"}, /* when connected */ - {MISDN_PRECONNECTED,"PRECONNECTED"}, /* when connected */ - {MISDN_DISCONNECTED,"DISCONNECTED"}, /* when connected */ - {MISDN_RELEASED,"RELEASED"}, /* when connected */ - {MISDN_BRIDGED,"BRIDGED"}, /* when bridged */ - {MISDN_CLEANING,"CLEANING"}, /* when hangup from * but we were connected before */ - {MISDN_HUNGUP_FROM_MISDN,"HUNGUP_FROM_MISDN"}, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ - {MISDN_HOLDED,"HOLDED"}, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ - {MISDN_HOLD_DISCONNECT,"HOLD_DISCONNECT"}, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ - {MISDN_HUNGUP_FROM_AST,"HUNGUP_FROM_AST"} /* when DISCONNECT/RELEASE/REL_COMP came out of misdn_hangup */ -}; - -static const char *misdn_get_ch_state(struct chan_list *p) -{ - int i; - static char state[8]; - - if( !p) return NULL; - - for (i = 0; i < sizeof(state_array) / sizeof(struct state_struct); i++) { - if (state_array[i].state == p->state) - return state_array[i].txt; - } - - snprintf(state, sizeof(state), "%d", p->state) ; - - return state; -} - - - -static void reload_config(void) -{ - int i, cfg_debug; - - if (!g_config_initialized) { - ast_log(LOG_WARNING, "chan_misdn is not initialized properly, still reloading ?\n"); - return ; - } - - free_robin_list(); - misdn_cfg_reload(); - misdn_cfg_update_ptp(); - misdn_cfg_get(0, MISDN_GEN_TRACEFILE, global_tracefile, BUFFERSIZE); - misdn_cfg_get(0, MISDN_GEN_DEBUG, &cfg_debug, sizeof(int)); - - for (i = 0; i <= max_ports; i++) { - misdn_debug[i] = cfg_debug; - misdn_debug_only[i] = 0; - } -} - -static int misdn_reload (int fd, int argc, char *argv[]) -{ - ast_cli(fd, "Reloading mISDN configuration\n"); - reload_config(); - return 0; -} - -static void print_bc_info (int fd, struct chan_list *help, struct misdn_bchannel *bc) -{ - struct ast_channel *ast = help->ast; - ast_cli(fd, - "* Pid:%d Prt:%d Ch:%d Mode:%s Org:%s dad:%s oad:%s rad:%s ctx:%s state:%s\n", - - bc->pid, bc->port, bc->channel, - bc->nt ? "NT" : "TE", - help->originator == ORG_AST ? "*" : "I", - ast ? ast->exten : NULL, - ast ? ast->cid.cid_num : NULL, - bc->rad, - ast ? ast->context : NULL, - misdn_get_ch_state(help) - ); - if (misdn_debug[bc->port] > 0) - ast_cli(fd, - " --> astname: %s\n" - " --> ch_l3id: %x\n" - " --> ch_addr: %x\n" - " --> bc_addr: %x\n" - " --> bc_l3id: %x\n" - " --> display: %s\n" - " --> activated: %d\n" - " --> state: %s\n" - " --> capability: %s\n" -#ifdef MISDN_1_2 - " --> pipeline: %s\n" -#else - " --> echo_cancel: %d\n" -#endif - " --> notone : rx %d tx:%d\n" - " --> bc_hold: %d\n", - help->ast->name, - help->l3id, - help->addr, - bc->addr, - bc ? bc->l3_id : -1, - bc->display, - - bc->active, - bc_state2str(bc->bc_state), - bearer2str(bc->capability), -#ifdef MISDN_1_2 - bc->pipeline, -#else - bc->ec_enable, -#endif - - help->norxtone, help->notxtone, - bc->holded - ); - -} - -static int misdn_show_cls (int fd, int argc, char *argv[]) -{ - struct chan_list *help; - - help = cl_te; - - ast_cli(fd, "Channel List: %p\n", cl_te); - - for (; help; help = help->next) { - struct misdn_bchannel *bc = help->bc; - struct ast_channel *ast = help->ast; - if (!ast) { - if (!bc) { - ast_cli(fd, "chan_list obj. with l3id:%x has no bc and no ast Leg\n", help->l3id); - continue; - } - ast_cli(fd, "bc with pid:%d has no Ast Leg\n", bc->pid); - continue; - } - - if (misdn_debug[0] > 2) - ast_cli(fd, "Bc:%p Ast:%p\n", bc, ast); - if (bc) { - print_bc_info(fd, help, bc); - } else { - if (help->state == MISDN_HOLDED) { - ast_cli(fd, "ITS A HOLDED BC:\n"); - ast_cli(fd, " --> l3_id: %x\n" - " --> dad:%s oad:%s\n" - " --> hold_port: %d\n" - " --> hold_channel: %d\n", - help->l3id, - ast->exten, - ast->cid.cid_num, - help->hold_info.port, - help->hold_info.channel - ); - } else { - ast_cli(fd, "* Channel in unknown STATE !!! Exten:%s, Callerid:%s\n", ast->exten, ast->cid.cid_num); - } - } - } - - misdn_dump_chanlist(); - - return 0; -} - -static int misdn_show_cl (int fd, int argc, char *argv[]) -{ - struct chan_list *help; - - if (argc != 4) - return RESULT_SHOWUSAGE; - - help = cl_te; - - for (; help; help = help->next) { - struct misdn_bchannel *bc = help->bc; - struct ast_channel *ast = help->ast; - - if (bc && ast) { - if (!strcasecmp(ast->name,argv[3])) { - print_bc_info(fd, help, bc); - break; - } - } - } - - return 0; -} - -ast_mutex_t lock; -int MAXTICS = 8; - -static int misdn_set_tics (int fd, int argc, char *argv[]) -{ - if (argc != 4) - return RESULT_SHOWUSAGE; - - MAXTICS = atoi(argv[3]); - - return 0; -} - -static int misdn_show_stacks (int fd, int argc, char *argv[]) -{ - int port; - - ast_cli(fd, "BEGIN STACK_LIST:\n"); - for (port = misdn_cfg_get_next_port(0); port > 0; - port = misdn_cfg_get_next_port(port)) { - char buf[128]; - get_show_stack_details(port, buf); - ast_cli(fd, " %s Debug:%d%s\n", buf, misdn_debug[port], misdn_debug_only[port] ? "(only)" : ""); - } - - return 0; -} - -static int misdn_show_ports_stats (int fd, int argc, char *argv[]) -{ - int port; - - ast_cli(fd, "Port\tin_calls\tout_calls\n"); - for (port = misdn_cfg_get_next_port(0); port > 0; - port = misdn_cfg_get_next_port(port)) { - ast_cli(fd, "%d\t%d\t\t%d\n", port, misdn_in_calls[port], misdn_out_calls[port]); - } - ast_cli(fd, "\n"); - - return 0; -} - -static int misdn_show_port (int fd, int argc, char *argv[]) -{ - int port; - char buf[128]; - - if (argc != 4) - return RESULT_SHOWUSAGE; - - port = atoi(argv[3]); - - ast_cli(fd, "BEGIN STACK_LIST:\n"); - get_show_stack_details(port, buf); - ast_cli(fd, " %s Debug:%d%s\n", buf, misdn_debug[port], misdn_debug_only[port] ? "(only)" : ""); - - return 0; -} - -static int misdn_send_cd (int fd, int argc, char *argv[]) -{ - char *channame; - char *nr; - struct chan_list *tmp; - - if (argc != 5) - return RESULT_SHOWUSAGE; - - - { - channame = argv[3]; - nr = argv[4]; - - ast_cli(fd, "Sending Calldeflection (%s) to %s\n", nr, channame); - tmp = get_chan_by_ast_name(channame); - if (!tmp) { - ast_cli(fd, "Sending CD with nr %s to %s failed: Channel does not exist.\n",nr, channame); - return 0; - } - - if (strlen(nr) >= 15) { - ast_cli(fd, "Sending CD with nr %s to %s failed: Number too long (up to 15 digits are allowed).\n",nr, channame); - return 0; - } - tmp->bc->fac_out.Function = Fac_CD; - ast_copy_string((char *)tmp->bc->fac_out.u.CDeflection.DeflectedToNumber, nr, sizeof(tmp->bc->fac_out.u.CDeflection.DeflectedToNumber)); - misdn_lib_send_event(tmp->bc, EVENT_FACILITY); - } - - return 0; -} - -static int misdn_send_restart(int fd, int argc, char *argv[]) -{ - int port; - int channel; - - if (argc < 4 || argc > 5) - return RESULT_SHOWUSAGE; - - port = atoi(argv[3]); - - if (argc == 5) { - channel = atoi(argv[4]); - misdn_lib_send_restart(port, channel); - } else { - misdn_lib_send_restart(port, -1); - } - - return 0; -} - -static int misdn_send_digit (int fd, int argc, char *argv[]) -{ - char *channame; - char *msg; - struct chan_list *tmp; - int i, msglen; - - if (argc != 5) - return RESULT_SHOWUSAGE; - - channame = argv[3]; - msg = argv[4]; - msglen = strlen(msg); - - ast_cli(fd, "Sending %s to %s\n", msg, channame); - - tmp = get_chan_by_ast_name(channame); - if (!tmp) { - ast_cli(fd, "Sending %s to %s failed Channel does not exist\n", msg, channame); - return 0; - } -#if 1 - for (i = 0; i < msglen; i++) { - ast_cli(fd, "Sending: %c\n", msg[i]); - send_digit_to_chan(tmp, msg[i]); - /* res = ast_safe_sleep(tmp->ast, 250); */ - usleep(250000); - /* res = ast_waitfor(tmp->ast,100); */ - } -#else - ast_dtmf_stream(tmp->ast, NULL, msg, 250); -#endif - - return 0; -} - -static int misdn_toggle_echocancel (int fd, int argc, char *argv[]) -{ - char *channame; - struct chan_list *tmp; - - if (argc != 4) - return RESULT_SHOWUSAGE; - - channame = argv[3]; - - ast_cli(fd, "Toggling EchoCancel on %s\n", channame); - - tmp = get_chan_by_ast_name(channame); - if (!tmp) { - ast_cli(fd, "Toggling EchoCancel %s failed Channel does not exist\n", channame); - return 0; - } - - tmp->toggle_ec = tmp->toggle_ec ? 0 : 1; - - if (tmp->toggle_ec) { -#ifdef MISDN_1_2 - update_pipeline_config(tmp->bc); -#else - update_ec_config(tmp->bc); -#endif - manager_ec_enable(tmp->bc); - } else { - manager_ec_disable(tmp->bc); - } - - return 0; -} - -static int misdn_send_display (int fd, int argc, char *argv[]) -{ - char *channame; - char *msg; - struct chan_list *tmp; - - if (argc != 5) - return RESULT_SHOWUSAGE; - - channame = argv[3]; - msg = argv[4]; - - ast_cli(fd, "Sending %s to %s\n", msg, channame); - tmp = get_chan_by_ast_name(channame); - - if (tmp && tmp->bc) { - ast_copy_string(tmp->bc->display, msg, sizeof(tmp->bc->display)); - misdn_lib_send_event(tmp->bc, EVENT_INFORMATION); - } else { - ast_cli(fd, "No such channel %s\n", channame); - return RESULT_FAILURE; - } - - return RESULT_SUCCESS; -} - -static char *complete_ch_helper(const char *line, const char *word, int pos, int state, int rpos) -{ - struct ast_channel *c; - int which=0; - char *ret; - if (pos != rpos) - return NULL; - c = ast_channel_walk_locked(NULL); - while(c) { - if (!strncasecmp(word, c->name, strlen(word))) { - if (++which > state) - break; - } - ast_mutex_unlock(&c->lock); - c = ast_channel_walk_locked(c); - } - if (c) { - ret = strdup(c->name); - ast_mutex_unlock(&c->lock); - } else - ret = NULL; - return ret; -} - -static char *complete_ch(const char *line, const char *word, int pos, int state) -{ - return complete_ch_helper(line, word, pos, state, 3); -} - -static char *complete_debug_port (const char *line, const char *word, int pos, int state) -{ - if (state) - return NULL; - - switch (pos) { - case 4: - if (*word == 'p') - return strdup("port"); - else if (*word == 'o') - return strdup("only"); - break; - case 6: - if (*word == 'o') - return strdup("only"); - break; - } - return NULL; -} - -static char *complete_show_config (const char *line, const char *word, int pos, int state) -{ - char buffer[BUFFERSIZE]; - enum misdn_cfg_elements elem; - int wordlen = strlen(word); - int which = 0; - int port = 0; - - switch (pos) { - case 3: - if ((!strncmp(word, "description", wordlen)) && (++which > state)) - return strdup("description"); - if ((!strncmp(word, "descriptions", wordlen)) && (++which > state)) - return strdup("descriptions"); - if ((!strncmp(word, "0", wordlen)) && (++which > state)) - return strdup("0"); - while ((port = misdn_cfg_get_next_port(port)) != -1) { - snprintf(buffer, sizeof(buffer), "%d", port); - if ((!strncmp(word, buffer, wordlen)) && (++which > state)) { - return strdup(buffer); - } - } - break; - case 4: - if (strstr(line, "description ")) { - for (elem = MISDN_CFG_FIRST + 1; elem < MISDN_GEN_LAST; ++elem) { - if ((elem == MISDN_CFG_LAST) || (elem == MISDN_GEN_FIRST)) - continue; - misdn_cfg_get_name(elem, buffer, BUFFERSIZE); - if (!wordlen || !strncmp(word, buffer, wordlen)) { - if (++which > state) - return strdup(buffer); - } - } - } else if (strstr(line, "descriptions ")) { - if ((!wordlen || !strncmp(word, "general", wordlen)) && (++which > state)) - return strdup("general"); - if ((!wordlen || !strncmp(word, "ports", wordlen)) && (++which > state)) - return strdup("ports"); - } - break; - } - return NULL; -} - -static struct ast_cli_entry chan_misdn_clis[] = { - { {"misdn","send","calldeflect", NULL}, misdn_send_cd, "Sends CallDeflection to mISDN Channel", - "Usage: misdn send calldeflect <channel> \"<nr>\" \n", complete_ch }, - { {"misdn","send","digit", NULL}, misdn_send_digit, "Sends DTMF Digit to mISDN Channel", - "Usage: misdn send digit <channel> \"<msg>\" \n" - " Send <digit> to <channel> as DTMF Tone\n" - " when channel is a mISDN channel\n", complete_ch }, - { {"misdn","toggle","echocancel", NULL}, misdn_toggle_echocancel, "Toggles EchoCancel on mISDN Channel", - "Usage: misdn toggle echocancel <channel>\n", complete_ch }, - { {"misdn","send","display", NULL}, misdn_send_display, "Sends Text to mISDN Channel", - "Usage: misdn send display <channel> \"<msg>\" \n" - " Send <msg> to <channel> as Display Message\n" - " when channel is a mISDN channel\n", complete_ch }, - { {"misdn","show","config", NULL}, misdn_show_config, "Shows internal mISDN config, read from cfg-file", - "Usage: misdn show config [<port> | description <config element> | descriptions [general|ports]]\n" - " Use 0 for <port> to only print the general config.\n", complete_show_config }, - { {"misdn","reload", NULL}, misdn_reload, "Reloads internal mISDN config, read from cfg-file", - "Usage: misdn reload\n" }, - { {"misdn","set","tics", NULL}, misdn_set_tics, "", - "\n" }, - { {"misdn","show","channels", NULL}, misdn_show_cls, "Shows internal mISDN chan_list", - "Usage: misdn show channels\n" }, - { {"misdn","show","channel", NULL}, misdn_show_cl, "Shows internal mISDN chan_list", - "Usage: misdn show channels\n", complete_ch }, - { {"misdn","port","block", NULL}, misdn_port_block, "Blocks the given port", - "Usage: misdn port block\n" }, - { {"misdn","port","unblock", NULL}, misdn_port_unblock, "Unblocks the given port", - "Usage: misdn port unblock\n" }, - { {"misdn","restart","port", NULL}, misdn_restart_port, "Restarts the given port", - "Usage: misdn restart port\n" }, - { {"misdn","restart","pid", NULL}, misdn_restart_pid, "Restarts the given pid", - "Usage: misdn restart pid\n" }, - { {"misdn","send","restart", NULL}, misdn_send_restart, - "Sends a restart for every bchannel on the given port", - "Usage: misdn send restart <port>\n"}, - { {"misdn","port","up", NULL}, misdn_port_up, "Tries to establish L1 on the given port", - "Usage: misdn port up <port>\n" }, - { {"misdn","port","down", NULL}, misdn_port_down, "Tries to deactivate the L1 on the given port", - "Usage: misdn port down <port>\n" }, - { {"misdn","show","stacks", NULL}, misdn_show_stacks, "Shows internal mISDN stack_list", - "Usage: misdn show stacks\n" }, - { {"misdn","show","ports","stats", NULL}, misdn_show_ports_stats, "Shows chan_misdns call statistics per port", - "Usage: misdn show port stats\n" }, - { {"misdn","show","port", NULL}, misdn_show_port, "Shows detailed information for given port", - "Usage: misdn show port <port>\n" }, - { {"misdn","set","debug", NULL}, misdn_set_debug, "Sets Debuglevel of chan_misdn", - "Usage: misdn set debug <level> [only] | [port <port> [only]]\n", complete_debug_port }, - { {"misdn","set","crypt","debug", NULL}, misdn_set_crypt_debug, "Sets CryptDebuglevel of chan_misdn, at the moment, level={1,2}", - "Usage: misdn set crypt debug <level>\n" } -}; - -/*! \brief Updates caller ID information from config */ -static int update_config(struct chan_list *ch, int orig) -{ - struct ast_channel *ast; - struct misdn_bchannel *bc; - int port, hdlc = 0; - int pres, screen; - - if (!ch) { - ast_log(LOG_WARNING, "Cannot configure without chanlist\n"); - return -1; - } - - ast = ch->ast; - bc = ch->bc; - if (! ast || ! bc) { - ast_log(LOG_WARNING, "Cannot configure without ast || bc\n"); - return -1; - } - - port = bc->port; - - chan_misdn_log(7, port, "update_config: Getting Config\n"); - - misdn_cfg_get(port, MISDN_CFG_HDLC, &hdlc, sizeof(int)); - - if (hdlc) { - switch (bc->capability) { - case INFO_CAPABILITY_DIGITAL_UNRESTRICTED: - case INFO_CAPABILITY_DIGITAL_RESTRICTED: - chan_misdn_log(1, bc->port, " --> CONF HDLC\n"); - bc->hdlc = 1; - break; - } - } - - - misdn_cfg_get(port, MISDN_CFG_PRES, &pres, sizeof(pres)); - misdn_cfg_get(port, MISDN_CFG_SCREEN, &screen, sizeof(screen)); - chan_misdn_log(2, port, " --> pres: %d screen: %d\n", pres, screen); - - if (pres < 0 || screen < 0) { - chan_misdn_log(2, port, " --> pres: %x\n", ast->cid.cid_pres); - - switch (ast->cid.cid_pres & 0x60) { - case AST_PRES_RESTRICTED: - bc->pres = 1; - chan_misdn_log(2, port, " --> PRES: Restricted (1)\n"); - break; - case AST_PRES_UNAVAILABLE: - bc->pres = 2; - chan_misdn_log(2, port, " --> PRES: Unavailable (2)\n"); - break; - default: - bc->pres = 0; - chan_misdn_log(2, port, " --> PRES: Allowed (0)\n"); - break; - } - - switch (ast->cid.cid_pres & 0x3) { - default: - case AST_PRES_USER_NUMBER_UNSCREENED: - bc->screen = 0; - chan_misdn_log(2, port, " --> SCREEN: Unscreened (0)\n"); - break; - case AST_PRES_USER_NUMBER_PASSED_SCREEN: - bc->screen = 1; - chan_misdn_log(2, port, " --> SCREEN: Passed Screen (1)\n"); - break; - case AST_PRES_USER_NUMBER_FAILED_SCREEN: - bc->screen = 2; - chan_misdn_log(2, port, " --> SCREEN: Failed Screen (2)\n"); - break; - case AST_PRES_NETWORK_NUMBER: - bc->screen = 3; - chan_misdn_log(2, port, " --> SCREEN: Network Nr. (3)\n"); - break; - } - } else { - bc->screen = screen; - bc->pres = pres; - } - - return 0; -} - - -static void config_jitterbuffer(struct chan_list *ch) -{ - struct misdn_bchannel *bc = ch->bc; - int len = ch->jb_len, threshold = ch->jb_upper_threshold; - - chan_misdn_log(5, bc->port, "config_jb: Called\n"); - - if (! len) { - chan_misdn_log(1, bc->port, "config_jb: Deactivating Jitterbuffer\n"); - bc->nojitter=1; - } else { - if (len <= 100 || len > 8000) { - chan_misdn_log(0, bc->port, "config_jb: Jitterbuffer out of Bounds, setting to 1000\n"); - len = 1000; - } - - if ( threshold > len ) { - chan_misdn_log(0, bc->port, "config_jb: Jitterbuffer Threshold > Jitterbuffer setting to Jitterbuffer -1\n"); - } - - if ( ch->jb) { - cb_log(0, bc->port, "config_jb: We've got a Jitterbuffer Already on this port.\n"); - misdn_jb_destroy(ch->jb); - ch->jb = NULL; - } - - ch->jb=misdn_jb_init(len, threshold); - - if (!ch->jb ) - bc->nojitter = 1; - } -} - - -void debug_numplan(int port, int numplan, char *type) -{ - switch (numplan) { - case NUMPLAN_INTERNATIONAL: - chan_misdn_log(2, port, " --> %s: International\n", type); - break; - case NUMPLAN_NATIONAL: - chan_misdn_log(2, port, " --> %s: National\n", type); - break; - case NUMPLAN_SUBSCRIBER: - chan_misdn_log(2, port, " --> %s: Subscriber\n", type); - break; - case NUMPLAN_UNKNOWN: - chan_misdn_log(2, port, " --> %s: Unknown\n", type); - break; - /* Maybe we should cut off the prefix if present ? */ - default: - chan_misdn_log(0, port, " --> !!!! Wrong dialplan setting, please see the misdn.conf sample file\n "); - break; - } -} - - -#ifdef MISDN_1_2 -static int update_pipeline_config(struct misdn_bchannel *bc) -{ - int ec; - - misdn_cfg_get(bc->port, MISDN_CFG_PIPELINE, bc->pipeline, sizeof(bc->pipeline)); - - if (*bc->pipeline) - return 0; - - misdn_cfg_get(bc->port, MISDN_CFG_ECHOCANCEL, &ec, sizeof(int)); - if (ec == 1) - ast_copy_string(bc->pipeline, "mg2ec", sizeof(bc->pipeline)); - else if (ec > 1) - snprintf(bc->pipeline, sizeof(bc->pipeline), "mg2ec(deftaps=%d)", ec); - - return 0; -} -#else -static int update_ec_config(struct misdn_bchannel *bc) -{ - int ec; - int port = bc->port; - - misdn_cfg_get(port, MISDN_CFG_ECHOCANCEL, &ec, sizeof(int)); - - if (ec == 1) { - bc->ec_enable = 1; - } else if (ec > 1) { - bc->ec_enable = 1; - bc->ec_deftaps = ec; - } - - return 0; -} -#endif - - -static int read_config(struct chan_list *ch, int orig) -{ - struct ast_channel *ast; - struct misdn_bchannel *bc; - int port; - int hdlc = 0; - char lang[BUFFERSIZE + 1]; - char faxdetect[BUFFERSIZE + 1]; - char buf[256]; - char buf2[256]; - ast_group_t pg; - ast_group_t cg; - - if (!ch) { - ast_log(LOG_WARNING, "Cannot configure without chanlist\n"); - return -1; - } - - ast = ch->ast; - bc = ch->bc; - if (! ast || ! bc) { - ast_log(LOG_WARNING, "Cannot configure without ast || bc\n"); - return -1; - } - - port = bc->port; - chan_misdn_log(1, port, "read_config: Getting Config\n"); - - misdn_cfg_get(port, MISDN_CFG_LANGUAGE, lang, BUFFERSIZE); - ast_string_field_set(ast, language, lang); - - misdn_cfg_get(port, MISDN_CFG_MUSICCLASS, ch->mohinterpret, sizeof(ch->mohinterpret)); - - misdn_cfg_get(port, MISDN_CFG_TXGAIN, &bc->txgain, sizeof(int)); - misdn_cfg_get(port, MISDN_CFG_RXGAIN, &bc->rxgain, sizeof(int)); - - misdn_cfg_get(port, MISDN_CFG_INCOMING_EARLY_AUDIO, &ch->incoming_early_audio, sizeof(int)); - - misdn_cfg_get(port, MISDN_CFG_SENDDTMF, &bc->send_dtmf, sizeof(int)); - - misdn_cfg_get(port, MISDN_CFG_ASTDTMF, &ch->ast_dsp, sizeof(int)); - - if (ch->ast_dsp) { - ch->ignore_dtmf = 1; - } - - misdn_cfg_get(port, MISDN_CFG_NEED_MORE_INFOS, &bc->need_more_infos, sizeof(int)); - misdn_cfg_get(port, MISDN_CFG_NTTIMEOUT, &ch->nttimeout, sizeof(int)); - - misdn_cfg_get(port, MISDN_CFG_NOAUTORESPOND_ON_SETUP, &ch->noautorespond_on_setup, sizeof(int)); - - misdn_cfg_get(port, MISDN_CFG_FAR_ALERTING, &ch->far_alerting, sizeof(int)); - - misdn_cfg_get(port, MISDN_CFG_ALLOWED_BEARERS, &ch->allowed_bearers, BUFFERSIZE); - - misdn_cfg_get(port, MISDN_CFG_FAXDETECT, faxdetect, BUFFERSIZE); - - misdn_cfg_get(port, MISDN_CFG_HDLC, &hdlc, sizeof(int)); - - if (hdlc) { - switch (bc->capability) { - case INFO_CAPABILITY_DIGITAL_UNRESTRICTED: - case INFO_CAPABILITY_DIGITAL_RESTRICTED: - chan_misdn_log(1, bc->port, " --> CONF HDLC\n"); - bc->hdlc = 1; - break; - } - - } - /*Initialize new Jitterbuffer*/ - misdn_cfg_get(port, MISDN_CFG_JITTERBUFFER, &ch->jb_len, sizeof(int)); - misdn_cfg_get(port, MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD, &ch->jb_upper_threshold, sizeof(int)); - - config_jitterbuffer(ch); - - misdn_cfg_get(bc->port, MISDN_CFG_CONTEXT, ch->context, sizeof(ch->context)); - - ast_copy_string(ast->context, ch->context, sizeof(ast->context)); - -#ifdef MISDN_1_2 - update_pipeline_config(bc); -#else - update_ec_config(bc); -#endif - - { - int eb3; - - misdn_cfg_get( bc->port, MISDN_CFG_EARLY_BCONNECT, &eb3, sizeof(int)); - bc->early_bconnect=eb3; - } - - misdn_cfg_get(port, MISDN_CFG_PICKUPGROUP, &pg, sizeof(pg)); - misdn_cfg_get(port, MISDN_CFG_CALLGROUP, &cg, sizeof(cg)); - - chan_misdn_log(5, port, " --> * CallGrp:%s PickupGrp:%s\n", ast_print_group(buf, sizeof(buf), cg), ast_print_group(buf2, sizeof(buf2), pg)); - ast->pickupgroup = pg; - ast->callgroup = cg; - - if (orig == ORG_AST) { - char callerid[BUFFERSIZE + 1]; - - /* ORIGINATOR Asterisk (outgoing call) */ - - misdn_cfg_get(port, MISDN_CFG_TE_CHOOSE_CHANNEL, &(bc->te_choose_channel), sizeof(int)); - - if (strstr(faxdetect, "outgoing") || strstr(faxdetect, "both")) { - if (strstr(faxdetect, "nojump")) - ch->faxdetect = 2; - else - ch->faxdetect = 1; - } - - misdn_cfg_get(port, MISDN_CFG_CALLERID, callerid, BUFFERSIZE); - if ( ! ast_strlen_zero(callerid) ) { - chan_misdn_log(1, port, " --> * Setting Cid to %s\n", callerid); - ast_copy_string(bc->oad, callerid, sizeof(bc->oad)); - } - - misdn_cfg_get(port, MISDN_CFG_DIALPLAN, &bc->dnumplan, sizeof(int)); - misdn_cfg_get(port, MISDN_CFG_LOCALDIALPLAN, &bc->onumplan, sizeof(int)); - misdn_cfg_get(port, MISDN_CFG_CPNDIALPLAN, &bc->cpnnumplan, sizeof(int)); - debug_numplan(port, bc->dnumplan, "TON"); - debug_numplan(port, bc->onumplan, "LTON"); - debug_numplan(port, bc->cpnnumplan, "CTON"); - - ch->overlap_dial = 0; - } else { - /* ORIGINATOR MISDN (incoming call) */ - char prefix[BUFFERSIZE + 1] = ""; - - if (strstr(faxdetect, "incoming") || strstr(faxdetect, "both")) { - if (strstr(faxdetect, "nojump")) - ch->faxdetect = 2; - else - ch->faxdetect = 1; - } - - misdn_cfg_get(port, MISDN_CFG_CPNDIALPLAN, &bc->cpnnumplan, sizeof(int)); - debug_numplan(port, bc->cpnnumplan, "CTON"); - - switch (bc->onumplan) { - case NUMPLAN_INTERNATIONAL: - misdn_cfg_get(bc->port, MISDN_CFG_INTERNATPREFIX, prefix, BUFFERSIZE); - break; - - case NUMPLAN_NATIONAL: - misdn_cfg_get(bc->port, MISDN_CFG_NATPREFIX, prefix, BUFFERSIZE); - break; - default: - break; - } - - ast_copy_string(buf, bc->oad, sizeof(buf)); - snprintf(bc->oad, sizeof(bc->oad), "%s%s", prefix, buf); - - if (!ast_strlen_zero(bc->dad)) { - ast_copy_string(bc->orig_dad, bc->dad, sizeof(bc->orig_dad)); - } - - if ( ast_strlen_zero(bc->dad) && !ast_strlen_zero(bc->keypad)) { - ast_copy_string(bc->dad, bc->keypad, sizeof(bc->dad)); - } - - prefix[0] = 0; - - switch (bc->dnumplan) { - case NUMPLAN_INTERNATIONAL: - misdn_cfg_get(bc->port, MISDN_CFG_INTERNATPREFIX, prefix, BUFFERSIZE); - break; - case NUMPLAN_NATIONAL: - misdn_cfg_get(bc->port, MISDN_CFG_NATPREFIX, prefix, BUFFERSIZE); - break; - default: - break; - } - - ast_copy_string(buf, bc->dad, sizeof(buf)); - snprintf(bc->dad, sizeof(bc->dad), "%s%s", prefix, buf); - - if (strcmp(bc->dad, ast->exten)) { - ast_copy_string(ast->exten, bc->dad, sizeof(ast->exten)); - } - - ast_set_callerid(ast, bc->oad, NULL, bc->oad); - - if ( !ast_strlen_zero(bc->rad) ) { - if (ast->cid.cid_rdnis) - free(ast->cid.cid_rdnis); - ast->cid.cid_rdnis = strdup(bc->rad); - } - - misdn_cfg_get(bc->port, MISDN_CFG_OVERLAP_DIAL, &ch->overlap_dial, sizeof(ch->overlap_dial)); - ast_mutex_init(&ch->overlap_tv_lock); - } /* ORIG MISDN END */ - - ch->overlap_dial_task = -1; - - if (ch->faxdetect || ch->ast_dsp) { - misdn_cfg_get(port, MISDN_CFG_FAXDETECT_TIMEOUT, &ch->faxdetect_timeout, sizeof(ch->faxdetect_timeout)); - if (!ch->dsp) - ch->dsp = ast_dsp_new(); - if (ch->dsp) { - if (ch->faxdetect) - ast_dsp_set_features(ch->dsp, DSP_FEATURE_DTMF_DETECT | DSP_FEATURE_FAX_DETECT); - else - ast_dsp_set_features(ch->dsp, DSP_FEATURE_DTMF_DETECT ); - } - if (!ch->trans) - ch->trans = ast_translator_build_path(AST_FORMAT_SLINEAR, AST_FORMAT_ALAW); - } - - /* AOCD initialization */ - bc->AOCDtype = Fac_None; - - return 0; -} - - -/*****************************/ -/*** AST Indications Start ***/ -/*****************************/ - -static int misdn_call(struct ast_channel *ast, char *dest, int timeout) -{ - int port = 0; - int r; - int exceed; - int bridging; - struct chan_list *ch; - struct misdn_bchannel *newbc; - char *opts, *ext; - char *dest_cp; - - if (!ast) { - ast_log(LOG_WARNING, " --> ! misdn_call called on ast_channel *ast where ast == NULL\n"); - return -1; - } - - if (((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) || !dest ) { - ast_log(LOG_WARNING, " --> ! misdn_call called on %s, neither down nor reserved (or dest==NULL)\n", ast->name); - ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; - ast_setstate(ast, AST_STATE_DOWN); - return -1; - } - - ch = MISDN_ASTERISK_TECH_PVT(ast); - if (!ch) { - ast_log(LOG_WARNING, " --> ! misdn_call called on %s, neither down nor reserved (or dest==NULL)\n", ast->name); - ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; - ast_setstate(ast, AST_STATE_DOWN); - return -1; - } - - newbc = ch->bc; - if (!newbc) { - ast_log(LOG_WARNING, " --> ! misdn_call called on %s, neither down nor reserved (or dest==NULL)\n", ast->name); - ast->hangupcause = AST_CAUSE_NORMAL_TEMPORARY_FAILURE; - ast_setstate(ast, AST_STATE_DOWN); - return -1; - } - - /* - * dest is ---v - * Dial(mISDN/g:group_name[/extension[/options]]) - * Dial(mISDN/port[:preselected_channel][/extension[/options]]) - * - * The dial extension could be empty if you are using MISDN_KEYPAD - * to control ISDN provider features. - */ - dest_cp = ast_strdupa(dest); - strsep(&dest_cp, "/");/* Discard port/group token */ - ext = strsep(&dest_cp, "/"); - if (!ext) { - ext = ""; - } - opts = dest_cp; - - port = newbc->port; - - if ((exceed = add_out_calls(port))) { - char tmp[16]; - snprintf(tmp, sizeof(tmp), "%d", exceed); - pbx_builtin_setvar_helper(ast, "MAX_OVERFLOW", tmp); - return -1; - } - - chan_misdn_log(1, port, "* CALL: %s\n", dest); - - chan_misdn_log(2, port, " --> * dad:%s tech:%s ctx:%s\n", ast->exten, ast->name, ast->context); - - chan_misdn_log(3, port, " --> * adding2newbc ext %s\n", ast->exten); - if (ast->exten) { - ast_copy_string(ast->exten, ext, sizeof(ast->exten)); - ast_copy_string(newbc->dad, ext, sizeof(newbc->dad)); - } - - ast_copy_string(newbc->rad, S_OR(ast->cid.cid_rdnis, ""), sizeof(newbc->rad)); - - chan_misdn_log(3, port, " --> * adding2newbc callerid %s\n", ast->cid.cid_num); - if (ast_strlen_zero(newbc->oad) && !ast_strlen_zero(ast->cid.cid_num)) { - ast_copy_string(newbc->oad, ast->cid.cid_num, sizeof(newbc->oad)); - } - - newbc->capability = ast->transfercapability; - pbx_builtin_setvar_helper(ast, "TRANSFERCAPABILITY", ast_transfercapability2str(newbc->capability)); - if ( ast->transfercapability == INFO_CAPABILITY_DIGITAL_UNRESTRICTED) { - chan_misdn_log(2, port, " --> * Call with flag Digital\n"); - } - - /* update screening and presentation */ - update_config(ch, ORG_AST); - - /* fill in some ies from channel vary*/ - import_ch(ast, newbc, ch); - - /* Finally The Options Override Everything */ - if (opts) - misdn_set_opt_exec(ast, opts); - else - chan_misdn_log(2, port, "NO OPTS GIVEN\n"); - - /*check for bridging*/ - misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(bridging)); - if (bridging && ch->other_ch) { -#ifdef MISDN_1_2 - chan_misdn_log(1, port, "Disabling EC (aka Pipeline) on both Sides\n"); - *ch->bc->pipeline = 0; - *ch->other_ch->bc->pipeline = 0; -#else - chan_misdn_log(1, port, "Disabling EC on both Sides\n"); - ch->bc->ec_enable = 0; - ch->other_ch->bc->ec_enable = 0; -#endif - } - - r = misdn_lib_send_event( newbc, EVENT_SETUP ); - - /** we should have l3id after sending setup **/ - ch->l3id = newbc->l3_id; - - if ( r == -ENOCHAN ) { - chan_misdn_log(0, port, " --> * Theres no Channel at the moment .. !\n"); - chan_misdn_log(1, port, " --> * SEND: State Down pid:%d\n", newbc ? newbc->pid : -1); - ast->hangupcause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION; - ast_setstate(ast, AST_STATE_DOWN); - return -1; - } - - chan_misdn_log(2, port, " --> * SEND: State Dialing pid:%d\n", newbc ? newbc->pid : 1); - - ast_setstate(ast, AST_STATE_DIALING); - ast->hangupcause = AST_CAUSE_NORMAL_CLEARING; - - if (newbc->nt) - stop_bc_tones(ch); - - ch->state = MISDN_CALLING; - - return 0; -} - - -static int misdn_answer(struct ast_channel *ast) -{ - struct chan_list *p; - const char *tmp; - - if (!ast || !(p = MISDN_ASTERISK_TECH_PVT(ast))) return -1; - - chan_misdn_log(1, p ? (p->bc ? p->bc->port : 0) : 0, "* ANSWER:\n"); - - if (!p) { - ast_log(LOG_WARNING, " --> Channel not connected ??\n"); - ast_queue_hangup(ast); - } - - if (!p->bc) { - chan_misdn_log(1, 0, " --> Got Answer, but there is no bc obj ??\n"); - - ast_queue_hangup(ast); - } - - tmp = pbx_builtin_getvar_helper(p->ast, "CRYPT_KEY"); - if (!ast_strlen_zero(tmp)) { - chan_misdn_log(1, p->bc->port, " --> Connection will be BF crypted\n"); - ast_copy_string(p->bc->crypt_key, tmp, sizeof(p->bc->crypt_key)); - } else { - chan_misdn_log(3, p->bc->port, " --> Connection is without BF encryption\n"); - } - - tmp = pbx_builtin_getvar_helper(ast, "MISDN_DIGITAL_TRANS"); - if (!ast_strlen_zero(tmp) && ast_true(tmp)) { - chan_misdn_log(1, p->bc->port, " --> Connection is transparent digital\n"); - p->bc->nodsp = 1; - p->bc->hdlc = 0; - p->bc->nojitter = 1; - } - - p->state = MISDN_CONNECTED; - stop_indicate(p); - - if ( ast_strlen_zero(p->bc->cad) ) { - chan_misdn_log(2,p->bc->port," --> empty cad using dad\n"); - ast_copy_string(p->bc->cad, p->bc->dad, sizeof(p->bc->cad)); - } - - misdn_lib_send_event( p->bc, EVENT_CONNECT); - start_bc_tones(p); - - return 0; -} - -static int misdn_digit_begin(struct ast_channel *chan, char digit) -{ - /* XXX Modify this callback to support Asterisk controlling the length of DTMF */ - return 0; -} - -static int misdn_digit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ - struct chan_list *p; - struct misdn_bchannel *bc; - - if (!ast || !(p = MISDN_ASTERISK_TECH_PVT(ast))) return -1; - - bc = p->bc; - chan_misdn_log(1, bc ? bc->port : 0, "* IND : Digit %c\n", digit); - - if (!bc) { - ast_log(LOG_WARNING, " --> !! Got Digit Event without having bchannel Object\n"); - return -1; - } - - switch (p->state ) { - case MISDN_CALLING: - { - int l; - char buf[8]; - buf[0]=digit; - buf[1]=0; - - l = sizeof(bc->infos_pending); - strncat(bc->infos_pending, buf, l - strlen(bc->infos_pending) - 1); - } - break; - case MISDN_CALLING_ACKNOWLEDGE: - { - bc->info_dad[0]=digit; - bc->info_dad[1]=0; - - { - int l = sizeof(bc->dad); - strncat(bc->dad, bc->info_dad, l - strlen(bc->dad) - 1); - } - { - int l = sizeof(p->ast->exten); - strncpy(p->ast->exten, bc->dad, l); - p->ast->exten[l-1] = 0; - } - - misdn_lib_send_event( bc, EVENT_INFORMATION); - } - break; - default: - /* Do not send Digits in CONNECTED State, when - * the other side is too mISDN. */ - if (p->other_ch ) - return 0; - - if ( bc->send_dtmf ) - send_digit_to_chan(p,digit); - break; - } - - return 0; -} - - -static int misdn_fixup(struct ast_channel *oldast, struct ast_channel *ast) -{ - struct chan_list *p; - - if (!ast || ! (p=MISDN_ASTERISK_TECH_PVT(ast) )) return -1; - - chan_misdn_log(1, p->bc ? p->bc->port : 0, "* IND: Got Fixup State:%s L3id:%x\n", misdn_get_ch_state(p), p->l3id); - - p->ast = ast; - - return 0; -} - - - -static int misdn_indication(struct ast_channel *ast, int cond, const void *data, size_t datalen) -{ - struct chan_list *p; - - if (!ast || ! (p=MISDN_ASTERISK_TECH_PVT(ast))) { - ast_log(LOG_WARNING, "Returned -1 in misdn_indication\n"); - return -1; - } - - if (!p->bc ) { - chan_misdn_log(1, 0, "* IND : Indication from %s\n", ast->exten); - ast_log(LOG_WARNING, "Private Pointer but no bc ?\n"); - return -1; - } - - chan_misdn_log(5, p->bc->port, "* IND : Indication [%d] from %s\n", cond, ast->exten); - - switch (cond) { - case AST_CONTROL_BUSY: - chan_misdn_log(1, p->bc->port, "* IND :\tbusy pid:%d\n", p->bc ? p->bc->pid : -1); - ast_setstate(ast, AST_STATE_BUSY); - - p->bc->out_cause = AST_CAUSE_USER_BUSY; - if (p->state != MISDN_CONNECTED) { - start_bc_tones(p); - misdn_lib_send_event( p->bc, EVENT_DISCONNECT); - } else { - chan_misdn_log(-1, p->bc->port, " --> !! Got Busy in Connected State !?! ast:%s\n", ast->name); - } - return -1; - case AST_CONTROL_RING: - chan_misdn_log(1, p->bc->port, "* IND :\tring pid:%d\n", p->bc ? p->bc->pid : -1); - return -1; - case AST_CONTROL_RINGING: - chan_misdn_log(1, p->bc->port, "* IND :\tringing pid:%d\n", p->bc ? p->bc->pid : -1); - switch (p->state) { - case MISDN_ALERTING: - chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but I was Ringing before, so ignoring it\n", p->bc ? p->bc->pid : -1); - break; - case MISDN_CONNECTED: - chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d but Connected, so just send TONE_ALERTING without state changes \n", p->bc ? p->bc->pid : -1); - return -1; - default: - p->state = MISDN_ALERTING; - chan_misdn_log(2, p->bc->port, " --> * IND :\tringing pid:%d\n", p->bc ? p->bc->pid : -1); - misdn_lib_send_event( p->bc, EVENT_ALERTING); - - if (p->other_ch && p->other_ch->bc) { - if (misdn_inband_avail(p->other_ch->bc)) { - chan_misdn_log(2, p->bc->port, " --> other End is mISDN and has inband info available\n"); - break; - } - - if (!p->other_ch->bc->nt) { - chan_misdn_log(2, p->bc->port, " --> other End is mISDN TE so it has inband info for sure (?)\n"); - break; - } - } - - chan_misdn_log(3, p->bc->port, " --> * SEND: State Ring pid:%d\n", p->bc ? p->bc->pid : -1); - ast_setstate(ast, AST_STATE_RING); - - if (!p->bc->nt && (p->originator == ORG_MISDN) && !p->incoming_early_audio) - chan_misdn_log(2, p->bc->port, " --> incoming_early_audio off\n"); - else - return -1; - } - break; - case AST_CONTROL_ANSWER: - chan_misdn_log(1, p->bc->port, " --> * IND :\tanswer pid:%d\n", p->bc ? p->bc->pid : -1); - start_bc_tones(p); - break; - case AST_CONTROL_TAKEOFFHOOK: - chan_misdn_log(1, p->bc->port, " --> *\ttakeoffhook pid:%d\n", p->bc ? p->bc->pid : -1); - return -1; - case AST_CONTROL_OFFHOOK: - chan_misdn_log(1, p->bc->port, " --> *\toffhook pid:%d\n", p->bc ? p->bc->pid : -1); - return -1; - case AST_CONTROL_FLASH: - chan_misdn_log(1, p->bc->port, " --> *\tflash pid:%d\n", p->bc ? p->bc->pid : -1); - break; - case AST_CONTROL_PROGRESS: - chan_misdn_log(1, p->bc->port, " --> * IND :\tprogress pid:%d\n", p->bc ? p->bc->pid : -1); - misdn_lib_send_event( p->bc, EVENT_PROGRESS); - break; - case AST_CONTROL_PROCEEDING: - chan_misdn_log(1, p->bc->port, " --> * IND :\tproceeding pid:%d\n", p->bc ? p->bc->pid : -1); - misdn_lib_send_event( p->bc, EVENT_PROCEEDING); - break; - case AST_CONTROL_CONGESTION: - chan_misdn_log(1, p->bc->port, " --> * IND :\tcongestion pid:%d\n", p->bc ? p->bc->pid : -1); - - p->bc->out_cause = AST_CAUSE_SWITCH_CONGESTION; - start_bc_tones(p); - misdn_lib_send_event( p->bc, EVENT_DISCONNECT); - - if (p->bc->nt) { - hanguptone_indicate(p); - } - break; - case -1 : - chan_misdn_log(1, p->bc->port, " --> * IND :\t-1! (stop indication) pid:%d\n", p->bc ? p->bc->pid : -1); - - stop_indicate(p); - - if (p->state == MISDN_CONNECTED) - start_bc_tones(p); - break; - case AST_CONTROL_HOLD: - ast_moh_start(ast, data, p->mohinterpret); - chan_misdn_log(1, p->bc->port, " --> *\tHOLD pid:%d\n", p->bc ? p->bc->pid : -1); - break; - case AST_CONTROL_UNHOLD: - ast_moh_stop(ast); - chan_misdn_log(1, p->bc->port, " --> *\tUNHOLD pid:%d\n", p->bc ? p->bc->pid : -1); - break; - default: - chan_misdn_log(1, p->bc->port, " --> * Unknown Indication:%d pid:%d\n", cond, p->bc ? p->bc->pid : -1); - } - - return 0; -} - -static int misdn_hangup(struct ast_channel *ast) -{ - struct chan_list *p; - struct misdn_bchannel *bc = NULL; - const char *varcause = NULL; - - ast_log(LOG_DEBUG, "misdn_hangup(%s)\n", ast->name); - - if (!ast || ! (p=MISDN_ASTERISK_TECH_PVT(ast) ) ) return -1; - - if (!p) { - chan_misdn_log(3, 0, "misdn_hangup called, without chan_list obj.\n"); - return 0 ; - } - - bc = p->bc; - - if (bc) { - const char *tmp=pbx_builtin_getvar_helper(ast,"MISDN_USERUSER"); - if (tmp) { - ast_log(LOG_NOTICE, "MISDN_USERUSER: %s\n", tmp); - strcpy(bc->uu, tmp); - bc->uulen=strlen(bc->uu); - } - } - - MISDN_ASTERISK_TECH_PVT(ast) = NULL; - p->ast = NULL; - - if (ast->_state == AST_STATE_RESERVED || - p->state == MISDN_NOTHING || - p->state == MISDN_HOLDED || - p->state == MISDN_HOLD_DISCONNECT ) { - - CLEAN_CH: - /* between request and call */ - ast_log(LOG_DEBUG, "State Reserved (or nothing) => chanIsAvail\n"); - MISDN_ASTERISK_TECH_PVT(ast) = NULL; - - ast_mutex_lock(&release_lock); - cl_dequeue_chan(&cl_te, p); - close(p->pipe[0]); - close(p->pipe[1]); - free(p); - ast_mutex_unlock(&release_lock); - - if (bc) - misdn_lib_release(bc); - - return 0; - } - - if (!bc) { - ast_log(LOG_WARNING, "Hangup with private but no bc ? state:%s l3id:%x\n", misdn_get_ch_state(p), p->l3id); - goto CLEAN_CH; - } - - - p->need_hangup = 0; - p->need_queue_hangup = 0; - p->need_busy = 0; - - - if (!p->bc->nt) - stop_bc_tones(p); - - bc->out_cause = ast->hangupcause ? ast->hangupcause : AST_CAUSE_NORMAL_CLEARING; - - if ((varcause = pbx_builtin_getvar_helper(ast, "HANGUPCAUSE")) || - (varcause = pbx_builtin_getvar_helper(ast, "PRI_CAUSE"))) { - int tmpcause = atoi(varcause); - bc->out_cause = tmpcause ? tmpcause : AST_CAUSE_NORMAL_CLEARING; - } - - chan_misdn_log(1, bc->port, "* IND : HANGUP\tpid:%d ctx:%s dad:%s oad:%s State:%s\n", p->bc ? p->bc->pid : -1, ast->context, ast->exten, ast->cid.cid_num, misdn_get_ch_state(p)); - chan_misdn_log(3, bc->port, " --> l3id:%x\n", p->l3id); - chan_misdn_log(3, bc->port, " --> cause:%d\n", bc->cause); - chan_misdn_log(2, bc->port, " --> out_cause:%d\n", bc->out_cause); - chan_misdn_log(2, bc->port, " --> state:%s\n", misdn_get_ch_state(p)); - - switch (p->state) { - case MISDN_INCOMING_SETUP: - case MISDN_CALLING: - /* This is the only place in misdn_hangup, where we - * can call release_chan, else it might create lot's of trouble - * */ - ast_log(LOG_NOTICE, "release channel, in CALLING/INCOMING_SETUP state.. no other events happened\n"); - release_chan(bc); - - p->state = MISDN_CLEANING; - if (bc->need_release_complete) - misdn_lib_send_event( bc, EVENT_RELEASE_COMPLETE); - break; - case MISDN_HOLDED: - case MISDN_DIALING: - start_bc_tones(p); - hanguptone_indicate(p); - - if (bc->need_disconnect) - misdn_lib_send_event( bc, EVENT_DISCONNECT); - break; - case MISDN_CALLING_ACKNOWLEDGE: - start_bc_tones(p); - hanguptone_indicate(p); - - if (bc->need_disconnect) - misdn_lib_send_event( bc, EVENT_DISCONNECT); - break; - - case MISDN_ALERTING: - case MISDN_PROGRESS: - case MISDN_PROCEEDING: - if (p->originator != ORG_AST) - hanguptone_indicate(p); - - /*p->state=MISDN_CLEANING;*/ - if (bc->need_disconnect) - misdn_lib_send_event( bc, EVENT_DISCONNECT); - break; - case MISDN_CONNECTED: - case MISDN_PRECONNECTED: - /* Alerting or Disconnect */ - if (p->bc->nt) { - start_bc_tones(p); - hanguptone_indicate(p); - p->bc->progress_indicator = INFO_PI_INBAND_AVAILABLE; - } - if (bc->need_disconnect) - misdn_lib_send_event( bc, EVENT_DISCONNECT); - - /*p->state=MISDN_CLEANING;*/ - break; - case MISDN_DISCONNECTED: - if (bc->need_release) - misdn_lib_send_event( bc, EVENT_RELEASE); - p->state = MISDN_CLEANING; /* MISDN_HUNGUP_FROM_AST; */ - break; - - case MISDN_RELEASED: - case MISDN_CLEANING: - p->state = MISDN_CLEANING; - break; - - case MISDN_BUSY: - break; - - case MISDN_HOLD_DISCONNECT: - /* need to send release here */ - chan_misdn_log(1, bc->port, " --> cause %d\n", bc->cause); - chan_misdn_log(1, bc->port, " --> out_cause %d\n", bc->out_cause); - - bc->out_cause = -1; - if (bc->need_release) - misdn_lib_send_event(bc, EVENT_RELEASE); - p->state = MISDN_CLEANING; - break; - default: - if (bc->nt) { - bc->out_cause = -1; - if (bc->need_release) - misdn_lib_send_event(bc, EVENT_RELEASE); - p->state = MISDN_CLEANING; - } else { - if (bc->need_disconnect) - misdn_lib_send_event(bc, EVENT_DISCONNECT); - } - } - - p->state = MISDN_CLEANING; - - chan_misdn_log(3, bc->port, " --> Channel: %s hanguped new state:%s\n", ast->name, misdn_get_ch_state(p)); - - return 0; -} - - -static struct ast_frame *process_ast_dsp(struct chan_list *tmp, struct ast_frame *frame) -{ - struct ast_frame *f,*f2; - - if (tmp->trans) { - f2 = ast_translate(tmp->trans, frame, 0); - f = ast_dsp_process(tmp->ast, tmp->dsp, f2); - } else { - chan_misdn_log(0, tmp->bc->port, "No T-Path found\n"); - return NULL; - } - - if (!f || (f->frametype != AST_FRAME_DTMF)) - return frame; - - ast_log(LOG_DEBUG, "Detected inband DTMF digit: %c\n", f->subclass); - - if (tmp->faxdetect && (f->subclass == 'f')) { - /* Fax tone -- Handle and return NULL */ - if (!tmp->faxhandled) { - struct ast_channel *ast = tmp->ast; - tmp->faxhandled++; - chan_misdn_log(0, tmp->bc->port, "Fax detected, preparing %s for fax transfer.\n", ast->name); - tmp->bc->rxgain = 0; - isdn_lib_update_rxgain(tmp->bc); - tmp->bc->txgain = 0; - isdn_lib_update_txgain(tmp->bc); -#ifdef MISDN_1_2 - *tmp->bc->pipeline = 0; -#else - tmp->bc->ec_enable = 0; -#endif - isdn_lib_update_ec(tmp->bc); - isdn_lib_stop_dtmf(tmp->bc); - switch (tmp->faxdetect) { - case 1: - if (strcmp(ast->exten, "fax")) { - char *context; - char context_tmp[BUFFERSIZE]; - misdn_cfg_get(tmp->bc->port, MISDN_CFG_FAXDETECT_CONTEXT, &context_tmp, sizeof(context_tmp)); - context = ast_strlen_zero(context_tmp) ? (ast_strlen_zero(ast->macrocontext) ? ast->context : ast->macrocontext) : context_tmp; - if (ast_exists_extension(ast, context, "fax", 1, ast->cid.cid_num)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Redirecting %s to fax extension (context:%s)\n", ast->name, context); - /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ - pbx_builtin_setvar_helper(ast,"FAXEXTEN",ast->exten); - if (ast_async_goto(ast, context, "fax", 1)) - ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, context); - } else - ast_log(LOG_NOTICE, "Fax detected, but no fax extension ctx:%s exten:%s\n", context, ast->exten); - } else { - ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n"); - } - break; - case 2: - ast_verbose(VERBOSE_PREFIX_3 "Not redirecting %s to fax extension, nojump is set.\n", ast->name); - break; - } - } else { - ast_log(LOG_DEBUG, "Fax already handled\n"); - } - } - - if (tmp->ast_dsp && (f->subclass != 'f')) { - chan_misdn_log(2, tmp->bc->port, " --> * SEND: DTMF (AST_DSP) :%c\n", f->subclass); - } - - return f; -} - - -static struct ast_frame *misdn_read(struct ast_channel *ast) -{ - struct chan_list *tmp; - fd_set rrfs; - struct timeval tv; - int len, t; - - if (!ast) { - chan_misdn_log(1, 0, "misdn_read called without ast\n"); - return NULL; - } - if (!(tmp = MISDN_ASTERISK_TECH_PVT(ast))) { - chan_misdn_log(1, 0, "misdn_read called without ast->pvt\n"); - return NULL; - } - - if (!tmp->bc && !(tmp->state == MISDN_HOLDED)) { - chan_misdn_log(1, 0, "misdn_read called without bc\n"); - return NULL; - } - - tv.tv_sec=0; - tv.tv_usec=20000; - - FD_ZERO(&rrfs); - FD_SET(tmp->pipe[0],&rrfs); - - t=select(FD_SETSIZE,&rrfs,NULL, NULL,&tv); - - if (!t) { - chan_misdn_log(3, tmp->bc->port, "read Select Timed out\n"); - len=160; - } - - if (t<0) { - chan_misdn_log(-1, tmp->bc->port, "Select Error (err=%s)\n",strerror(errno)); - return NULL; - } - - if (FD_ISSET(tmp->pipe[0],&rrfs)) { - len=read(tmp->pipe[0],tmp->ast_rd_buf,sizeof(tmp->ast_rd_buf)); - - if (len<=0) { - /* we hangup here, since our pipe is closed */ - chan_misdn_log(2,tmp->bc->port,"misdn_read: Pipe closed, hanging up\n"); - return NULL; - } - - } else { - return NULL; - } - - tmp->frame.frametype = AST_FRAME_VOICE; - tmp->frame.subclass = AST_FORMAT_ALAW; - tmp->frame.datalen = len; - tmp->frame.samples = len; - tmp->frame.mallocd = 0; - tmp->frame.offset = 0; - tmp->frame.delivery = ast_tv(0,0); - tmp->frame.src = NULL; - tmp->frame.data = tmp->ast_rd_buf; - - if (tmp->faxdetect && !tmp->faxhandled) { - if (tmp->faxdetect_timeout) { - if (ast_tvzero(tmp->faxdetect_tv)) { - tmp->faxdetect_tv = ast_tvnow(); - chan_misdn_log(2, tmp->bc->port, "faxdetect: starting detection with timeout: %ds ...\n", tmp->faxdetect_timeout); - return process_ast_dsp(tmp, &tmp->frame); - } else { - struct timeval tv_now = ast_tvnow(); - int diff = ast_tvdiff_ms(tv_now, tmp->faxdetect_tv); - if (diff <= (tmp->faxdetect_timeout * 1000)) { - chan_misdn_log(5, tmp->bc->port, "faxdetect: detecting ...\n"); - return process_ast_dsp(tmp, &tmp->frame); - } else { - chan_misdn_log(2, tmp->bc->port, "faxdetect: stopping detection (time ran out) ...\n"); - tmp->faxdetect = 0; - return &tmp->frame; - } - } - } else { - chan_misdn_log(5, tmp->bc->port, "faxdetect: detecting ... (no timeout)\n"); - return process_ast_dsp(tmp, &tmp->frame); - } - } else { - if (tmp->ast_dsp) - return process_ast_dsp(tmp, &tmp->frame); - else - return &tmp->frame; - } -} - - -static int misdn_write(struct ast_channel *ast, struct ast_frame *frame) -{ - struct chan_list *ch; - int i = 0; - - if (!ast || ! (ch = MISDN_ASTERISK_TECH_PVT(ast)) ) return -1; - - if (ch->state == MISDN_HOLDED) { - chan_misdn_log(7, 0, "misdn_write: Returning because holded\n"); - return 0; - } - - if (!ch->bc ) { - ast_log(LOG_WARNING, "private but no bc\n"); - return -1; - } - - if (ch->notxtone) { - chan_misdn_log(7, ch->bc->port, "misdn_write: Returning because notxtone\n"); - return 0; - } - - - if (!frame->subclass) { - chan_misdn_log(4, ch->bc->port, "misdn_write: * prods us\n"); - return 0; - } - - if (!(frame->subclass & prefformat)) { - - chan_misdn_log(-1, ch->bc->port, "Got Unsupported Frame with Format:%d\n", frame->subclass); - return 0; - } - - - if (!frame->samples ) { - chan_misdn_log(4, ch->bc->port, "misdn_write: zero write\n"); - - if (!strcmp(frame->src,"ast_prod")) { - chan_misdn_log(1, ch->bc->port, "misdn_write: state (%s) prodded.\n", misdn_get_ch_state(ch)); - - if (ch->ts) { - chan_misdn_log(4, ch->bc->port, "Starting Playtones\n"); - misdn_lib_tone_generator_start(ch->bc); - } - return 0; - } - - return -1; - } - - if ( ! ch->bc->addr ) { - chan_misdn_log(8, ch->bc->port, "misdn_write: no addr for bc dropping:%d\n", frame->samples); - return 0; - } - -#ifdef MISDN_DEBUG - { - int i, max = 5 > frame->samples ? frame->samples : 5; - - printf("write2mISDN %p %d bytes: ", p, frame->samples); - - for (i = 0; i < max; i++) - printf("%2.2x ", ((char*) frame->data)[i]); - printf ("\n"); - } -#endif - - switch (ch->bc->bc_state) { - case BCHAN_ACTIVATED: - case BCHAN_BRIDGED: - break; - default: - if (!ch->dropped_frame_cnt) - chan_misdn_log(5, ch->bc->port, "BC not active (nor bridged) dropping: %d frames addr:%x exten:%s cid:%s ch->state:%s bc_state:%d l3id:%x\n", frame->samples, ch->bc->addr, ast->exten, ast->cid.cid_num, misdn_get_ch_state( ch), ch->bc->bc_state, ch->bc->l3_id); - - ch->dropped_frame_cnt++; - if (ch->dropped_frame_cnt > 100) { - ch->dropped_frame_cnt = 0; - chan_misdn_log(5, ch->bc->port, "BC not active (nor bridged) dropping: %d frames addr:%x dropped > 100 frames!\n", frame->samples, ch->bc->addr); - } - - return 0; - } - - chan_misdn_log(9, ch->bc->port, "Sending :%d bytes to MISDN\n", frame->samples); - if ( !ch->bc->nojitter && misdn_cap_is_speech(ch->bc->capability) ) { - /* Buffered Transmit (triggered by read from isdn side)*/ - if (misdn_jb_fill(ch->jb, frame->data, frame->samples) < 0) { - if (ch->bc->active) - cb_log(0, ch->bc->port, "Misdn Jitterbuffer Overflow.\n"); - } - - } else { - /*transmit without jitterbuffer*/ - i = misdn_lib_tx2misdn_frm(ch->bc, frame->data, frame->samples); - } - - return 0; -} - - - - -static enum ast_bridge_result misdn_bridge (struct ast_channel *c0, - struct ast_channel *c1, int flags, - struct ast_frame **fo, - struct ast_channel **rc, - int timeoutms) - -{ - struct chan_list *ch1, *ch2; - struct ast_channel *carr[2], *who; - int to = -1; - struct ast_frame *f; - int p1_b, p2_b; - int bridging; - - ch1 = get_chan_by_ast(c0); - ch2 = get_chan_by_ast(c1); - - carr[0] = c0; - carr[1] = c1; - - if (!(ch1 && ch2)) - return -1; - - misdn_cfg_get(ch1->bc->port, MISDN_CFG_BRIDGING, &p1_b, sizeof(int)); - misdn_cfg_get(ch2->bc->port, MISDN_CFG_BRIDGING, &p2_b, sizeof(int)); - - if (! p1_b || ! p2_b) { - ast_log(LOG_NOTICE, "Falling back to Asterisk bridging\n"); - return AST_BRIDGE_FAILED; - } - - misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(int)); - if (bridging) { - /* trying to make a mISDN_dsp conference */ - chan_misdn_log(1, ch1->bc->port, "I SEND: Making conference with Number:%d\n", ch1->bc->pid + 1); - misdn_lib_bridge(ch1->bc, ch2->bc); - } - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name); - - chan_misdn_log(1, ch1->bc->port, "* Making Native Bridge between %s and %s\n", ch1->bc->oad, ch2->bc->oad); - - if (! (flags & AST_BRIDGE_DTMF_CHANNEL_0) ) - ch1->ignore_dtmf = 1; - - if (! (flags & AST_BRIDGE_DTMF_CHANNEL_1) ) - ch2->ignore_dtmf = 1; - - for (;/*ever*/;) { - to = -1; - who = ast_waitfor_n(carr, 2, &to); - - if (!who) { - ast_log(LOG_NOTICE, "misdn_bridge: empty read, breaking out\n"); - break; - } - f = ast_read(who); - - if (!f || f->frametype == AST_FRAME_CONTROL) { - /* got hangup .. */ - - if (!f) - chan_misdn_log(4, ch1->bc->port, "Read Null Frame\n"); - else - chan_misdn_log(4, ch1->bc->port, "Read Frame Control class:%d\n", f->subclass); - - *fo = f; - *rc = who; - break; - } - - if ( f->frametype == AST_FRAME_DTMF ) { - chan_misdn_log(1, 0, "Read DTMF %d from %s\n", f->subclass, who->exten); - - *fo = f; - *rc = who; - break; - } - -#if 0 - if (f->frametype == AST_FRAME_VOICE) { - chan_misdn_log(1, ch1->bc->port, "I SEND: Splitting conference with Number:%d\n", ch1->bc->pid +1); - - continue; - } -#endif - - if (who == c0) { - ast_write(c1, f); - } - else { - ast_write(c0, f); - } - } - - chan_misdn_log(1, ch1->bc->port, "I SEND: Splitting conference with Number:%d\n", ch1->bc->pid + 1); - - misdn_lib_split_bridge(ch1->bc, ch2->bc); - - return AST_BRIDGE_COMPLETE; -} - -/** AST INDICATIONS END **/ - -static int dialtone_indicate(struct chan_list *cl) -{ - const struct tone_zone_sound *ts = NULL; - struct ast_channel *ast = cl->ast; - int nd = 0; - - if (!ast) { - chan_misdn_log(0, cl->bc->port, "No Ast in dialtone_indicate\n"); - return -1; - } - - misdn_cfg_get(cl->bc->port, MISDN_CFG_NODIALTONE, &nd, sizeof(nd)); - - if (nd) { - chan_misdn_log(1, cl->bc->port, "Not sending Dialtone, because config wants it\n"); - return 0; - } - - chan_misdn_log(3, cl->bc->port, " --> Dial\n"); - ts = ast_get_indication_tone(ast->zone, "dial"); - cl->ts = ts; - - if (ts) { - cl->notxtone = 0; - cl->norxtone = 0; - /* This prods us in misdn_write */ - ast_playtones_start(ast, 0, ts->data, 0); - } - - return 0; -} - -static int hanguptone_indicate(struct chan_list *cl) -{ - misdn_lib_send_tone(cl->bc, TONE_HANGUP); - return 0; -} - -static int stop_indicate(struct chan_list *cl) -{ - struct ast_channel *ast = cl->ast; - - if (!ast) { - chan_misdn_log(0, cl->bc->port, "No Ast in stop_indicate\n"); - return -1; - } - - chan_misdn_log(3, cl->bc->port, " --> None\n"); - misdn_lib_tone_generator_stop(cl->bc); - ast_playtones_stop(ast); - - cl->ts = NULL; - /*ast_deactivate_generator(ast);*/ - - return 0; -} - - -static int start_bc_tones(struct chan_list* cl) -{ - misdn_lib_tone_generator_stop(cl->bc); - cl->notxtone = 0; - cl->norxtone = 0; - return 0; -} - -static int stop_bc_tones(struct chan_list *cl) -{ - if (!cl) return -1; - - cl->notxtone = 1; - cl->norxtone = 1; - - return 0; -} - - -static struct chan_list *init_chan_list(int orig) -{ - struct chan_list *cl; - - cl = calloc(1, sizeof(struct chan_list)); - if (!cl) { - chan_misdn_log(-1, 0, "misdn_request: malloc failed!"); - return NULL; - } - - cl->originator = orig; - cl->need_queue_hangup = 1; - cl->need_hangup = 1; - cl->need_busy = 1; - cl->overlap_dial_task = -1; - - return cl; -} - -static struct ast_channel *misdn_request(const char *type, int format, void *data, int *cause) -{ - struct ast_channel *tmp = NULL; - char group[BUFFERSIZE + 1] = ""; - char dial_str[128]; - char *buf2 = ast_strdupa(data); - char *ext; - char *port_str; - char *p = NULL; - int channel = 0; - int port = 0; - struct misdn_bchannel *newbc = NULL; - int dec = 0; - - struct chan_list *cl = init_chan_list(ORG_AST); - - snprintf(dial_str, sizeof(dial_str), "%s/%s", misdn_type, (char *) data); - - /* - * data is ---v - * Dial(mISDN/g:group_name[/extension[/options]]) - * Dial(mISDN/port[:preselected_channel][/extension[/options]]) - * - * The dial extension could be empty if you are using MISDN_KEYPAD - * to control ISDN provider features. - */ - port_str = strsep(&buf2, "/"); - if (!ast_strlen_zero(port_str)) { - if (port_str[0] == 'g' && port_str[1] == ':' ) { - /* We make a group call lets checkout which ports are in my group */ - port_str += 2; - ast_copy_string(group, port_str, sizeof(group)); - chan_misdn_log(2, 0, " --> Group Call group: %s\n", group); - } else if ((p = strchr(port_str, ':'))) { - /* we have a preselected channel */ - *p = 0; - channel = atoi(++p); - port = atoi(port_str); - chan_misdn_log(2, port, " --> Call on preselected Channel (%d).\n", channel); - } else { - port = atoi(port_str); - } - } else { - ast_log(LOG_WARNING, " --> ! IND : Dial(%s) WITHOUT Port or Group, check extensions.conf\n", dial_str); - return NULL; - } - - ext = strsep(&buf2, "/"); - if (!ext) { - ext = ""; - } - - if (misdn_cfg_is_group_method(group, METHOD_STANDARD_DEC)) { - chan_misdn_log(4, port, " --> STARTING STANDARD DEC...\n"); - dec = 1; - } - - if (!ast_strlen_zero(group)) { - char cfg_group[BUFFERSIZE + 1]; - struct robin_list *rr = NULL; - - /* Group dial */ - - if (misdn_cfg_is_group_method(group, METHOD_ROUND_ROBIN)) { - chan_misdn_log(4, port, " --> STARTING ROUND ROBIN...\n"); - rr = get_robin_position(group); - } - - if (rr) { - int port_start = 0; - int port_bak = rr->port; - int chan_bak = rr->channel; - - if (!rr->port) - rr->port = misdn_cfg_get_next_port_spin(rr->port); - - for (; rr->port > 0; rr->port = misdn_cfg_get_next_port_spin(rr->port)) { - int port_up; - int check; - int max_chan; - int last_chance = 0; - - misdn_cfg_get(rr->port, MISDN_CFG_GROUPNAME, cfg_group, BUFFERSIZE); - if (strcasecmp(cfg_group, group)) - continue; - - misdn_cfg_get(rr->port, MISDN_CFG_PMP_L1_CHECK, &check, sizeof(int)); - port_up = misdn_lib_port_up(rr->port, check); - - if (check && !port_up) - chan_misdn_log(1, rr->port, "L1 is not Up on this Port\n"); - - if (check && port_up < 0) - ast_log(LOG_WARNING,"This port (%d) is blocked\n", rr->port); - - if ((port_start == rr->port) && (port_up <= 0)) - break; - - if (!port_start) - port_start = rr->port; - - if (port_up <= 0) - continue; - - max_chan = misdn_lib_get_maxchans(rr->port); - - for (++rr->channel; !last_chance && rr->channel <= max_chan; ++rr->channel) { - if (rr->port == port_bak && rr->channel == chan_bak) - last_chance = 1; - - chan_misdn_log(1, 0, "trying port:%d channel:%d\n", rr->port, rr->channel); - newbc = misdn_lib_get_free_bc(rr->port, rr->channel, 0, 0); - if (newbc) { - chan_misdn_log(4, rr->port, " Success! Found port:%d channel:%d\n", newbc->port, newbc->channel); - if (port_up) - chan_misdn_log(4, rr->port, "portup:%d\n", port_up); - port = rr->port; - break; - } - } - - if (newbc || last_chance) - break; - - rr->channel = 0; - } - if (!newbc) { - rr->port = port_bak; - rr->channel = chan_bak; - } - } else { - for (port = misdn_cfg_get_next_port(0); port > 0; - port = misdn_cfg_get_next_port(port)) { - - misdn_cfg_get(port, MISDN_CFG_GROUPNAME, cfg_group, BUFFERSIZE); - - chan_misdn_log(3, port, "Group [%s] Port [%d]\n", group, port); - if (!strcasecmp(cfg_group, group)) { - int port_up; - int check; - misdn_cfg_get(port, MISDN_CFG_PMP_L1_CHECK, &check, sizeof(int)); - port_up = misdn_lib_port_up(port, check); - - chan_misdn_log(4, port, "portup:%d\n", port_up); - - if (port_up > 0) { - newbc = misdn_lib_get_free_bc(port, 0, 0, dec); - if (newbc) - break; - } - } - } - } - - /* Group dial failed ?*/ - if (!newbc) { - ast_log(LOG_WARNING, - "Could not Dial out on group '%s'.\n" - "\tEither the L2 and L1 on all of these ports where DOWN (see 'show application misdn_check_l2l1')\n" - "\tOr there was no free channel on none of the ports\n\n" - , group); - return NULL; - } - } else { - /* 'Normal' Port dial * Port dial */ - if (channel) - chan_misdn_log(1, port, " --> preselected_channel: %d\n", channel); - newbc = misdn_lib_get_free_bc(port, channel, 0, dec); - - if (!newbc) { - ast_log(LOG_WARNING, "Could not create channel on port:%d with extensions:%s\n", port, ext); - return NULL; - } - } - - - /* create ast_channel and link all the objects together */ - cl->bc = newbc; - - tmp = misdn_new(cl, AST_STATE_RESERVED, ext, NULL, format, port, channel); - if (!tmp) { - ast_log(LOG_ERROR,"Could not create Asterisk object\n"); - return NULL; - } - - cl->ast=tmp; - - /* register chan in local list */ - cl_queue_chan(&cl_te, cl) ; - - /* fill in the config into the objects */ - read_config(cl, ORG_AST); - - /* important */ - cl->need_hangup = 0; - - return tmp; -} - - -static int misdn_send_text(struct ast_channel *chan, const char *text) -{ - struct chan_list *tmp = chan->tech_pvt; - - if (tmp && tmp->bc) { - ast_copy_string(tmp->bc->display, text, sizeof(tmp->bc->display)); - misdn_lib_send_event(tmp->bc, EVENT_INFORMATION); - } else { - ast_log(LOG_WARNING, "No chan_list but send_text request?\n"); - return -1; - } - - return 0; -} - -static struct ast_channel_tech misdn_tech = { - .type = "mISDN", - .description = "Channel driver for mISDN Support (Bri/Pri)", - .capabilities = AST_FORMAT_ALAW , - .requester = misdn_request, - .send_digit_begin = misdn_digit_begin, - .send_digit_end = misdn_digit_end, - .call = misdn_call, - .bridge = misdn_bridge, - .hangup = misdn_hangup, - .answer = misdn_answer, - .read = misdn_read, - .write = misdn_write, - .indicate = misdn_indication, - .fixup = misdn_fixup, - .send_text = misdn_send_text, - .properties = 0 -}; - -static struct ast_channel_tech misdn_tech_wo_bridge = { - .type = "mISDN", - .description = "Channel driver for mISDN Support (Bri/Pri)", - .capabilities = AST_FORMAT_ALAW , - .requester = misdn_request, - .send_digit_begin = misdn_digit_begin, - .send_digit_end = misdn_digit_end, - .call = misdn_call, - .hangup = misdn_hangup, - .answer = misdn_answer, - .read = misdn_read, - .write = misdn_write, - .indicate = misdn_indication, - .fixup = misdn_fixup, - .send_text = misdn_send_text, - .properties = 0 -}; - - -static int glob_channel = 0; - -static void update_name(struct ast_channel *tmp, int port, int c) -{ - int chan_offset = 0; - int tmp_port = misdn_cfg_get_next_port(0); - for (; tmp_port > 0; tmp_port = misdn_cfg_get_next_port(tmp_port)) { - if (tmp_port == port) - break; - chan_offset += misdn_lib_port_is_pri(tmp_port) ? 30 : 2; - } - if (c < 0) - c = 0; - - ast_string_field_build(tmp, name, "%s/%d-u%d", - misdn_type, chan_offset + c, glob_channel++); - - chan_misdn_log(3, port, " --> updating channel name to [%s]\n", tmp->name); -} - -static struct ast_channel *misdn_new(struct chan_list *chlist, int state, char *exten, char *callerid, int format, int port, int c) -{ - struct ast_channel *tmp; - char *cid_name = 0, *cid_num = 0; - int chan_offset = 0; - int tmp_port = misdn_cfg_get_next_port(0); - int bridging; - - for (; tmp_port > 0; tmp_port = misdn_cfg_get_next_port(tmp_port)) { - if (tmp_port == port) - break; - chan_offset += misdn_lib_port_is_pri(tmp_port) ? 30 : 2; - } - if (c < 0) - c = 0; - - if (callerid) { - ast_callerid_parse(callerid, &cid_name, &cid_num); - } - - tmp = ast_channel_alloc(1, state, cid_num, cid_name, "", exten, "", 0, "%s/%d-u%d", misdn_type, chan_offset + c, glob_channel++); - if (tmp) { - chan_misdn_log(2, 0, " --> * NEW CHANNEL dad:%s oad:%s\n", exten, callerid); - - tmp->nativeformats = prefformat; - - tmp->readformat = format; - tmp->rawreadformat = format; - tmp->writeformat = format; - tmp->rawwriteformat = format; - - tmp->tech_pvt = chlist; - - misdn_cfg_get(0, MISDN_GEN_BRIDGING, &bridging, sizeof(int)); - - if (bridging) - tmp->tech = &misdn_tech; - else - tmp->tech = &misdn_tech_wo_bridge; - - tmp->writeformat = format; - tmp->readformat = format; - tmp->priority=1; - - if (exten) - ast_copy_string(tmp->exten, exten, sizeof(tmp->exten)); - else - chan_misdn_log(1, 0, "misdn_new: no exten given.\n"); - - if (callerid) - /* Don't use ast_set_callerid() here because it will - * generate a needless NewCallerID event */ - tmp->cid.cid_ani = ast_strdup(cid_num); - - if (pipe(chlist->pipe) < 0) - perror("Pipe failed\n"); - tmp->fds[0] = chlist->pipe[0]; - - if (state == AST_STATE_RING) - tmp->rings = 1; - else - tmp->rings = 0; - - } else { - chan_misdn_log(-1, 0, "Unable to allocate channel structure\n"); - } - - return tmp; -} - -static struct chan_list *find_chan_by_bc(struct chan_list *list, struct misdn_bchannel *bc) -{ - struct chan_list *help = list; - for (; help; help = help->next) { - if (help->bc == bc) return help; - } - - chan_misdn_log(6, bc->port, "$$$ find_chan: No channel found for oad:%s dad:%s\n", bc->oad, bc->dad); - - return NULL; -} - -static struct chan_list *find_chan_by_pid(struct chan_list *list, int pid) -{ - struct chan_list *help = list; - for (; help; help = help->next) { - if ( help->bc && (help->bc->pid == pid) ) return help; - } - - chan_misdn_log(6, 0, "$$$ find_chan: No channel found for pid:%d\n", pid); - - return NULL; -} - -static struct chan_list *find_holded(struct chan_list *list, struct misdn_bchannel *bc) -{ - struct chan_list *help = list; - - if (bc->pri) return NULL; - - chan_misdn_log(6, bc->port, "$$$ find_holded: channel:%d oad:%s dad:%s\n", bc->channel, bc->oad, bc->dad); - for (;help; help = help->next) { - chan_misdn_log(4, bc->port, "$$$ find_holded: --> holded:%d channel:%d\n", help->state == MISDN_HOLDED, help->hold_info.channel); - if ((help->state == MISDN_HOLDED) && - (help->hold_info.port == bc->port)) - return help; - } - chan_misdn_log(6, bc->port, "$$$ find_chan: No channel found for oad:%s dad:%s\n", bc->oad, bc->dad); - - return NULL; -} - - -static struct chan_list *find_holded_l3(struct chan_list *list, unsigned long l3_id, int w) -{ - struct chan_list *help = list; - - for (; help; help = help->next) { - if ( (help->state == MISDN_HOLDED) && - (help->l3id == l3_id) - ) - return help; - } - - return NULL; -} - -static void cl_queue_chan(struct chan_list **list, struct chan_list *chan) -{ - chan_misdn_log(4, chan->bc ? chan->bc->port : 0, "* Queuing chan %p\n", chan); - - ast_mutex_lock(&cl_te_lock); - if (!*list) { - *list = chan; - } else { - struct chan_list *help = *list; - for (; help->next; help = help->next); - help->next = chan; - } - chan->next = NULL; - ast_mutex_unlock(&cl_te_lock); -} - -static void cl_dequeue_chan(struct chan_list **list, struct chan_list *chan) -{ - struct chan_list *help; - - if (chan->dsp) - ast_dsp_free(chan->dsp); - if (chan->trans) - ast_translator_free_path(chan->trans); - - ast_mutex_lock(&cl_te_lock); - if (!*list) { - ast_mutex_unlock(&cl_te_lock); - return; - } - - if (*list == chan) { - *list = (*list)->next; - ast_mutex_unlock(&cl_te_lock); - return; - } - - for (help = *list; help->next; help = help->next) { - if (help->next == chan) { - help->next = help->next->next; - ast_mutex_unlock(&cl_te_lock); - return; - } - } - - ast_mutex_unlock(&cl_te_lock); -} - -/** Channel Queue End **/ - - -static int pbx_start_chan(struct chan_list *ch) -{ - int ret = ast_pbx_start(ch->ast); - - if (ret >= 0) - ch->need_hangup = 0; - else - ch->need_hangup = 1; - - return ret; -} - -static void hangup_chan(struct chan_list *ch) -{ - int port = ch ? (ch->bc ? ch->bc->port : 0) : 0; - if (!ch) { - cb_log(1, 0, "Cannot hangup chan, no ch\n"); - return; - } - - cb_log(5, port, "hangup_chan called\n"); - - if (ch->need_hangup) { - cb_log(2, port, " --> hangup\n"); - send_cause2ast(ch->ast, ch->bc, ch); - ch->need_hangup = 0; - ch->need_queue_hangup = 0; - if (ch->ast) - ast_hangup(ch->ast); - return; - } - - if (!ch->need_queue_hangup) { - cb_log(2, port, " --> No need to queue hangup\n"); - } - - ch->need_queue_hangup = 0; - if (ch->ast) { - send_cause2ast(ch->ast, ch->bc, ch); - - if (ch->ast) - ast_queue_hangup(ch->ast); - cb_log(2, port, " --> queue_hangup\n"); - } else { - cb_log(1, port, "Cannot hangup chan, no ast\n"); - } -} - -/** Isdn asks us to release channel, pendant to misdn_hangup **/ -static void release_chan(struct misdn_bchannel *bc) { - struct ast_channel *ast = NULL; - - ast_mutex_lock(&release_lock); - { - struct chan_list *ch=find_chan_by_bc(cl_te, bc); - if (!ch) { - chan_misdn_log(1, bc->port, "release_chan: Ch not found!\n"); - ast_mutex_unlock(&release_lock); - return; - } - - if (ch->ast) { - ast = ch->ast; - } - - chan_misdn_log(5, bc->port, "release_chan: bc with l3id: %x\n", bc->l3_id); - - /*releasing jitterbuffer*/ - if (ch->jb ) { - misdn_jb_destroy(ch->jb); - ch->jb = NULL; - } else { - if (!bc->nojitter) - chan_misdn_log(5, bc->port, "Jitterbuffer already destroyed.\n"); - } - - if (ch->overlap_dial) { - if (ch->overlap_dial_task != -1) { - misdn_tasks_remove(ch->overlap_dial_task); - ch->overlap_dial_task = -1; - } - ast_mutex_destroy(&ch->overlap_tv_lock); - } - - if (ch->originator == ORG_AST) { - misdn_out_calls[bc->port]--; - } else { - misdn_in_calls[bc->port]--; - } - - if (ch) { - close(ch->pipe[0]); - close(ch->pipe[1]); - - if (ast && MISDN_ASTERISK_TECH_PVT(ast)) { - chan_misdn_log(1, bc->port, "* RELEASING CHANNEL pid:%d ctx:%s dad:%s oad:%s state: %s\n", bc ? bc->pid : -1, ast->context, ast->exten, ast->cid.cid_num, misdn_get_ch_state(ch)); - chan_misdn_log(3, bc->port, " --> * State Down\n"); - MISDN_ASTERISK_TECH_PVT(ast) = NULL; - - if (ast->_state != AST_STATE_RESERVED) { - chan_misdn_log(3, bc->port, " --> Setting AST State to down\n"); - ast_setstate(ast, AST_STATE_DOWN); - } - } - - ch->state = MISDN_CLEANING; - cl_dequeue_chan(&cl_te, ch); - - free(ch); - } else { - /* chan is already cleaned, so exiting */ - } - } - ast_mutex_unlock(&release_lock); -/*** release end **/ -} - -static void misdn_transfer_bc(struct chan_list *tmp_ch, struct chan_list *holded_chan) -{ - chan_misdn_log(4, 0, "TRANSFERRING %s to %s\n", holded_chan->ast->name, tmp_ch->ast->name); - - tmp_ch->state = MISDN_HOLD_DISCONNECT; - - ast_moh_stop(ast_bridged_channel(holded_chan->ast)); - - holded_chan->state=MISDN_CONNECTED; - /* misdn_lib_transfer(holded_chan->bc); */ - ast_channel_masquerade(holded_chan->ast, ast_bridged_channel(tmp_ch->ast)); -} - - -static void do_immediate_setup(struct misdn_bchannel *bc, struct chan_list *ch, struct ast_channel *ast) -{ - char predial[256]=""; - char *p = predial; - - struct ast_frame fr; - - strncpy(predial, ast->exten, sizeof(predial) -1 ); - - ch->state = MISDN_DIALING; - - if (!ch->noautorespond_on_setup) { - if (bc->nt) { - int ret; - ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE ); - } else { - int ret; - if ( misdn_lib_is_ptp(bc->port)) { - ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE ); - } else { - ret = misdn_lib_send_event(bc, EVENT_PROCEEDING ); - } - } - } else { - ch->state = MISDN_INCOMING_SETUP; - } - - chan_misdn_log(1, bc->port, "* Starting Ast ctx:%s dad:%s oad:%s with 's' extension\n", ast->context, ast->exten, ast->cid.cid_num); - - strcpy(ast->exten, "s"); - - if (pbx_start_chan(ch) < 0) { - ast = NULL; - hangup_chan(ch); - hanguptone_indicate(ch); - - if (bc->nt) - misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE ); - else - misdn_lib_send_event(bc, EVENT_DISCONNECT ); - } - - - while (!ast_strlen_zero(p) ) { - fr.frametype = AST_FRAME_DTMF; - fr.subclass = *p; - fr.src = NULL; - fr.data = NULL; - fr.datalen = 0; - fr.samples = 0; - fr.mallocd = 0; - fr.offset = 0; - fr.delivery = ast_tv(0,0); - - if (ch->ast && MISDN_ASTERISK_PVT(ch->ast) && MISDN_ASTERISK_TECH_PVT(ch->ast)) { - ast_queue_frame(ch->ast, &fr); - } - p++; - } -} - - - -static void send_cause2ast(struct ast_channel *ast, struct misdn_bchannel *bc, struct chan_list *ch) { - if (!ast) { - chan_misdn_log(1, 0, "send_cause2ast: No Ast\n"); - return; - } - if (!bc) { - chan_misdn_log(1, 0, "send_cause2ast: No BC\n"); - return; - } - if (!ch) { - chan_misdn_log(1, 0, "send_cause2ast: No Ch\n"); - return; - } - - ast->hangupcause = bc->cause; - - switch (bc->cause) { - - case AST_CAUSE_UNALLOCATED: - case AST_CAUSE_NO_ROUTE_TRANSIT_NET: - case AST_CAUSE_NO_ROUTE_DESTINATION: - case 4: /* Send special information tone */ - case AST_CAUSE_NUMBER_CHANGED: - case AST_CAUSE_DESTINATION_OUT_OF_ORDER: - /* Congestion Cases */ - /* - * Not Queueing the Congestion anymore, since we want to hear - * the inband message - * - chan_misdn_log(1, bc ? bc->port : 0, " --> * SEND: Queue Congestion pid:%d\n", bc ? bc->pid : -1); - ch->state = MISDN_BUSY; - - ast_queue_control(ast, AST_CONTROL_CONGESTION); - */ - break; - - case AST_CAUSE_CALL_REJECTED: - case AST_CAUSE_USER_BUSY: - ch->state = MISDN_BUSY; - - if (!ch->need_busy) { - chan_misdn_log(1, bc ? bc->port : 0, "Queued busy already\n"); - break; - } - - chan_misdn_log(1, bc ? bc->port : 0, " --> * SEND: Queue Busy pid:%d\n", bc ? bc->pid : -1); - - ast_queue_control(ast, AST_CONTROL_BUSY); - - ch->need_busy = 0; - - break; - } -} - - -/*! \brief Import parameters from the dialplan environment variables */ -void import_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch) -{ - const char *tmp; - - tmp = pbx_builtin_getvar_helper(chan, "MISDN_PID"); - if (tmp) { - ch->other_pid = atoi(tmp); - chan_misdn_log(3, bc->port, " --> IMPORT_PID: importing pid:%s\n", tmp); - if (ch->other_pid > 0) { - ch->other_ch = find_chan_by_pid(cl_te, ch->other_pid); - if (ch->other_ch) - ch->other_ch->other_ch = ch; - } - } - - tmp = pbx_builtin_getvar_helper(chan, "MISDN_ADDRESS_COMPLETE"); - if (tmp && (atoi(tmp) == 1)) { - bc->sending_complete = 1; - } - - tmp = pbx_builtin_getvar_helper(chan, "MISDN_USERUSER"); - if (tmp) { - ast_log(LOG_NOTICE, "MISDN_USERUSER: %s\n", tmp); - ast_copy_string(bc->uu, tmp, sizeof(bc->uu)); - bc->uulen = strlen(bc->uu); - } - - tmp = pbx_builtin_getvar_helper(chan, "MISDN_KEYPAD"); - if (tmp) { - ast_copy_string(bc->keypad, tmp, sizeof(bc->keypad)); - } -} - -/*! \brief Export parameters to the dialplan environment variables */ -void export_ch(struct ast_channel *chan, struct misdn_bchannel *bc, struct chan_list *ch) -{ - char tmp[32]; - chan_misdn_log(3, bc->port, " --> EXPORT_PID: pid:%d\n", bc->pid); - snprintf(tmp, sizeof(tmp), "%d", bc->pid); - pbx_builtin_setvar_helper(chan, "_MISDN_PID", tmp); - - if (bc->sending_complete) { - snprintf(tmp, sizeof(tmp), "%d", bc->sending_complete); - pbx_builtin_setvar_helper(chan, "MISDN_ADDRESS_COMPLETE", tmp); - } - - if (bc->urate) { - snprintf(tmp, sizeof(tmp), "%d", bc->urate); - pbx_builtin_setvar_helper(chan, "MISDN_URATE", tmp); - } - - if (bc->uulen && (bc->uulen < sizeof(bc->uu))) { - bc->uu[bc->uulen] = 0; - pbx_builtin_setvar_helper(chan, "MISDN_USERUSER", bc->uu); - } - - if (!ast_strlen_zero(bc->keypad)) - pbx_builtin_setvar_helper(chan, "MISDN_KEYPAD", bc->keypad); -} - -int add_in_calls(int port) -{ - int max_in_calls; - - misdn_cfg_get(port, MISDN_CFG_MAX_IN, &max_in_calls, sizeof(max_in_calls)); - misdn_in_calls[port]++; - - if (max_in_calls >= 0 && max_in_calls < misdn_in_calls[port]) { - ast_log(LOG_NOTICE, "Marking Incoming Call on port[%d]\n", port); - return misdn_in_calls[port] - max_in_calls; - } - - return 0; -} - -int add_out_calls(int port) -{ - int max_out_calls; - - misdn_cfg_get(port, MISDN_CFG_MAX_OUT, &max_out_calls, sizeof(max_out_calls)); - - if (max_out_calls >= 0 && max_out_calls <= misdn_out_calls[port]) { - ast_log(LOG_NOTICE, "Rejecting Outgoing Call on port[%d]\n", port); - return (misdn_out_calls[port] + 1) - max_out_calls; - } - - misdn_out_calls[port]++; - - return 0; -} - -static void start_pbx(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) { - if (pbx_start_chan(ch) < 0) { - hangup_chan(ch); - chan_misdn_log(-1, bc->port, "ast_pbx_start returned <0 in SETUP\n"); - if (bc->nt) { - hanguptone_indicate(ch); - misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); - } else - misdn_lib_send_event(bc, EVENT_RELEASE); - } -} - -static void wait_for_digits(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) { - ch->state=MISDN_WAITING4DIGS; - misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE ); - if (bc->nt && !bc->dad[0]) - dialtone_indicate(ch); -} - - -/************************************************************/ -/* Receive Events from isdn_lib here */ -/************************************************************/ -static enum event_response_e -cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) -{ - int msn_valid; - struct chan_list *ch = find_chan_by_bc(cl_te, bc); - - if (event != EVENT_BCHAN_DATA && event != EVENT_TONE_GENERATE) { /* Debug Only Non-Bchan */ - int debuglevel = 1; - if ( event == EVENT_CLEANUP && !user_data) - debuglevel = 5; - - chan_misdn_log(debuglevel, bc->port, "I IND :%s oad:%s dad:%s pid:%d state:%s\n", manager_isdn_get_info(event), bc->oad, bc->dad, bc->pid, ch ? misdn_get_ch_state(ch) : "none"); - if (debuglevel == 1) { - misdn_lib_log_ies(bc); - chan_misdn_log(4, bc->port, " --> bc_state:%s\n", bc_state2str(bc->bc_state)); - } - } - - if (!ch) { - switch(event) { - case EVENT_SETUP: - case EVENT_DISCONNECT: - case EVENT_PORT_ALARM: - case EVENT_RETRIEVE: - case EVENT_NEW_BC: - case EVENT_FACILITY: - break; - case EVENT_RELEASE_COMPLETE: - chan_misdn_log(1, bc->port, " --> no Ch, so we've already released.\n"); - break; - case EVENT_CLEANUP: - case EVENT_TONE_GENERATE: - case EVENT_BCHAN_DATA: - return -1; - default: - chan_misdn_log(1, bc->port, "Chan not existing at the moment bc->l3id:%x bc:%p event:%s port:%d channel:%d\n", bc->l3_id, bc, manager_isdn_get_info(event), bc->port, bc->channel); - return -1; - } - } - - if (ch) { - switch (event) { - case EVENT_TONE_GENERATE: - break; - case EVENT_DISCONNECT: - case EVENT_RELEASE: - case EVENT_RELEASE_COMPLETE: - case EVENT_CLEANUP: - case EVENT_TIMEOUT: - if (!ch->ast) - chan_misdn_log(3, bc->port, "ast_hangup already called, so we have no ast ptr anymore in event(%s)\n", manager_isdn_get_info(event)); - break; - default: - if (!ch->ast || !MISDN_ASTERISK_PVT(ch->ast) || !MISDN_ASTERISK_TECH_PVT(ch->ast)) { - if (event != EVENT_BCHAN_DATA) - ast_log(LOG_NOTICE, "No Ast or No private Pointer in Event (%d:%s)\n", event, manager_isdn_get_info(event)); - return -1; - } - } - } - - - switch (event) { - case EVENT_PORT_ALARM: - { - int boa = 0; - misdn_cfg_get(bc->port, MISDN_CFG_ALARM_BLOCK, &boa, sizeof(int)); - if (boa) { - cb_log(1, bc->port, " --> blocking\n"); - misdn_lib_port_block(bc->port); - } - } - break; - case EVENT_BCHAN_ACTIVATED: - break; - - case EVENT_NEW_CHANNEL: - update_name(ch->ast,bc->port,bc->channel); - break; - - case EVENT_NEW_L3ID: - ch->l3id=bc->l3_id; - ch->addr=bc->addr; - break; - - case EVENT_NEW_BC: - if (!ch) { - ch = find_holded(cl_te,bc); - } - - if (!ch) { - ast_log(LOG_WARNING, "NEW_BC without chan_list?\n"); - break; - } - - if (bc) - ch->bc = (struct misdn_bchannel *)user_data; - break; - - case EVENT_DTMF_TONE: - { - /* sending INFOS as DTMF-Frames :) */ - struct ast_frame fr; - - memset(&fr, 0, sizeof(fr)); - fr.frametype = AST_FRAME_DTMF; - fr.subclass = bc->dtmf ; - fr.src = NULL; - fr.data = NULL; - fr.datalen = 0; - fr.samples = 0; - fr.mallocd = 0; - fr.offset = 0; - fr.delivery = ast_tv(0,0); - - if (!ch->ignore_dtmf) { - chan_misdn_log(2, bc->port, " --> DTMF:%c\n", bc->dtmf); - ast_queue_frame(ch->ast, &fr); - } else { - chan_misdn_log(2, bc->port, " --> Ignoring DTMF:%c due to bridge flags\n", bc->dtmf); - } - } - break; - case EVENT_STATUS: - break; - - case EVENT_INFORMATION: - { - if ( ch->state != MISDN_CONNECTED ) - stop_indicate(ch); - - if (!ch->ast) - break; - - if (ch->state == MISDN_WAITING4DIGS ) { - /* Ok, incomplete Setup, waiting till extension exists */ - if (ast_strlen_zero(bc->info_dad) && ! ast_strlen_zero(bc->keypad)) { - chan_misdn_log(1, bc->port, " --> using keypad as info\n"); - ast_copy_string(bc->info_dad, bc->keypad, sizeof(bc->info_dad)); - } - - strncat(bc->dad, bc->info_dad, sizeof(bc->dad) - strlen(bc->dad) - 1); - ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten)); - - /* Check for Pickup Request first */ - if (!strcmp(ch->ast->exten, ast_pickup_ext())) { - if (ast_pickup_call(ch->ast)) { - hangup_chan(ch); - } else { - struct ast_channel *chan = ch->ast; - ch->state = MISDN_CALLING_ACKNOWLEDGE; - ast_setstate(chan, AST_STATE_DOWN); - hangup_chan(ch); - ch->ast = NULL; - break; - } - } - - if (!ast_canmatch_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { - if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->oad)) { - ast_log(LOG_WARNING, "Extension can never match, So jumping to 'i' extension. port(%d)\n", bc->port); - strcpy(ch->ast->exten, "i"); - - ch->state = MISDN_DIALING; - start_pbx(ch, bc, ch->ast); - break; - } - - ast_log(LOG_WARNING, "Extension can never match, so disconnecting on port(%d).\n" - "\tMaybe you want to add an 'i' extension to catch this case.\n", - bc->port); - - if (bc->nt) - hanguptone_indicate(ch); - ch->state = MISDN_EXTCANTMATCH; - bc->out_cause = AST_CAUSE_UNALLOCATED; - - misdn_lib_send_event(bc, EVENT_DISCONNECT); - break; - } - - if (ch->overlap_dial) { - ast_mutex_lock(&ch->overlap_tv_lock); - ch->overlap_tv = ast_tvnow(); - ast_mutex_unlock(&ch->overlap_tv_lock); - if (ch->overlap_dial_task == -1) { - ch->overlap_dial_task = - misdn_tasks_add_variable(ch->overlap_dial, misdn_overlap_dial_task, ch); - } - break; - } - - if (ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { - - ch->state = MISDN_DIALING; - start_pbx(ch, bc, ch->ast); - } - } else { - /* sending INFOS as DTMF-Frames :) */ - struct ast_frame fr; - int digits; - - memset(&fr, 0, sizeof(fr)); - fr.frametype = AST_FRAME_DTMF; - fr.subclass = bc->info_dad[0] ; - fr.src = NULL; - fr.data = NULL; - fr.datalen = 0; - fr.samples = 0; - fr.mallocd = 0; - fr.offset = 0; - fr.delivery = ast_tv(0,0); - - misdn_cfg_get(0, MISDN_GEN_APPEND_DIGITS2EXTEN, &digits, sizeof(int)); - if (ch->state != MISDN_CONNECTED ) { - if (digits) { - strncat(bc->dad, bc->info_dad, sizeof(bc->dad) - strlen(bc->dad) - 1); - ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten)); - ast_cdr_update(ch->ast); - } - - ast_queue_frame(ch->ast, &fr); - } - } - } - break; - case EVENT_SETUP: - { - struct chan_list *ch = find_chan_by_bc(cl_te, bc); - struct ast_channel *chan; - int exceed; - int pres, screen; - int ai; - int im; - - if (ch) { - switch (ch->state) { - case MISDN_NOTHING: - ch = NULL; - break; - default: - chan_misdn_log(1, bc->port, " --> Ignoring Call we have already one\n"); - return RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE; /* Ignore MSNs which are not in our List */ - } - } - - msn_valid = misdn_cfg_is_msn_valid(bc->port, bc->dad); - if (!bc->nt && ! msn_valid) { - chan_misdn_log(1, bc->port, " --> Ignoring Call, its not in our MSN List\n"); - return RESPONSE_IGNORE_SETUP; /* Ignore MSNs which are not in our List */ - } - - if (bc->cw) { - int cause; - chan_misdn_log(0, bc->port, " --> Call Waiting on PMP sending RELEASE_COMPLETE\n"); - misdn_cfg_get(bc->port, MISDN_CFG_REJECT_CAUSE, &cause, sizeof(cause)); - bc->out_cause = cause ? cause : AST_CAUSE_NORMAL_CLEARING; - return RESPONSE_RELEASE_SETUP; - } - - print_bearer(bc); - - ch = init_chan_list(ORG_MISDN); - - if (!ch) { - chan_misdn_log(-1, bc->port, "cb_events: malloc for chan_list failed!\n"); - return 0; - } - - ch->bc = bc; - ch->l3id = bc->l3_id; - ch->addr = bc->addr; - ch->originator = ORG_MISDN; - - chan = misdn_new(ch, AST_STATE_RESERVED, bc->dad, bc->oad, AST_FORMAT_ALAW, bc->port, bc->channel); - if (!chan) { - misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); - ast_log(LOG_ERROR, "cb_events: misdn_new failed !\n"); - return 0; - } - - ch->ast = chan; - - if ((exceed = add_in_calls(bc->port))) { - char tmp[16]; - snprintf(tmp, sizeof(tmp), "%d", exceed); - pbx_builtin_setvar_helper(chan, "MAX_OVERFLOW", tmp); - } - - read_config(ch, ORG_MISDN); - - export_ch(chan, bc, ch); - - ch->ast->rings = 1; - ast_setstate(ch->ast, AST_STATE_RINGING); - - switch (bc->pres) { - case 1: - pres = AST_PRES_RESTRICTED; - chan_misdn_log(2, bc->port, " --> PRES: Restricted (1)\n"); - break; - case 2: - pres = AST_PRES_UNAVAILABLE; - chan_misdn_log(2, bc->port, " --> PRES: Unavailable (2)\n"); - break; - default: - pres = AST_PRES_ALLOWED; - chan_misdn_log(2, bc->port, " --> PRES: Allowed (%d)\n", bc->pres); - break; - } - - switch (bc->screen) { - default: - case 0: - screen = AST_PRES_USER_NUMBER_UNSCREENED; - chan_misdn_log(2, bc->port, " --> SCREEN: Unscreened (%d)\n", bc->screen); - break; - case 1: - screen = AST_PRES_USER_NUMBER_PASSED_SCREEN; - chan_misdn_log(2, bc->port, " --> SCREEN: Passed screen (1)\n"); - break; - case 2: - screen = AST_PRES_USER_NUMBER_FAILED_SCREEN; - chan_misdn_log(2, bc->port, " --> SCREEN: failed screen (2)\n"); - break; - case 3: - screen = AST_PRES_NETWORK_NUMBER; - chan_misdn_log(2, bc->port, " --> SCREEN: Network Number (3)\n"); - break; - } - - chan->cid.cid_pres = pres | screen; - - pbx_builtin_setvar_helper(chan, "TRANSFERCAPABILITY", ast_transfercapability2str(bc->capability)); - chan->transfercapability = bc->capability; - - switch (bc->capability) { - case INFO_CAPABILITY_DIGITAL_UNRESTRICTED: - pbx_builtin_setvar_helper(chan, "CALLTYPE", "DIGITAL"); - break; - default: - pbx_builtin_setvar_helper(chan, "CALLTYPE", "SPEECH"); - } - - /** queue new chan **/ - cl_queue_chan(&cl_te, ch); - - if (!strstr(ch->allowed_bearers, "all")) { - int i; - - for (i = 0; i < ARRAY_LEN(allowed_bearers_array); ++i) { - if (allowed_bearers_array[i].cap == bc->capability) { - if (strstr(ch->allowed_bearers, allowed_bearers_array[i].name)) { - /* The bearer capability is allowed */ - if (allowed_bearers_array[i].deprecated) { - chan_misdn_log(0, bc->port, "%s in allowed_bearers list is deprecated\n", - allowed_bearers_array[i].name); - } - break; - } - } - } /* end for */ - if (i == ARRAY_LEN(allowed_bearers_array)) { - /* We did not find the bearer capability */ - chan_misdn_log(0, bc->port, "Bearer capability not allowed: %s(%d)\n", - bearer2str(bc->capability), bc->capability); - bc->out_cause = AST_CAUSE_INCOMPATIBLE_DESTINATION; - - ch->state = MISDN_EXTCANTMATCH; - misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); - return RESPONSE_OK; - } - } - - /* Check for Pickup Request first */ - if (!strcmp(chan->exten, ast_pickup_ext())) { - if (!ch->noautorespond_on_setup) { - int ret;/** Sending SETUP_ACK**/ - ret = misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE ); - } else { - ch->state = MISDN_INCOMING_SETUP; - } - if (ast_pickup_call(chan)) { - hangup_chan(ch); - } else { - ch->state = MISDN_CALLING_ACKNOWLEDGE; - ast_setstate(chan, AST_STATE_DOWN); - hangup_chan(ch); - ch->ast = NULL; - break; - } - } - - /* - * added support for s extension hope it will help those poor cretains - * which haven't overlap dial. - */ - misdn_cfg_get(bc->port, MISDN_CFG_ALWAYS_IMMEDIATE, &ai, sizeof(ai)); - if (ai) { - do_immediate_setup(bc, ch, chan); - break; - } - - /* check if we should jump into s when we have no dad */ - misdn_cfg_get(bc->port, MISDN_CFG_IMMEDIATE, &im, sizeof(im)); - if (im && ast_strlen_zero(bc->dad)) { - do_immediate_setup(bc, ch, chan); - break; - } - - chan_misdn_log(5, bc->port, "CONTEXT:%s\n", ch->context); - if(!ast_canmatch_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { - if (ast_exists_extension(ch->ast, ch->context, "i", 1, bc->oad)) { - ast_log(LOG_WARNING, "Extension can never match, So jumping to 'i' extension. port(%d)\n", bc->port); - strcpy(ch->ast->exten, "i"); - misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE); - ch->state = MISDN_DIALING; - start_pbx(ch, bc, chan); - break; - } - - ast_log(LOG_WARNING, "Extension can never match, so disconnecting on port(%d).\n" - "\tMaybe you want to add an 'i' extension to catch this case.\n", - bc->port); - if (bc->nt) - hanguptone_indicate(ch); - - ch->state = MISDN_EXTCANTMATCH; - bc->out_cause = AST_CAUSE_UNALLOCATED; - - if (bc->nt) - misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE ); - else - misdn_lib_send_event(bc, EVENT_RELEASE ); - - break; - } - - /* Whatever happens, when sending_complete is set or we are PTMP TE, we will definitely - * jump into the dialplan, when the dialed extension does not exist, the 's' extension - * will be used by Asterisk automatically. */ - if (bc->sending_complete || (!bc->nt && !misdn_lib_is_ptp(bc->port))) { - if (!ch->noautorespond_on_setup) { - ch->state=MISDN_DIALING; - misdn_lib_send_event(bc, EVENT_PROCEEDING ); - } else { - ch->state = MISDN_INCOMING_SETUP; - } - start_pbx(ch, bc, chan); - break; - } - - - /* - * When we are NT and overlapdial is set and if - * the number is empty, we wait for the ISDN timeout - * instead of our own timer. - */ - if (ch->overlap_dial && bc->nt && !bc->dad[0] ) { - wait_for_digits(ch, bc, chan); - break; - } - - /* - * If overlapdial we will definitely send a SETUP_ACKNOWLEDGE and wait for more - * Infos with a Interdigit Timeout. - * */ - if (ch->overlap_dial) { - ast_mutex_lock(&ch->overlap_tv_lock); - ch->overlap_tv = ast_tvnow(); - ast_mutex_unlock(&ch->overlap_tv_lock); - - wait_for_digits(ch, bc, chan); - if (ch->overlap_dial_task == -1) - ch->overlap_dial_task = - misdn_tasks_add_variable(ch->overlap_dial, misdn_overlap_dial_task, ch); - - break; - } - - /* If the extension does not exist and we're not TE_PTMP we wait for more digits - * without interdigit timeout. - * */ - if (!ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { - wait_for_digits(ch, bc, chan); - break; - } - - /* - * If the extension exists let's just jump into it. - * */ - if (ast_exists_extension(ch->ast, ch->context, bc->dad, 1, bc->oad)) { - if (bc->need_more_infos) - misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE ); - else - misdn_lib_send_event(bc, EVENT_PROCEEDING); - - ch->state = MISDN_DIALING; - start_pbx(ch, bc, chan); - break; - } - } - break; - - case EVENT_SETUP_ACKNOWLEDGE: - { - ch->state = MISDN_CALLING_ACKNOWLEDGE; - - if (bc->channel) - update_name(ch->ast,bc->port,bc->channel); - - if (!ast_strlen_zero(bc->infos_pending)) { - /* TX Pending Infos */ - strncat(bc->dad, bc->infos_pending, sizeof(bc->dad) - strlen(bc->dad) - 1); - - if (!ch->ast) - break; - ast_copy_string(ch->ast->exten, bc->dad, sizeof(ch->ast->exten)); - ast_copy_string(bc->info_dad, bc->infos_pending, sizeof(bc->info_dad)); - ast_copy_string(bc->infos_pending, "", sizeof(bc->infos_pending)); - - misdn_lib_send_event(bc, EVENT_INFORMATION); - } - } - break; - case EVENT_PROCEEDING: - { - if (bc->channel) - update_name(ch->ast, bc->port, bc->channel); - - if (misdn_cap_is_speech(bc->capability) && - misdn_inband_avail(bc) ) { - start_bc_tones(ch); - } - - ch->state = MISDN_PROCEEDING; - - if (!ch->ast) - break; - - ast_queue_control(ch->ast, AST_CONTROL_PROCEEDING); - } - break; - case EVENT_PROGRESS: - if (bc->channel) - update_name(ch->ast, bc->port, bc->channel); - - if (!bc->nt ) { - if ( misdn_cap_is_speech(bc->capability) && - misdn_inband_avail(bc) - ) { - start_bc_tones(ch); - } - - ch->state = MISDN_PROGRESS; - - if (!ch->ast) - break; - ast_queue_control(ch->ast, AST_CONTROL_PROGRESS); - } - break; - - - case EVENT_ALERTING: - { - if (bc->channel) - update_name(ch->ast, bc->port, bc->channel); - - ch->state = MISDN_ALERTING; - - if (!ch->ast) - break; - - ast_queue_control(ch->ast, AST_CONTROL_RINGING); - ast_setstate(ch->ast, AST_STATE_RINGING); - - cb_log(7, bc->port, " --> Set State Ringing\n"); - - if (misdn_cap_is_speech(bc->capability) && misdn_inband_avail(bc)) { - cb_log(1, bc->port, "Starting Tones, we have inband Data\n"); - start_bc_tones(ch); - } else { - cb_log(3, bc->port, " --> We have no inband Data, the other end must create ringing\n"); - if (ch->far_alerting) { - cb_log(1, bc->port, " --> The other end can not do ringing eh ?.. we must do all ourself.."); - start_bc_tones(ch); - /*tone_indicate(ch, TONE_FAR_ALERTING);*/ - } - } - } - break; - case EVENT_CONNECT: - { - struct ast_channel *bridged; - - /*we answer when we've got our very new L3 ID from the NT stack */ - misdn_lib_send_event(bc, EVENT_CONNECT_ACKNOWLEDGE); - - if (!ch->ast) - break; - - bridged = ast_bridged_channel(ch->ast); - stop_indicate(ch); - - if (bridged && !strcasecmp(bridged->tech->type, "mISDN")) { - struct chan_list *bridged_ch = MISDN_ASTERISK_TECH_PVT(bridged); - - chan_misdn_log(1, bc->port, " --> copying cpndialplan:%d and cad:%s to the A-Channel\n", bc->cpnnumplan, bc->cad); - if (bridged_ch) { - bridged_ch->bc->cpnnumplan = bc->cpnnumplan; - ast_copy_string(bridged_ch->bc->cad, bc->cad, sizeof(bridged_ch->bc->cad)); - } - } - } - ch->l3id=bc->l3_id; - ch->addr=bc->addr; - - start_bc_tones(ch); - - ch->state = MISDN_CONNECTED; - - ast_queue_control(ch->ast, AST_CONTROL_ANSWER); - break; - case EVENT_CONNECT_ACKNOWLEDGE: - { - ch->l3id = bc->l3_id; - ch->addr = bc->addr; - - start_bc_tones(ch); - - ch->state = MISDN_CONNECTED; - } - break; - case EVENT_DISCONNECT: - /*we might not have an ch->ast ptr here anymore*/ - if (ch) { - struct chan_list *holded_ch = find_holded(cl_te, bc); - - chan_misdn_log(3, bc->port, " --> org:%d nt:%d, inbandavail:%d state:%d\n", ch->originator, bc->nt, misdn_inband_avail(bc), ch->state); - if (ch->originator == ORG_AST && !bc->nt && misdn_inband_avail(bc) && ch->state != MISDN_CONNECTED) { - /* If there's inband information available (e.g. a - recorded message saying what was wrong with the - dialled number, or perhaps even giving an - alternative number, then play it instead of - immediately releasing the call */ - chan_misdn_log(1, bc->port, " --> Inband Info Avail, not sending RELEASE\n"); - - ch->state = MISDN_DISCONNECTED; - start_bc_tones(ch); - - if (ch->ast) { - ch->ast->hangupcause = bc->cause; - if (bc->cause == AST_CAUSE_USER_BUSY) - ast_queue_control(ch->ast, AST_CONTROL_BUSY); - } - ch->need_busy = 0; - break; - } - - /*Check for holded channel, to implement transfer*/ - if (holded_ch && holded_ch != ch && ch->ast && ch->state == MISDN_CONNECTED) { - cb_log(1, bc->port, " --> found holded ch\n"); - misdn_transfer_bc(ch, holded_ch) ; - } - - bc->need_disconnect = 0; - - stop_bc_tones(ch); - hangup_chan(ch); -#if 0 - } else { - ch = find_holded_l3(cl_te, bc->l3_id,1); - if (ch) { - hangup_chan(ch); - } -#endif - } - bc->out_cause = -1; - if (bc->need_release) - misdn_lib_send_event(bc, EVENT_RELEASE); - break; - - case EVENT_RELEASE: - { - bc->need_disconnect = 0; - bc->need_release = 0; - - hangup_chan(ch); - release_chan(bc); - } - break; - case EVENT_RELEASE_COMPLETE: - { - bc->need_disconnect = 0; - bc->need_release = 0; - bc->need_release_complete = 0; - - stop_bc_tones(ch); - hangup_chan(ch); - - if (ch) - ch->state = MISDN_CLEANING; - - release_chan(bc); - } - break; - case EVENT_BCHAN_ERROR: - case EVENT_CLEANUP: - { - stop_bc_tones(ch); - - switch (ch->state) { - case MISDN_CALLING: - bc->cause = AST_CAUSE_DESTINATION_OUT_OF_ORDER; - break; - default: - break; - } - - hangup_chan(ch); - release_chan(bc); - } - break; - - case EVENT_TONE_GENERATE: - { - int tone_len = bc->tone_cnt; - struct ast_channel *ast = ch->ast; - void *tmp; - int res; - int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples); - - chan_misdn_log(9, bc->port, "TONE_GEN: len:%d\n", tone_len); - - if (!ast) - break; - - if (!ast->generator) - break; - - tmp = ast->generatordata; - ast->generatordata = NULL; - generate = ast->generator->generate; - - if (tone_len < 0 || tone_len > 512 ) { - ast_log(LOG_NOTICE, "TONE_GEN: len was %d, set to 128\n", tone_len); - tone_len = 128; - } - - res = generate(ast, tmp, tone_len, tone_len); - ast->generatordata = tmp; - - if (res) { - ast_log(LOG_WARNING, "Auto-deactivating generator\n"); - ast_deactivate_generator(ast); - } else { - bc->tone_cnt = 0; - } - } - break; - - case EVENT_BCHAN_DATA: - { - if (!misdn_cap_is_speech(ch->bc->capability)) { - struct ast_frame frame; - /*In Data Modes we queue frames*/ - frame.frametype = AST_FRAME_VOICE; /*we have no data frames yet*/ - frame.subclass = AST_FORMAT_ALAW; - frame.datalen = bc->bframe_len; - frame.samples = bc->bframe_len; - frame.mallocd = 0; - frame.offset = 0; - frame.delivery = ast_tv(0,0); - frame.src = NULL; - frame.data = bc->bframe; - - if (ch->ast) - ast_queue_frame(ch->ast, &frame); - } else { - fd_set wrfs; - struct timeval tv = { 0, 0 }; - int t; - - FD_ZERO(&wrfs); - FD_SET(ch->pipe[1], &wrfs); - - t = select(FD_SETSIZE, NULL, &wrfs, NULL, &tv); - - if (!t) { - chan_misdn_log(9, bc->port, "Select Timed out\n"); - break; - } - - if (t < 0) { - chan_misdn_log(-1, bc->port, "Select Error (err=%s)\n", strerror(errno)); - break; - } - - if (FD_ISSET(ch->pipe[1], &wrfs)) { - chan_misdn_log(9, bc->port, "writing %d bytes to asterisk\n", bc->bframe_len); - if (write(ch->pipe[1], bc->bframe, bc->bframe_len) <= 0) { - chan_misdn_log(0, bc->port, "Write returned <=0 (err=%s) --> hanging up channel\n", strerror(errno)); - - stop_bc_tones(ch); - hangup_chan(ch); - release_chan(bc); - } - } else { - chan_misdn_log(1, bc->port, "Write Pipe full!\n"); - } - } - } - break; - case EVENT_TIMEOUT: - { - if (ch && bc) - chan_misdn_log(1, bc->port, "--> state: %s\n", misdn_get_ch_state(ch)); - - switch (ch->state) { - case MISDN_DIALING: - case MISDN_PROGRESS: - if (bc->nt && !ch->nttimeout) - break; - - case MISDN_CALLING: - case MISDN_ALERTING: - case MISDN_PROCEEDING: - case MISDN_CALLING_ACKNOWLEDGE: - if (bc->nt) { - bc->progress_indicator = INFO_PI_INBAND_AVAILABLE; - hanguptone_indicate(ch); - } - - bc->out_cause = AST_CAUSE_UNALLOCATED; - misdn_lib_send_event(bc, EVENT_DISCONNECT); - break; - - case MISDN_WAITING4DIGS: - if (bc->nt) { - bc->progress_indicator = INFO_PI_INBAND_AVAILABLE; - bc->out_cause = AST_CAUSE_UNALLOCATED; - hanguptone_indicate(ch); - misdn_lib_send_event(bc, EVENT_DISCONNECT); - } else { - bc->out_cause = AST_CAUSE_NORMAL_CLEARING; - misdn_lib_send_event(bc, EVENT_RELEASE); - } - - break; - - case MISDN_CLEANING: - chan_misdn_log(1,bc->port," --> in state cleaning .. so ignoring, the stack should clean it for us\n"); - break; - - default: - misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); - } - } - break; - - - /****************************/ - /** Supplementary Services **/ - /****************************/ - case EVENT_RETRIEVE: - { - struct ast_channel *hold_ast; - - if (!ch) { - chan_misdn_log(4, bc->port, " --> no CH, searching in holded\n"); - ch = find_holded_l3(cl_te, bc->l3_id, 1); - } - - if (!ch) { - ast_log(LOG_WARNING, "Found no Holded channel, cannot Retrieve\n"); - misdn_lib_send_event(bc, EVENT_RETRIEVE_REJECT); - break; - } - - /*remember the channel again*/ - ch->bc = bc; - ch->state = MISDN_CONNECTED; - - ch->hold_info.port = 0; - ch->hold_info.channel = 0; - - hold_ast = ast_bridged_channel(ch->ast); - - if (hold_ast) { - ast_moh_stop(hold_ast); - } - - if (misdn_lib_send_event(bc, EVENT_RETRIEVE_ACKNOWLEDGE) < 0) { - chan_misdn_log(4, bc->port, " --> RETRIEVE_ACK failed\n"); - misdn_lib_send_event(bc, EVENT_RETRIEVE_REJECT); - } - } - break; - - case EVENT_HOLD: - { - int hold_allowed; - struct ast_channel *bridged; - - misdn_cfg_get(bc->port, MISDN_CFG_HOLD_ALLOWED, &hold_allowed, sizeof(int)); - - if (!hold_allowed) { - - chan_misdn_log(-1, bc->port, "Hold not allowed this port.\n"); - misdn_lib_send_event(bc, EVENT_HOLD_REJECT); - break; - } - - bridged = ast_bridged_channel(ch->ast); - if (bridged) { - chan_misdn_log(2, bc->port, "Bridge Partner is of type: %s\n", bridged->tech->type); - ch->state = MISDN_HOLDED; - ch->l3id = bc->l3_id; - - misdn_lib_send_event(bc, EVENT_HOLD_ACKNOWLEDGE); - - /* XXX This should queue an AST_CONTROL_HOLD frame on this channel - * instead of starting moh on the bridged channel directly */ - ast_moh_start(bridged, NULL, NULL); - - /*forget the channel now*/ - ch->bc = NULL; - ch->hold_info.port = bc->port; - ch->hold_info.channel = bc->channel; - - } else { - misdn_lib_send_event(bc, EVENT_HOLD_REJECT); - chan_misdn_log(0, bc->port, "We aren't bridged to anybody\n"); - } - } - break; - - case EVENT_FACILITY: - if (!ch) { - /* This may come from a call we don't know nothing about, so we ignore it. */ - chan_misdn_log(-1, bc->port, "Got EVENT_FACILITY but we don't have a ch!\n"); - break; - } - - print_facility(&(bc->fac_in), bc); - - switch (bc->fac_in.Function) { - case Fac_CD: - { - struct ast_channel *bridged = ast_bridged_channel(ch->ast); - struct chan_list *ch_br; - if (bridged && MISDN_ASTERISK_TECH_PVT(bridged)) { - ch_br = MISDN_ASTERISK_TECH_PVT(bridged); - /*ch->state = MISDN_FACILITY_DEFLECTED;*/ - if (ch_br->bc) { - if (ast_exists_extension(bridged, ch->context, (char *)bc->fac_in.u.CDeflection.DeflectedToNumber, 1, bc->oad)) { - ch_br->state = MISDN_DIALING; - if (pbx_start_chan(ch_br) < 0) { - chan_misdn_log(-1, ch_br->bc->port, "ast_pbx_start returned < 0 in misdn_overlap_dial_task\n"); - } - } - } - } - misdn_lib_send_event(bc, EVENT_DISCONNECT); - } - break; - case Fac_AOCDCurrency: - { - bc->AOCDtype = Fac_AOCDCurrency; - memcpy(&(bc->AOCD.currency), &(bc->fac_in.u.AOCDcur), sizeof(struct FacAOCDCurrency)); - export_aoc_vars(ch->originator, ch->ast, bc); - } - break; - case Fac_AOCDChargingUnit: - { - bc->AOCDtype = Fac_AOCDChargingUnit; - memcpy(&(bc->AOCD.chargingUnit), &(bc->fac_in.u.AOCDchu), sizeof(struct FacAOCDChargingUnit)); - export_aoc_vars(ch->originator, ch->ast, bc); - } - break; - default: - chan_misdn_log(0, bc->port," --> not yet handled: facility type:%d\n", bc->fac_in.Function); - } - - break; - - case EVENT_RESTART: - - if (!bc->dummy) { - stop_bc_tones(ch); - release_chan(bc); - } - break; - - default: - chan_misdn_log(1, 0, "Got Unknown Event\n"); - break; - } - - return RESPONSE_OK; -} - -/** TE STUFF END **/ - -/****************************************** - * - * Asterisk Channel Endpoint END - * - * - *******************************************/ - - - -static int unload_module(void) -{ - /* First, take us out of the channel loop */ - ast_log(LOG_VERBOSE, "-- Unregistering mISDN Channel Driver --\n"); - - misdn_tasks_destroy(); - - if (!g_config_initialized) - return 0; - - ast_cli_unregister_multiple(chan_misdn_clis, sizeof(chan_misdn_clis) / sizeof(struct ast_cli_entry)); - - /* ast_unregister_application("misdn_crypt"); */ - ast_unregister_application("misdn_set_opt"); - ast_unregister_application("misdn_facility"); - ast_unregister_application("misdn_check_l2l1"); - - ast_channel_unregister(&misdn_tech); - - free_robin_list(); - misdn_cfg_destroy(); - misdn_lib_destroy(); - - if (misdn_debug) - free(misdn_debug); - if (misdn_debug_only) - free(misdn_debug_only); - free(misdn_ports); - - return 0; -} - -static int load_module(void) -{ - int i, port; - int ntflags = 0, ntkc = 0; - char ports[256] = ""; - char tempbuf[BUFFERSIZE + 1]; - char ntfile[BUFFERSIZE + 1]; - struct misdn_lib_iface iface = { - .cb_event = cb_events, - .cb_log = chan_misdn_log, - .cb_jb_empty = chan_misdn_jb_empty, - }; - - max_ports = misdn_lib_maxports_get(); - - if (max_ports <= 0) { - ast_log(LOG_ERROR, "Unable to initialize mISDN\n"); - return AST_MODULE_LOAD_DECLINE; - } - - if (misdn_cfg_init(max_ports)) { - ast_log(LOG_ERROR, "Unable to initialize misdn_config.\n"); - return AST_MODULE_LOAD_DECLINE; - } - g_config_initialized = 1; - - misdn_debug = (int *) malloc(sizeof(int) * (max_ports + 1)); - if (!misdn_debug) { - ast_log(LOG_ERROR, "Out of memory for misdn_debug\n"); - return AST_MODULE_LOAD_DECLINE; - } - misdn_ports = (int *) malloc(sizeof(int) * (max_ports + 1)); - if (!misdn_ports) { - ast_log(LOG_ERROR, "Out of memory for misdn_ports\n"); - return AST_MODULE_LOAD_DECLINE; - } - misdn_cfg_get(0, MISDN_GEN_DEBUG, &misdn_debug[0], sizeof(int)); - for (i = 1; i <= max_ports; i++) { - misdn_debug[i] = misdn_debug[0]; - misdn_ports[i] = i; - } - *misdn_ports = 0; - misdn_debug_only = (int *) calloc(max_ports + 1, sizeof(int)); - - misdn_cfg_get(0, MISDN_GEN_TRACEFILE, tempbuf, BUFFERSIZE); - if (!ast_strlen_zero(tempbuf)) - tracing = 1; - - misdn_in_calls = (int *) malloc(sizeof(int) * (max_ports + 1)); - misdn_out_calls = (int *) malloc(sizeof(int) * (max_ports + 1)); - - for (i = 1; i <= max_ports; i++) { - misdn_in_calls[i] = 0; - misdn_out_calls[i] = 0; - } - - ast_mutex_init(&cl_te_lock); - ast_mutex_init(&release_lock); - - misdn_cfg_update_ptp(); - misdn_cfg_get_ports_string(ports); - - if (!ast_strlen_zero(ports)) - chan_misdn_log(0, 0, "Got: %s from get_ports\n", ports); - if (misdn_lib_init(ports, &iface, NULL)) - chan_misdn_log(0, 0, "No te ports initialized\n"); - - misdn_cfg_get(0, MISDN_GEN_NTDEBUGFLAGS, &ntflags, sizeof(int)); - misdn_cfg_get(0, MISDN_GEN_NTDEBUGFILE, &ntfile, BUFFERSIZE); - misdn_lib_nt_debug_init(ntflags, ntfile); - - misdn_cfg_get( 0, MISDN_GEN_NTKEEPCALLS, &ntkc, sizeof(int)); - misdn_lib_nt_keepcalls(ntkc); - - if (ast_channel_register(&misdn_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class %s\n", misdn_type); - unload_module(); - return -1; - } - - ast_cli_register_multiple(chan_misdn_clis, sizeof(chan_misdn_clis) / sizeof(struct ast_cli_entry)); - - ast_register_application("misdn_set_opt", misdn_set_opt_exec, "misdn_set_opt", - "misdn_set_opt(:<opt><optarg>:<opt><optarg>...):\n" - "Sets mISDN opts. and optargs\n" - "\n" - "The available options are:\n" - " a - Have Asterisk detect DTMF tones on called channel\n" - " c - Make crypted outgoing call, optarg is keyindex\n" - " d - Send display text to called phone, text is the optarg\n" - " e - Perform echo cancelation on this channel,\n" - " takes taps as optarg (32,64,128,256)\n" - " e! - Disable echo cancelation on this channel\n" - " f - Enable fax detection\n" - " h - Make digital outgoing call\n" - " h1 - Make HDLC mode digital outgoing call\n" - " i - Ignore detected DTMF tones, don't signal them to Asterisk,\n" - " they will be transported inband.\n" - " jb - Set jitter buffer length, optarg is length\n" - " jt - Set jitter buffer upper threshold, optarg is threshold\n" - " jn - Disable jitter buffer\n" - " n - Disable mISDN DSP on channel.\n" - " Disables: echo cancel, DTMF detection, and volume control.\n" - " p - Caller ID presentation,\n" - " optarg is either 'allowed' or 'restricted'\n" - " s - Send Non-inband DTMF as inband\n" - " vr - Rx gain control, optarg is gain\n" - " vt - Tx gain control, optarg is gain\n" - ); - - - ast_register_application("misdn_facility", misdn_facility_exec, "misdn_facility", - "misdn_facility(<FACILITY_TYPE>|<ARG1>|..)\n" - "Sends the Facility Message FACILITY_TYPE with \n" - "the given Arguments to the current ISDN Channel\n" - "Supported Facilities are:\n" - "\n" - "type=calldeflect args=Nr where to deflect\n" - ); - - - ast_register_application("misdn_check_l2l1", misdn_check_l2l1, "misdn_check_l2l1", - "misdn_check_l2l1(<port>||g:<groupname>,timeout)" - "Checks if the L2 and L1 are up on either the given <port> or\n" - "on the ports in the group with <groupname>\n" - "If the L1/L2 are down, check_l2l1 gets up the L1/L2 and waits\n" - "for <timeout> seconds that this happens. Otherwise, nothing happens\n" - "\n" - "This application, ensures the L1/L2 state of the Ports in a group\n" - "it is intended to make the pmp_l1_check option redundant and to\n" - "fix a buggy switch config from your provider\n" - "\n" - "a sample dialplan would look like:\n\n" - "exten => _X.,1,misdn_check_l2l1(g:out|2)\n" - "exten => _X.,n,dial(mISDN/g:out/${EXTEN})\n" - "\n" - ); - - - misdn_cfg_get(0, MISDN_GEN_TRACEFILE, global_tracefile, BUFFERSIZE); - - /* start the l1 watchers */ - - for (port = misdn_cfg_get_next_port(0); port >= 0; port = misdn_cfg_get_next_port(port)) { - int l1timeout; - misdn_cfg_get(port, MISDN_CFG_L1_TIMEOUT, &l1timeout, sizeof(l1timeout)); - if (l1timeout) { - chan_misdn_log(4, 0, "Adding L1watcher task: port:%d timeout:%ds\n", port, l1timeout); - misdn_tasks_add(l1timeout * 1000, misdn_l1_task, &misdn_ports[port]); - } - } - - chan_misdn_log(0, 0, "-- mISDN Channel Driver Registered --\n"); - - return 0; -} - - - -static int reload(void) -{ - reload_config(); - - return 0; -} - -/*** SOME APPS ;)***/ - -static int misdn_facility_exec(struct ast_channel *chan, void *data) -{ - struct chan_list *ch = MISDN_ASTERISK_TECH_PVT(chan); - char *tok, *tokb; - - chan_misdn_log(0, 0, "TYPE: %s\n", chan->tech->type); - - if (strcasecmp(chan->tech->type, "mISDN")) { - ast_log(LOG_WARNING, "misdn_facility makes only sense with chan_misdn channels!\n"); - return -1; - } - - if (ast_strlen_zero((char *)data)) { - ast_log(LOG_WARNING, "misdn_facility Requires arguments\n"); - return -1; - } - - tok = strtok_r((char*) data, "|", &tokb) ; - - if (!tok) { - ast_log(LOG_WARNING, "misdn_facility Requires arguments\n"); - return -1; - } - - if (!strcasecmp(tok, "calldeflect")) { - tok = strtok_r(NULL, "|", &tokb) ; - - if (!tok) { - ast_log(LOG_WARNING, "Facility: Call Defl Requires arguments\n"); - } - - if (strlen(tok) >= sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)) { - ast_log(LOG_WARNING, "Facility: Number argument too long (up to 15 digits are allowed). Ignoring.\n"); - return 0; - } - ch->bc->fac_out.Function = Fac_CD; - ast_copy_string((char *)ch->bc->fac_out.u.CDeflection.DeflectedToNumber, tok, sizeof(ch->bc->fac_out.u.CDeflection.DeflectedToNumber)); - misdn_lib_send_event(ch->bc, EVENT_FACILITY); - } else { - chan_misdn_log(1, ch->bc->port, "Unknown Facility: %s\n", tok); - } - - return 0; -} - -static int misdn_check_l2l1(struct ast_channel *chan, void *data) -{ - char group[BUFFERSIZE + 1]; - char *port_str; - int port = 0; - int timeout; - int dowait = 0; - int port_up; - - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(grouppar); - AST_APP_ARG(timeout); - ); - - if (ast_strlen_zero((char *)data)) { - ast_log(LOG_WARNING, "misdn_check_l2l1 Requires arguments\n"); - return -1; - } - - AST_STANDARD_APP_ARGS(args, data); - - if (args.argc != 2) { - ast_log(LOG_WARNING, "Wrong argument count\n"); - return 0; - } - - /*ast_log(LOG_NOTICE, "Arguments: group/port '%s' timeout '%s'\n", args.grouppar, args.timeout);*/ - timeout = atoi(args.timeout); - port_str = args.grouppar; - - if (port_str[0] == 'g' && port_str[1] == ':' ) { - /* We make a group call lets checkout which ports are in my group */ - port_str += 2; - ast_copy_string(group, port_str, sizeof(group)); - chan_misdn_log(2, 0, "Checking Ports in group: %s\n", group); - - for ( port = misdn_cfg_get_next_port(port); - port > 0; - port = misdn_cfg_get_next_port(port)) { - char cfg_group[BUFFERSIZE + 1]; - - chan_misdn_log(2, 0, "trying port %d\n", port); - - misdn_cfg_get(port, MISDN_CFG_GROUPNAME, cfg_group, BUFFERSIZE); - - if (!strcasecmp(cfg_group, group)) { - port_up = misdn_lib_port_up(port, 1); - - if (!port_up) { - chan_misdn_log(2, 0, " --> port '%d'\n", port); - misdn_lib_get_port_up(port); - dowait = 1; - } - } - } - - } else { - port = atoi(port_str); - chan_misdn_log(2, 0, "Checking Port: %d\n",port); - port_up = misdn_lib_port_up(port, 1); - if (!port_up) { - misdn_lib_get_port_up(port); - dowait = 1; - } - } - - if (dowait) { - chan_misdn_log(2, 0, "Waiting for '%d' seconds\n", timeout); - sleep(timeout); - } - - return 0; -} - -static int misdn_set_opt_exec(struct ast_channel *chan, void *data) -{ - struct chan_list *ch = MISDN_ASTERISK_TECH_PVT(chan); - char *tok, *tokb; - int keyidx = 0; - int rxgain = 0; - int txgain = 0; - int change_jitter = 0; - - if (strcasecmp(chan->tech->type, "mISDN")) { - ast_log(LOG_WARNING, "misdn_set_opt makes only sense with chan_misdn channels!\n"); - return -1; - } - - if (ast_strlen_zero((char *)data)) { - ast_log(LOG_WARNING, "misdn_set_opt Requires arguments\n"); - return -1; - } - - for (tok = strtok_r((char*) data, ":", &tokb); - tok; - tok = strtok_r(NULL, ":", &tokb) ) { - int neglect = 0; - - if (tok[0] == '!' ) { - neglect = 1; - tok++; - } - - switch(tok[0]) { - - case 'd' : - ast_copy_string(ch->bc->display, ++tok, sizeof(ch->bc->display)); - chan_misdn_log(1, ch->bc->port, "SETOPT: Display:%s\n", ch->bc->display); - break; - - case 'n': - chan_misdn_log(1, ch->bc->port, "SETOPT: No DSP\n"); - ch->bc->nodsp = 1; - break; - - case 'j': - chan_misdn_log(1, ch->bc->port, "SETOPT: jitter\n"); - tok++; - change_jitter = 1; - - switch ( tok[0] ) { - case 'b': - ch->jb_len = atoi(++tok); - chan_misdn_log(1, ch->bc->port, " --> buffer_len:%d\n", ch->jb_len); - break; - case 't' : - ch->jb_upper_threshold = atoi(++tok); - chan_misdn_log(1, ch->bc->port, " --> upper_threshold:%d\n", ch->jb_upper_threshold); - break; - case 'n': - ch->bc->nojitter = 1; - chan_misdn_log(1, ch->bc->port, " --> nojitter\n"); - break; - default: - ch->jb_len = 4000; - ch->jb_upper_threshold = 0; - chan_misdn_log(1, ch->bc->port, " --> buffer_len:%d (default)\n", ch->jb_len); - chan_misdn_log(1, ch->bc->port, " --> upper_threshold:%d (default)\n", ch->jb_upper_threshold); - } - break; - case 'v': - tok++; - - switch (tok[0]) { - case 'r' : - rxgain = atoi(++tok); - if (rxgain < -8) - rxgain = -8; - if (rxgain > 8) - rxgain = 8; - ch->bc->rxgain = rxgain; - chan_misdn_log(1, ch->bc->port, "SETOPT: Volume:%d\n", rxgain); - break; - case 't': - txgain = atoi(++tok); - if (txgain < -8) - txgain = -8; - if (txgain > 8) - txgain = 8; - ch->bc->txgain = txgain; - chan_misdn_log(1, ch->bc->port, "SETOPT: Volume:%d\n", txgain); - break; - } - break; - - case 'c': - keyidx = atoi(++tok); - { - char keys[4096]; - char *key = NULL, *tmp = keys; - int i; - misdn_cfg_get(0, MISDN_GEN_CRYPT_KEYS, keys, sizeof(keys)); - - for (i = 0; i < keyidx; i++) { - key = strsep(&tmp, ","); - } - - if (key) { - ast_copy_string(ch->bc->crypt_key, key, sizeof(ch->bc->crypt_key)); - } - - chan_misdn_log(0, ch->bc->port, "SETOPT: crypt with key:%s\n", ch->bc->crypt_key); - break; - } - case 'e': - chan_misdn_log(1, ch->bc->port, "SETOPT: EchoCancel\n"); - - if (neglect) { - chan_misdn_log(1, ch->bc->port, " --> disabled\n"); -#ifdef MISDN_1_2 - *ch->bc->pipeline = 0; -#else - ch->bc->ec_enable = 0; -#endif - } else { -#ifdef MISDN_1_2 - update_pipeline_config(ch->bc); -#else - ch->bc->ec_enable = 1; - ch->bc->orig = ch->originator; - tok++; - if (*tok) { - ch->bc->ec_deftaps = atoi(tok); - } -#endif - } - - break; - case 'h': - chan_misdn_log(1, ch->bc->port, "SETOPT: Digital\n"); - - if (strlen(tok) > 1 && tok[1] == '1') { - chan_misdn_log(1, ch->bc->port, "SETOPT: HDLC \n"); - if (!ch->bc->hdlc) { - ch->bc->hdlc = 1; - } - } - ch->bc->capability = INFO_CAPABILITY_DIGITAL_UNRESTRICTED; - break; - - case 's': - chan_misdn_log(1, ch->bc->port, "SETOPT: Send DTMF\n"); - ch->bc->send_dtmf = 1; - break; - - case 'f': - chan_misdn_log(1, ch->bc->port, "SETOPT: Faxdetect\n"); - ch->faxdetect = 1; - misdn_cfg_get(ch->bc->port, MISDN_CFG_FAXDETECT_TIMEOUT, &ch->faxdetect_timeout, sizeof(ch->faxdetect_timeout)); - break; - - case 'a': - chan_misdn_log(1, ch->bc->port, "SETOPT: AST_DSP (for DTMF)\n"); - ch->ast_dsp = 1; - break; - - case 'p': - chan_misdn_log(1, ch->bc->port, "SETOPT: callerpres: %s\n", &tok[1]); - /* CRICH: callingpres!!! */ - if (strstr(tok,"allowed")) { - ch->bc->pres = 0; - } else if (strstr(tok, "restricted")) { - ch->bc->pres = 1; - } else if (strstr(tok, "not_screened")) { - chan_misdn_log(0, ch->bc->port, "SETOPT: callerpres: not_screened is deprecated\n"); - ch->bc->pres = 1; - } - break; - case 'i' : - chan_misdn_log(1, ch->bc->port, "Ignoring dtmf tones, just use them inband\n"); - ch->ignore_dtmf=1; - break; - default: - break; - } - } - - if (change_jitter) - config_jitterbuffer(ch); - - if (ch->faxdetect || ch->ast_dsp) { - if (!ch->dsp) - ch->dsp = ast_dsp_new(); - if (ch->dsp) - ast_dsp_set_features(ch->dsp, DSP_FEATURE_DTMF_DETECT| DSP_FEATURE_FAX_DETECT); - if (!ch->trans) - ch->trans = ast_translator_build_path(AST_FORMAT_SLINEAR, AST_FORMAT_ALAW); - } - - if (ch->ast_dsp) { - chan_misdn_log(1, ch->bc->port, "SETOPT: with AST_DSP we deactivate mISDN_dsp\n"); - ch->bc->nodsp = 1; - ch->bc->nojitter = 1; - } - - return 0; -} - - -int chan_misdn_jb_empty ( struct misdn_bchannel *bc, char *buf, int len) -{ - struct chan_list *ch = find_chan_by_bc(cl_te, bc); - - if (ch && ch->jb) { - return misdn_jb_empty(ch->jb, buf, len); - } - - return -1; -} - - - -/*******************************************************/ -/***************** JITTERBUFFER ************************/ -/*******************************************************/ - - -/* allocates the jb-structure and initialize the elements*/ -struct misdn_jb *misdn_jb_init(int size, int upper_threshold) -{ - int i; - struct misdn_jb *jb; - - jb = malloc(sizeof(struct misdn_jb)); - if (!jb) { - chan_misdn_log(-1, 0, "No free Mem for jb\n"); - return NULL; - } - jb->size = size; - jb->upper_threshold = upper_threshold; - jb->wp = 0; - jb->rp = 0; - jb->state_full = 0; - jb->state_empty = 0; - jb->bytes_wrote = 0; - jb->samples = malloc(size * sizeof(char)); - if (!jb->samples) { - free(jb); - chan_misdn_log(-1, 0, "No free Mem for jb->samples\n"); - return NULL; - } - - jb->ok = malloc(size * sizeof(char)); - if (!jb->ok) { - free(jb->samples); - free(jb); - chan_misdn_log(-1, 0, "No free Mem for jb->ok\n"); - return NULL; - } - - for (i = 0; i < size; i++) - jb->ok[i] = 0; - - ast_mutex_init(&jb->mutexjb); - - return jb; -} - -/* frees the data and destroys the given jitterbuffer struct */ -void misdn_jb_destroy(struct misdn_jb *jb) -{ - ast_mutex_destroy(&jb->mutexjb); - - free(jb->ok); - free(jb->samples); - free(jb); -} - -/* fills the jitterbuffer with len data returns < 0 if there was an - error (buffer overflow). */ -int misdn_jb_fill(struct misdn_jb *jb, const char *data, int len) -{ - int i, j, rp, wp; - - if (!jb || ! data) - return 0; - - ast_mutex_lock(&jb->mutexjb); - - wp = jb->wp; - rp = jb->rp; - - for (i = 0; i < len; i++) { - jb->samples[wp] = data[i]; - jb->ok[wp] = 1; - wp = (wp != jb->size - 1) ? wp + 1 : 0; - - if (wp == jb->rp) - jb->state_full = 1; - } - - if (wp >= rp) - jb->state_buffer = wp - rp; - else - jb->state_buffer = jb->size - rp + wp; - chan_misdn_log(9, 0, "misdn_jb_fill: written:%d | Buffer status:%d p:%p\n", len, jb->state_buffer, jb); - - if (jb->state_full) { - jb->wp = wp; - - rp = wp; - for (j = 0; j < jb->upper_threshold; j++) - rp = (rp != 0) ? rp - 1 : jb->size - 1; - jb->rp = rp; - jb->state_full = 0; - jb->state_empty = 1; - - ast_mutex_unlock(&jb->mutexjb); - - return -1; - } - - if (!jb->state_empty) { - jb->bytes_wrote += len; - if (jb->bytes_wrote >= jb->upper_threshold) { - jb->state_empty = 1; - jb->bytes_wrote = 0; - } - } - jb->wp = wp; - - ast_mutex_unlock(&jb->mutexjb); - - return 0; -} - -/* gets len bytes out of the jitterbuffer if available, else only the -available data is returned and the return value indicates the number -of data. */ -int misdn_jb_empty(struct misdn_jb *jb, char *data, int len) -{ - int i, wp, rp, read = 0; - - ast_mutex_lock(&jb->mutexjb); - - rp = jb->rp; - wp = jb->wp; - - if (jb->state_empty) { - for (i = 0; i < len; i++) { - if (wp == rp) { - jb->rp = rp; - jb->state_empty = 0; - - ast_mutex_unlock(&jb->mutexjb); - - return read; - } else { - if (jb->ok[rp] == 1) { - data[i] = jb->samples[rp]; - jb->ok[rp] = 0; - rp = (rp != jb->size - 1) ? rp + 1 : 0; - read += 1; - } - } - } - - if (wp >= rp) - jb->state_buffer = wp - rp; - else - jb->state_buffer = jb->size - rp + wp; - chan_misdn_log(9, 0, "misdn_jb_empty: read:%d | Buffer status:%d p:%p\n", len, jb->state_buffer, jb); - - jb->rp = rp; - } else - chan_misdn_log(9, 0, "misdn_jb_empty: Wait...requested:%d p:%p\n", len, jb); - - ast_mutex_unlock(&jb->mutexjb); - - return read; -} - - - - -/*******************************************************/ -/*************** JITTERBUFFER END *********************/ -/*******************************************************/ - - - - -static void chan_misdn_log(int level, int port, char *tmpl, ...) -{ - va_list ap; - char buf[1024]; - char port_buf[8]; - - if (! ((0 <= port) && (port <= max_ports))) { - ast_log(LOG_WARNING, "cb_log called with out-of-range port number! (%d)\n", port); - port = 0; - level = -1; - } - - snprintf(port_buf, sizeof(port_buf), "P[%2d] ", port); - - va_start(ap, tmpl); - vsnprintf(buf, sizeof(buf), tmpl, ap); - va_end(ap); - - if (level == -1) - ast_log(LOG_WARNING, "%s", buf); - - else if (misdn_debug_only[port] ? - (level == 1 && misdn_debug[port]) || (level == misdn_debug[port]) - : level <= misdn_debug[port]) { - - ast_console_puts(port_buf); - ast_console_puts(buf); - } - - if ((level <= misdn_debug[0]) && !ast_strlen_zero(global_tracefile) ) { - time_t tm = time(NULL); - char *tmp = ctime(&tm), *p; - - FILE *fp = fopen(global_tracefile, "a+"); - - p = strchr(tmp, '\n'); - if (p) - *p = ':'; - - if (!fp) { - ast_console_puts("Error opening Tracefile: [ "); - ast_console_puts(global_tracefile); - ast_console_puts(" ] "); - - ast_console_puts(strerror(errno)); - ast_console_puts("\n"); - return ; - } - - fputs(tmp, fp); - fputs(" ", fp); - fputs(port_buf, fp); - fputs(" ", fp); - fputs(buf, fp); - - fclose(fp); - } -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Channel driver for mISDN Support (BRI/PRI)", - .load = load_module, - .unload = unload_module, - .reload = reload, -); diff --git a/1.4.23-rc4/channels/chan_nbs.c b/1.4.23-rc4/channels/chan_nbs.c deleted file mode 100644 index b231127ce..000000000 --- a/1.4.23-rc4/channels/chan_nbs.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief Network broadcast sound support channel driver - * - * \author Mark Spencer <markster@digium.com> - * - * \ingroup channel_drivers - */ - -/*** MODULEINFO - <depend>nbs</depend> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> -#include <arpa/inet.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <nbs.h> - -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/utils.h" - -static const char tdesc[] = "Network Broadcast Sound Driver"; - -/* Only linear is allowed */ -static int prefformat = AST_FORMAT_SLINEAR; - -static char context[AST_MAX_EXTENSION] = "default"; -static char type[] = "NBS"; - -/* NBS creates private structures on demand */ - -struct nbs_pvt { - NBS *nbs; - struct ast_channel *owner; /* Channel we belong to, possibly NULL */ - char app[16]; /* Our app */ - char stream[80]; /* Our stream */ - struct ast_frame fr; /* "null" frame */ - struct ast_module_user *u; /*! for holding a reference to this module */ -}; - -static struct ast_channel *nbs_request(const char *type, int format, void *data, int *cause); -static int nbs_call(struct ast_channel *ast, char *dest, int timeout); -static int nbs_hangup(struct ast_channel *ast); -static struct ast_frame *nbs_xread(struct ast_channel *ast); -static int nbs_xwrite(struct ast_channel *ast, struct ast_frame *frame); - -static const struct ast_channel_tech nbs_tech = { - .type = type, - .description = tdesc, - .capabilities = AST_FORMAT_SLINEAR, - .requester = nbs_request, - .call = nbs_call, - .hangup = nbs_hangup, - .read = nbs_xread, - .write = nbs_xwrite, -}; - -static int nbs_call(struct ast_channel *ast, char *dest, int timeout) -{ - struct nbs_pvt *p; - - p = ast->tech_pvt; - - if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { - ast_log(LOG_WARNING, "nbs_call called on %s, neither down nor reserved\n", ast->name); - return -1; - } - /* When we call, it just works, really, there's no destination... Just - ring the phone and wait for someone to answer */ - if (option_debug) - ast_log(LOG_DEBUG, "Calling %s on %s\n", dest, ast->name); - - /* If we can't connect, return congestion */ - if (nbs_connect(p->nbs)) { - ast_log(LOG_WARNING, "NBS Connection failed on %s\n", ast->name); - ast_queue_control(ast, AST_CONTROL_CONGESTION); - } else { - ast_setstate(ast, AST_STATE_RINGING); - ast_queue_control(ast, AST_CONTROL_ANSWER); - } - - return 0; -} - -static void nbs_destroy(struct nbs_pvt *p) -{ - if (p->nbs) - nbs_delstream(p->nbs); - ast_module_user_remove(p->u); - free(p); -} - -static struct nbs_pvt *nbs_alloc(void *data) -{ - struct nbs_pvt *p; - int flags = 0; - char stream[256]; - char *opts; - - ast_copy_string(stream, data, sizeof(stream)); - if ((opts = strchr(stream, ':'))) { - *opts = '\0'; - opts++; - } else - opts = ""; - p = malloc(sizeof(struct nbs_pvt)); - if (p) { - memset(p, 0, sizeof(struct nbs_pvt)); - if (!ast_strlen_zero(opts)) { - if (strchr(opts, 'm')) - flags |= NBS_FLAG_MUTE; - if (strchr(opts, 'o')) - flags |= NBS_FLAG_OVERSPEAK; - if (strchr(opts, 'e')) - flags |= NBS_FLAG_EMERGENCY; - if (strchr(opts, 'O')) - flags |= NBS_FLAG_OVERRIDE; - } else - flags = NBS_FLAG_OVERSPEAK; - - ast_copy_string(p->stream, stream, sizeof(p->stream)); - p->nbs = nbs_newstream("asterisk", stream, flags); - if (!p->nbs) { - ast_log(LOG_WARNING, "Unable to allocate new NBS stream '%s' with flags %d\n", stream, flags); - free(p); - p = NULL; - } else { - /* Set for 8000 hz mono, 640 samples */ - nbs_setbitrate(p->nbs, 8000); - nbs_setchannels(p->nbs, 1); - nbs_setblocksize(p->nbs, 640); - nbs_setblocking(p->nbs, 0); - } - } - return p; -} - -static int nbs_hangup(struct ast_channel *ast) -{ - struct nbs_pvt *p; - p = ast->tech_pvt; - if (option_debug) - ast_log(LOG_DEBUG, "nbs_hangup(%s)\n", ast->name); - if (!ast->tech_pvt) { - ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); - return 0; - } - nbs_destroy(p); - ast->tech_pvt = NULL; - ast_setstate(ast, AST_STATE_DOWN); - return 0; -} - -static struct ast_frame *nbs_xread(struct ast_channel *ast) -{ - struct nbs_pvt *p = ast->tech_pvt; - - - /* Some nice norms */ - p->fr.datalen = 0; - p->fr.samples = 0; - p->fr.data = NULL; - p->fr.src = type; - p->fr.offset = 0; - p->fr.mallocd=0; - p->fr.delivery.tv_sec = 0; - p->fr.delivery.tv_usec = 0; - - ast_log(LOG_DEBUG, "Returning null frame on %s\n", ast->name); - - return &p->fr; -} - -static int nbs_xwrite(struct ast_channel *ast, struct ast_frame *frame) -{ - struct nbs_pvt *p = ast->tech_pvt; - /* Write a frame of (presumably voice) data */ - if (frame->frametype != AST_FRAME_VOICE) { - if (frame->frametype != AST_FRAME_IMAGE) - ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype); - return 0; - } - if (!(frame->subclass & - (AST_FORMAT_SLINEAR))) { - ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass); - return 0; - } - if (ast->_state != AST_STATE_UP) { - /* Don't try tos end audio on-hook */ - return 0; - } - if (nbs_write(p->nbs, frame->data, frame->datalen / 2) < 0) - return -1; - return 0; -} - -static struct ast_channel *nbs_new(struct nbs_pvt *i, int state) -{ - struct ast_channel *tmp; - tmp = ast_channel_alloc(1, state, 0, 0, "", "s", context, 0, "NBS/%s", i->stream); - if (tmp) { - tmp->tech = &nbs_tech; - tmp->fds[0] = nbs_fd(i->nbs); - tmp->nativeformats = prefformat; - tmp->rawreadformat = prefformat; - tmp->rawwriteformat = prefformat; - tmp->writeformat = prefformat; - tmp->readformat = prefformat; - if (state == AST_STATE_RING) - tmp->rings = 1; - tmp->tech_pvt = i; - ast_copy_string(tmp->context, context, sizeof(tmp->context)); - ast_copy_string(tmp->exten, "s", sizeof(tmp->exten)); - ast_string_field_set(tmp, language, ""); - i->owner = tmp; - i->u = ast_module_user_add(tmp); - if (state != AST_STATE_DOWN) { - if (ast_pbx_start(tmp)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); - ast_hangup(tmp); - } - } - } else - ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); - return tmp; -} - - -static struct ast_channel *nbs_request(const char *type, int format, void *data, int *cause) -{ - int oldformat; - struct nbs_pvt *p; - struct ast_channel *tmp = NULL; - - oldformat = format; - format &= (AST_FORMAT_SLINEAR); - if (!format) { - ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat); - return NULL; - } - p = nbs_alloc(data); - if (p) { - tmp = nbs_new(p, AST_STATE_DOWN); - if (!tmp) - nbs_destroy(p); - } - return tmp; -} - -static int unload_module(void) -{ - /* First, take us out of the channel loop */ - ast_channel_unregister(&nbs_tech); - return 0; -} - -static int load_module(void) -{ - /* Make sure we can register our channel type */ - if (ast_channel_register(&nbs_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class %s\n", type); - return -1; - } - return 0; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Network Broadcast Sound Support"); diff --git a/1.4.23-rc4/channels/chan_oss.c b/1.4.23-rc4/channels/chan_oss.c deleted file mode 100644 index ac62f2005..000000000 --- a/1.4.23-rc4/channels/chan_oss.c +++ /dev/null @@ -1,1899 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * FreeBSD changes and multiple device support by Luigi Rizzo, 2005.05.25 - * note-this code best seen with ts=8 (8-spaces tabs) in the editor - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief Channel driver for OSS sound cards - * - * \author Mark Spencer <markster@digium.com> - * \author Luigi Rizzo - * - * \par See also - * \arg \ref Config_oss - * - * \ingroup channel_drivers - */ - -/*** MODULEINFO - <depend>ossaudio</depend> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <ctype.h> -#include <math.h> -#include <string.h> -#include <unistd.h> -#include <sys/ioctl.h> -#include <fcntl.h> -#include <sys/time.h> -#include <stdlib.h> -#include <errno.h> - -#ifdef __linux -#include <linux/soundcard.h> -#elif defined(__FreeBSD__) -#include <sys/soundcard.h> -#else -#include <soundcard.h> -#endif - -#include "asterisk/lock.h" -#include "asterisk/frame.h" -#include "asterisk/logger.h" -#include "asterisk/callerid.h" -#include "asterisk/channel.h" -#include "asterisk/module.h" -#include "asterisk/options.h" -#include "asterisk/pbx.h" -#include "asterisk/config.h" -#include "asterisk/cli.h" -#include "asterisk/utils.h" -#include "asterisk/causes.h" -#include "asterisk/endian.h" -#include "asterisk/stringfields.h" -#include "asterisk/abstract_jb.h" -#include "asterisk/musiconhold.h" - -/* ringtones we use */ -#include "busy_tone.h" -#include "ring_tone.h" -#include "ring10.h" -#include "answer.h" - -/*! Global jitterbuffer configuration - by default, jb is disabled */ -static struct ast_jb_conf default_jbconf = -{ - .flags = 0, - .max_size = -1, - .resync_threshold = -1, - .impl = "", -}; -static struct ast_jb_conf global_jbconf; - -/* - * Basic mode of operation: - * - * we have one keyboard (which receives commands from the keyboard) - * and multiple headset's connected to audio cards. - * Cards/Headsets are named as the sections of oss.conf. - * The section called [general] contains the default parameters. - * - * At any time, the keyboard is attached to one card, and you - * can switch among them using the command 'console foo' - * where 'foo' is the name of the card you want. - * - * oss.conf parameters are -START_CONFIG - -[general] - ; General config options, with default values shown. - ; You should use one section per device, with [general] being used - ; for the first device and also as a template for other devices. - ; - ; All but 'debug' can go also in the device-specific sections. - ; - ; debug = 0x0 ; misc debug flags, default is 0 - - ; Set the device to use for I/O - ; device = /dev/dsp - - ; Optional mixer command to run upon startup (e.g. to set - ; volume levels, mutes, etc. - ; mixer = - - ; Software mic volume booster (or attenuator), useful for sound - ; cards or microphones with poor sensitivity. The volume level - ; is in dB, ranging from -20.0 to +20.0 - ; boost = n ; mic volume boost in dB - - ; Set the callerid for outgoing calls - ; callerid = John Doe <555-1234> - - ; autoanswer = no ; no autoanswer on call - ; autohangup = yes ; hangup when other party closes - ; extension = s ; default extension to call - ; context = default ; default context for outgoing calls - ; language = "" ; default language - - ; Default Music on Hold class to use when this channel is placed on hold in - ; the case that the music class is not set on the channel with - ; Set(CHANNEL(musicclass)=whatever) in the dialplan and the peer channel - ; putting this one on hold did not suggest a class to use. - ; - ; mohinterpret=default - - ; If you set overridecontext to 'yes', then the whole dial string - ; will be interpreted as an extension, which is extremely useful - ; to dial SIP, IAX and other extensions which use the '@' character. - ; The default is 'no' just for backward compatibility, but the - ; suggestion is to change it. - ; overridecontext = no ; if 'no', the last @ will start the context - ; if 'yes' the whole string is an extension. - - ; low level device parameters in case you have problems with the - ; device driver on your operating system. You should not touch these - ; unless you know what you are doing. - ; queuesize = 10 ; frames in device driver - ; frags = 8 ; argument to SETFRAGMENT - - ;------------------------------ JITTER BUFFER CONFIGURATION -------------------------- - ; jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of an - ; OSS channel. Defaults to "no". An enabled jitterbuffer will - ; be used only if the sending side can create and the receiving - ; side can not accept jitter. The OSS channel can't accept jitter, - ; thus an enabled jitterbuffer on the receive OSS side will always - ; be used if the sending side can create jitter. - - ; jbmaxsize = 200 ; Max length of the jitterbuffer in milliseconds. - - ; jbresyncthreshold = 1000 ; Jump in the frame timestamps over which the jitterbuffer is - ; resynchronized. Useful to improve the quality of the voice, with - ; big jumps in/broken timestamps, usualy sent from exotic devices - ; and programs. Defaults to 1000. - - ; jbimpl = fixed ; Jitterbuffer implementation, used on the receiving side of an OSS - ; channel. Two implementations are currenlty available - "fixed" - ; (with size always equals to jbmax-size) and "adaptive" (with - ; variable size, actually the new jb of IAX2). Defaults to fixed. - - ; jblog = no ; Enables jitterbuffer frame logging. Defaults to "no". - ;----------------------------------------------------------------------------------- - -[card1] - ; device = /dev/dsp1 ; alternate device - -END_CONFIG - -.. and so on for the other cards. - - */ - -/* - * Helper macros to parse config arguments. They will go in a common - * header file if their usage is globally accepted. In the meantime, - * we define them here. Typical usage is as below. - * Remember to open a block right before M_START (as it declares - * some variables) and use the M_* macros WITHOUT A SEMICOLON: - * - * { - * M_START(v->name, v->value) - * - * M_BOOL("dothis", x->flag1) - * M_STR("name", x->somestring) - * M_F("bar", some_c_code) - * M_END(some_final_statement) - * ... other code in the block - * } - * - * XXX NOTE these macros should NOT be replicated in other parts of asterisk. - * Likely we will come up with a better way of doing config file parsing. - */ -#define M_START(var, val) \ - char *__s = var; char *__val = val; -#define M_END(x) x; -#define M_F(tag, f) if (!strcasecmp((__s), tag)) { f; } else -#define M_BOOL(tag, dst) M_F(tag, (dst) = ast_true(__val) ) -#define M_UINT(tag, dst) M_F(tag, (dst) = strtoul(__val, NULL, 0) ) -#define M_STR(tag, dst) M_F(tag, ast_copy_string(dst, __val, sizeof(dst))) - -/* - * The following parameters are used in the driver: - * - * FRAME_SIZE the size of an audio frame, in samples. - * 160 is used almost universally, so you should not change it. - * - * FRAGS the argument for the SETFRAGMENT ioctl. - * Overridden by the 'frags' parameter in oss.conf - * - * Bits 0-7 are the base-2 log of the device's block size, - * bits 16-31 are the number of blocks in the driver's queue. - * There are a lot of differences in the way this parameter - * is supported by different drivers, so you may need to - * experiment a bit with the value. - * A good default for linux is 30 blocks of 64 bytes, which - * results in 6 frames of 320 bytes (160 samples). - * FreeBSD works decently with blocks of 256 or 512 bytes, - * leaving the number unspecified. - * Note that this only refers to the device buffer size, - * this module will then try to keep the lenght of audio - * buffered within small constraints. - * - * QUEUE_SIZE The max number of blocks actually allowed in the device - * driver's buffer, irrespective of the available number. - * Overridden by the 'queuesize' parameter in oss.conf - * - * Should be >=2, and at most as large as the hw queue above - * (otherwise it will never be full). - */ - -#define FRAME_SIZE 160 -#define QUEUE_SIZE 10 - -#if defined(__FreeBSD__) -#define FRAGS 0x8 -#else -#define FRAGS ( ( (6 * 5) << 16 ) | 0x6 ) -#endif - -/* - * XXX text message sizes are probably 256 chars, but i am - * not sure if there is a suitable definition anywhere. - */ -#define TEXT_SIZE 256 - -#if 0 -#define TRYOPEN 1 /* try to open on startup */ -#endif -#define O_CLOSE 0x444 /* special 'close' mode for device */ -/* Which device to use */ -#if defined( __OpenBSD__ ) || defined( __NetBSD__ ) -#define DEV_DSP "/dev/audio" -#else -#define DEV_DSP "/dev/dsp" -#endif - -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif -#ifndef MAX -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#endif - -static char *config = "oss.conf"; /* default config file */ - -static int oss_debug; - -/* - * Each sound is made of 'datalen' samples of sound, repeated as needed to - * generate 'samplen' samples of data, then followed by 'silencelen' samples - * of silence. The loop is repeated if 'repeat' is set. - */ -struct sound { - int ind; - char *desc; - short *data; - int datalen; - int samplen; - int silencelen; - int repeat; -}; - -static struct sound sounds[] = { - { AST_CONTROL_RINGING, "RINGING", ringtone, sizeof(ringtone)/2, 16000, 32000, 1 }, - { AST_CONTROL_BUSY, "BUSY", busy, sizeof(busy)/2, 4000, 4000, 1 }, - { AST_CONTROL_CONGESTION, "CONGESTION", busy, sizeof(busy)/2, 2000, 2000, 1 }, - { AST_CONTROL_RING, "RING10", ring10, sizeof(ring10)/2, 16000, 32000, 1 }, - { AST_CONTROL_ANSWER, "ANSWER", answer, sizeof(answer)/2, 2200, 0, 0 }, - { -1, NULL, 0, 0, 0, 0 }, /* end marker */ -}; - - -/* - * descriptor for one of our channels. - * There is one used for 'default' values (from the [general] entry in - * the configuration file), and then one instance for each device - * (the default is cloned from [general], others are only created - * if the relevant section exists). - */ -struct chan_oss_pvt { - struct chan_oss_pvt *next; - - char *name; - /* - * cursound indicates which in struct sound we play. -1 means nothing, - * any other value is a valid sound, in which case sampsent indicates - * the next sample to send in [0..samplen + silencelen] - * nosound is set to disable the audio data from the channel - * (so we can play the tones etc.). - */ - int sndcmd[2]; /* Sound command pipe */ - int cursound; /* index of sound to send */ - int sampsent; /* # of sound samples sent */ - int nosound; /* set to block audio from the PBX */ - - int total_blocks; /* total blocks in the output device */ - int sounddev; - enum { M_UNSET, M_FULL, M_READ, M_WRITE } duplex; - int autoanswer; - int autohangup; - int hookstate; - char *mixer_cmd; /* initial command to issue to the mixer */ - unsigned int queuesize; /* max fragments in queue */ - unsigned int frags; /* parameter for SETFRAGMENT */ - - int warned; /* various flags used for warnings */ -#define WARN_used_blocks 1 -#define WARN_speed 2 -#define WARN_frag 4 - int w_errors; /* overfull in the write path */ - struct timeval lastopen; - - int overridecontext; - int mute; - - /* boost support. BOOST_SCALE * 10 ^(BOOST_MAX/20) must - * be representable in 16 bits to avoid overflows. - */ -#define BOOST_SCALE (1<<9) -#define BOOST_MAX 40 /* slightly less than 7 bits */ - int boost; /* input boost, scaled by BOOST_SCALE */ - char device[64]; /* device to open */ - - pthread_t sthread; - - struct ast_channel *owner; - char ext[AST_MAX_EXTENSION]; - char ctx[AST_MAX_CONTEXT]; - char language[MAX_LANGUAGE]; - char cid_name[256]; /*XXX */ - char cid_num[256]; /*XXX */ - char mohinterpret[MAX_MUSICCLASS]; - - /* buffers used in oss_write */ - char oss_write_buf[FRAME_SIZE * 2]; - int oss_write_dst; - /* buffers used in oss_read - AST_FRIENDLY_OFFSET space for headers - * plus enough room for a full frame - */ - char oss_read_buf[FRAME_SIZE * 2 + AST_FRIENDLY_OFFSET]; - int readpos; /* read position above */ - struct ast_frame read_f; /* returned by oss_read */ -}; - -static struct chan_oss_pvt oss_default = { - .cursound = -1, - .sounddev = -1, - .duplex = M_UNSET, /* XXX check this */ - .autoanswer = 1, - .autohangup = 1, - .queuesize = QUEUE_SIZE, - .frags = FRAGS, - .ext = "s", - .ctx = "default", - .readpos = AST_FRIENDLY_OFFSET, /* start here on reads */ - .lastopen = { 0, 0 }, - .boost = BOOST_SCALE, -}; - -static char *oss_active; /* the active device */ - -static int setformat(struct chan_oss_pvt *o, int mode); - -static struct ast_channel *oss_request(const char *type, int format, void *data -, int *cause); -static int oss_digit_begin(struct ast_channel *c, char digit); -static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration); -static int oss_text(struct ast_channel *c, const char *text); -static int oss_hangup(struct ast_channel *c); -static int oss_answer(struct ast_channel *c); -static struct ast_frame *oss_read(struct ast_channel *chan); -static int oss_call(struct ast_channel *c, char *dest, int timeout); -static int oss_write(struct ast_channel *chan, struct ast_frame *f); -static int oss_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen); -static int oss_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -static char tdesc[] = "OSS Console Channel Driver"; - -static const struct ast_channel_tech oss_tech = { - .type = "Console", - .description = tdesc, - .capabilities = AST_FORMAT_SLINEAR, - .requester = oss_request, - .send_digit_begin = oss_digit_begin, - .send_digit_end = oss_digit_end, - .send_text = oss_text, - .hangup = oss_hangup, - .answer = oss_answer, - .read = oss_read, - .call = oss_call, - .write = oss_write, - .indicate = oss_indicate, - .fixup = oss_fixup, -}; - -/* - * returns a pointer to the descriptor with the given name - */ -static struct chan_oss_pvt *find_desc(char *dev) -{ - struct chan_oss_pvt *o = NULL; - - if (!dev) - ast_log(LOG_WARNING, "null dev\n"); - - for (o = oss_default.next; o && o->name && dev && strcmp(o->name, dev) != 0; o = o->next); - - if (!o) - ast_log(LOG_WARNING, "could not find <%s>\n", dev ? dev : "--no-device--"); - - return o; -} - -/* - * split a string in extension-context, returns pointers to malloc'ed - * strings. - * If we do not have 'overridecontext' then the last @ is considered as - * a context separator, and the context is overridden. - * This is usually not very necessary as you can play with the dialplan, - * and it is nice not to need it because you have '@' in SIP addresses. - * Return value is the buffer address. - */ -static char *ast_ext_ctx(const char *src, char **ext, char **ctx) -{ - struct chan_oss_pvt *o = find_desc(oss_active); - - if (ext == NULL || ctx == NULL) - return NULL; /* error */ - - *ext = *ctx = NULL; - - if (src && *src != '\0') - *ext = ast_strdup(src); - - if (*ext == NULL) - return NULL; - - if (!o->overridecontext) { - /* parse from the right */ - *ctx = strrchr(*ext, '@'); - if (*ctx) - *(*ctx)++ = '\0'; - } - - return *ext; -} - -/* - * Returns the number of blocks used in the audio output channel - */ -static int used_blocks(struct chan_oss_pvt *o) -{ - struct audio_buf_info info; - - if (ioctl(o->sounddev, SNDCTL_DSP_GETOSPACE, &info)) { - if (!(o->warned & WARN_used_blocks)) { - ast_log(LOG_WARNING, "Error reading output space\n"); - o->warned |= WARN_used_blocks; - } - return 1; - } - - if (o->total_blocks == 0) { - if (0) /* debugging */ - ast_log(LOG_WARNING, "fragtotal %d size %d avail %d\n", info.fragstotal, info.fragsize, info.fragments); - o->total_blocks = info.fragments; - } - - return o->total_blocks - info.fragments; -} - -/* Write an exactly FRAME_SIZE sized frame */ -static int soundcard_writeframe(struct chan_oss_pvt *o, short *data) -{ - int res; - - if (o->sounddev < 0) - setformat(o, O_RDWR); - if (o->sounddev < 0) - return 0; /* not fatal */ - /* - * Nothing complex to manage the audio device queue. - * If the buffer is full just drop the extra, otherwise write. - * XXX in some cases it might be useful to write anyways after - * a number of failures, to restart the output chain. - */ - res = used_blocks(o); - if (res > o->queuesize) { /* no room to write a block */ - if (o->w_errors++ == 0 && (oss_debug & 0x4)) - ast_log(LOG_WARNING, "write: used %d blocks (%d)\n", res, o->w_errors); - return 0; - } - o->w_errors = 0; - return write(o->sounddev, ((void *) data), FRAME_SIZE * 2); -} - -/* - * Handler for 'sound writable' events from the sound thread. - * Builds a frame from the high level description of the sounds, - * and passes it to the audio device. - * The actual sound is made of 1 or more sequences of sound samples - * (s->datalen, repeated to make s->samplen samples) followed by - * s->silencelen samples of silence. The position in the sequence is stored - * in o->sampsent, which goes between 0 .. s->samplen+s->silencelen. - * In case we fail to write a frame, don't update o->sampsent. - */ -static void send_sound(struct chan_oss_pvt *o) -{ - short myframe[FRAME_SIZE]; - int ofs, l, start; - int l_sampsent = o->sampsent; - struct sound *s; - - if (o->cursound < 0) /* no sound to send */ - return; - - s = &sounds[o->cursound]; - - for (ofs = 0; ofs < FRAME_SIZE; ofs += l) { - l = s->samplen - l_sampsent; /* # of available samples */ - if (l > 0) { - start = l_sampsent % s->datalen; /* source offset */ - if (l > FRAME_SIZE - ofs) /* don't overflow the frame */ - l = FRAME_SIZE - ofs; - if (l > s->datalen - start) /* don't overflow the source */ - l = s->datalen - start; - bcopy(s->data + start, myframe + ofs, l * 2); - if (0) - ast_log(LOG_WARNING, "send_sound sound %d/%d of %d into %d\n", l_sampsent, l, s->samplen, ofs); - l_sampsent += l; - } else { /* end of samples, maybe some silence */ - static const short silence[FRAME_SIZE] = { 0, }; - - l += s->silencelen; - if (l > 0) { - if (l > FRAME_SIZE - ofs) - l = FRAME_SIZE - ofs; - bcopy(silence, myframe + ofs, l * 2); - l_sampsent += l; - } else { /* silence is over, restart sound if loop */ - if (s->repeat == 0) { /* last block */ - o->cursound = -1; - o->nosound = 0; /* allow audio data */ - if (ofs < FRAME_SIZE) /* pad with silence */ - bcopy(silence, myframe + ofs, (FRAME_SIZE - ofs) * 2); - } - l_sampsent = 0; - } - } - } - l = soundcard_writeframe(o, myframe); - if (l > 0) - o->sampsent = l_sampsent; /* update status */ -} - -static void *sound_thread(void *arg) -{ - char ign[4096]; - struct chan_oss_pvt *o = (struct chan_oss_pvt *) arg; - - /* - * Just in case, kick the driver by trying to read from it. - * Ignore errors - this read is almost guaranteed to fail. - */ - if (read(o->sounddev, ign, sizeof(ign)) < 0) { - } - for (;;) { - fd_set rfds, wfds; - int maxfd, res; - - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_SET(o->sndcmd[0], &rfds); - maxfd = o->sndcmd[0]; /* pipe from the main process */ - if (o->cursound > -1 && o->sounddev < 0) - setformat(o, O_RDWR); /* need the channel, try to reopen */ - else if (o->cursound == -1 && o->owner == NULL) - setformat(o, O_CLOSE); /* can close */ - if (o->sounddev > -1) { - if (!o->owner) { /* no one owns the audio, so we must drain it */ - FD_SET(o->sounddev, &rfds); - maxfd = MAX(o->sounddev, maxfd); - } - if (o->cursound > -1) { - FD_SET(o->sounddev, &wfds); - maxfd = MAX(o->sounddev, maxfd); - } - } - /* ast_select emulates linux behaviour in terms of timeout handling */ - res = ast_select(maxfd + 1, &rfds, &wfds, NULL, NULL); - if (res < 1) { - ast_log(LOG_WARNING, "select failed: %s\n", strerror(errno)); - sleep(1); - continue; - } - if (FD_ISSET(o->sndcmd[0], &rfds)) { - /* read which sound to play from the pipe */ - int i, what = -1; - - if (read(o->sndcmd[0], &what, sizeof(what)) != sizeof(what)) { - ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno)); - continue; - } - for (i = 0; sounds[i].ind != -1; i++) { - if (sounds[i].ind == what) { - o->cursound = i; - o->sampsent = 0; - o->nosound = 1; /* block audio from pbx */ - break; - } - } - if (sounds[i].ind == -1) - ast_log(LOG_WARNING, "invalid sound index: %d\n", what); - } - if (o->sounddev > -1) { - if (FD_ISSET(o->sounddev, &rfds)) /* read and ignore errors */ - if (read(o->sounddev, ign, sizeof(ign)) < 0) { - } - if (FD_ISSET(o->sounddev, &wfds)) - send_sound(o); - } - } - return NULL; /* Never reached */ -} - -/* - * reset and close the device if opened, - * then open and initialize it in the desired mode, - * trigger reads and writes so we can start using it. - */ -static int setformat(struct chan_oss_pvt *o, int mode) -{ - int fmt, desired, res, fd; - - if (o->sounddev >= 0) { - ioctl(o->sounddev, SNDCTL_DSP_RESET, 0); - close(o->sounddev); - o->duplex = M_UNSET; - o->sounddev = -1; - } - if (mode == O_CLOSE) /* we are done */ - return 0; - if (ast_tvdiff_ms(ast_tvnow(), o->lastopen) < 1000) - return -1; /* don't open too often */ - o->lastopen = ast_tvnow(); - fd = o->sounddev = open(o->device, mode | O_NONBLOCK); - if (fd < 0) { - ast_log(LOG_WARNING, "Unable to re-open DSP device %s: %s\n", o->device, strerror(errno)); - return -1; - } - if (o->owner) - o->owner->fds[0] = fd; - -#if __BYTE_ORDER == __LITTLE_ENDIAN - fmt = AFMT_S16_LE; -#else - fmt = AFMT_S16_BE; -#endif - res = ioctl(fd, SNDCTL_DSP_SETFMT, &fmt); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to set format to 16-bit signed\n"); - return -1; - } - switch (mode) { - case O_RDWR: - res = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); - /* Check to see if duplex set (FreeBSD Bug) */ - res = ioctl(fd, SNDCTL_DSP_GETCAPS, &fmt); - if (res == 0 && (fmt & DSP_CAP_DUPLEX)) { - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Console is full duplex\n"); - o->duplex = M_FULL; - }; - break; - case O_WRONLY: - o->duplex = M_WRITE; - break; - case O_RDONLY: - o->duplex = M_READ; - break; - } - - fmt = 0; - res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt); - if (res < 0) { - ast_log(LOG_WARNING, "Failed to set audio device to mono\n"); - return -1; - } - fmt = desired = DEFAULT_SAMPLE_RATE; /* 8000 Hz desired */ - res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt); - - if (res < 0) { - ast_log(LOG_WARNING, "Failed to set audio device to mono\n"); - return -1; - } - if (fmt != desired) { - if (!(o->warned & WARN_speed)) { - ast_log(LOG_WARNING, - "Requested %d Hz, got %d Hz -- sound may be choppy\n", - desired, fmt); - o->warned |= WARN_speed; - } - } - /* - * on Freebsd, SETFRAGMENT does not work very well on some cards. - * Default to use 256 bytes, let the user override - */ - if (o->frags) { - fmt = o->frags; - res = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fmt); - if (res < 0) { - if (!(o->warned & WARN_frag)) { - ast_log(LOG_WARNING, - "Unable to set fragment size -- sound may be choppy\n"); - o->warned |= WARN_frag; - } - } - } - /* on some cards, we need SNDCTL_DSP_SETTRIGGER to start outputting */ - res = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT; - res = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &res); - /* it may fail if we are in half duplex, never mind */ - return 0; -} - -/* - * some of the standard methods supported by channels. - */ -static int oss_digit_begin(struct ast_channel *c, char digit) -{ - return 0; -} - -static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration) -{ - /* no better use for received digits than print them */ - ast_verbose(" << Console Received digit %c of duration %u ms >> \n", - digit, duration); - return 0; -} - -static int oss_text(struct ast_channel *c, const char *text) -{ - /* print received messages */ - ast_verbose(" << Console Received text %s >> \n", text); - return 0; -} - -/* Play ringtone 'x' on device 'o' */ -static void ring(struct chan_oss_pvt *o, int x) -{ - if (write(o->sndcmd[1], &x, sizeof(x)) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } -} - - -/* - * handler for incoming calls. Either autoanswer, or start ringing - */ -static int oss_call(struct ast_channel *c, char *dest, int timeout) -{ - struct chan_oss_pvt *o = c->tech_pvt; - struct ast_frame f = { 0, }; - - ast_verbose(" << Call to device '%s' dnid '%s' rdnis '%s' on console from '%s' <%s> >>\n", dest, c->cid.cid_dnid, c->cid.cid_rdnis, c->cid.cid_name, c->cid.cid_num); - if (o->autoanswer) { - ast_verbose(" << Auto-answered >> \n"); - f.frametype = AST_FRAME_CONTROL; - f.subclass = AST_CONTROL_ANSWER; - ast_queue_frame(c, &f); - } else { - ast_verbose("<< Type 'answer' to answer, or use 'autoanswer' for future calls >> \n"); - f.frametype = AST_FRAME_CONTROL; - f.subclass = AST_CONTROL_RINGING; - ast_queue_frame(c, &f); - ring(o, AST_CONTROL_RING); - } - return 0; -} - -/* - * remote side answered the phone - */ -static int oss_answer(struct ast_channel *c) -{ - struct chan_oss_pvt *o = c->tech_pvt; - - ast_verbose(" << Console call has been answered >> \n"); -#if 0 - /* play an answer tone (XXX do we really need it ?) */ - ring(o, AST_CONTROL_ANSWER); -#endif - ast_setstate(c, AST_STATE_UP); - o->cursound = -1; - o->nosound = 0; - return 0; -} - -static int oss_hangup(struct ast_channel *c) -{ - struct chan_oss_pvt *o = c->tech_pvt; - - o->cursound = -1; - o->nosound = 0; - c->tech_pvt = NULL; - o->owner = NULL; - ast_verbose(" << Hangup on console >> \n"); - ast_module_unref(ast_module_info->self); - if (o->hookstate) { - if (o->autoanswer || o->autohangup) { - /* Assume auto-hangup too */ - o->hookstate = 0; - setformat(o, O_CLOSE); - } else { - /* Make congestion noise */ - ring(o, AST_CONTROL_CONGESTION); - } - } - return 0; -} - -/* used for data coming from the network */ -static int oss_write(struct ast_channel *c, struct ast_frame *f) -{ - int src; - struct chan_oss_pvt *o = c->tech_pvt; - - /* Immediately return if no sound is enabled */ - if (o->nosound) - return 0; - /* Stop any currently playing sound */ - o->cursound = -1; - /* - * we could receive a block which is not a multiple of our - * FRAME_SIZE, so buffer it locally and write to the device - * in FRAME_SIZE chunks. - * Keep the residue stored for future use. - */ - src = 0; /* read position into f->data */ - while (src < f->datalen) { - /* Compute spare room in the buffer */ - int l = sizeof(o->oss_write_buf) - o->oss_write_dst; - - if (f->datalen - src >= l) { /* enough to fill a frame */ - memcpy(o->oss_write_buf + o->oss_write_dst, f->data + src, l); - soundcard_writeframe(o, (short *) o->oss_write_buf); - src += l; - o->oss_write_dst = 0; - } else { /* copy residue */ - l = f->datalen - src; - memcpy(o->oss_write_buf + o->oss_write_dst, f->data + src, l); - src += l; /* but really, we are done */ - o->oss_write_dst += l; - } - } - return 0; -} - -static struct ast_frame *oss_read(struct ast_channel *c) -{ - int res; - struct chan_oss_pvt *o = c->tech_pvt; - struct ast_frame *f = &o->read_f; - - /* XXX can be simplified returning &ast_null_frame */ - /* prepare a NULL frame in case we don't have enough data to return */ - bzero(f, sizeof(struct ast_frame)); - f->frametype = AST_FRAME_NULL; - f->src = oss_tech.type; - - res = read(o->sounddev, o->oss_read_buf + o->readpos, sizeof(o->oss_read_buf) - o->readpos); - if (res < 0) /* audio data not ready, return a NULL frame */ - return f; - - o->readpos += res; - if (o->readpos < sizeof(o->oss_read_buf)) /* not enough samples */ - return f; - - if (o->mute) - return f; - - o->readpos = AST_FRIENDLY_OFFSET; /* reset read pointer for next frame */ - if (c->_state != AST_STATE_UP) /* drop data if frame is not up */ - return f; - /* ok we can build and deliver the frame to the caller */ - f->frametype = AST_FRAME_VOICE; - f->subclass = AST_FORMAT_SLINEAR; - f->samples = FRAME_SIZE; - f->datalen = FRAME_SIZE * 2; - f->data = o->oss_read_buf + AST_FRIENDLY_OFFSET; - if (o->boost != BOOST_SCALE) { /* scale and clip values */ - int i, x; - int16_t *p = (int16_t *) f->data; - for (i = 0; i < f->samples; i++) { - x = (p[i] * o->boost) / BOOST_SCALE; - if (x > 32767) - x = 32767; - else if (x < -32768) - x = -32768; - p[i] = x; - } - } - - f->offset = AST_FRIENDLY_OFFSET; - return f; -} - -static int oss_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct chan_oss_pvt *o = newchan->tech_pvt; - o->owner = newchan; - return 0; -} - -static int oss_indicate(struct ast_channel *c, int cond, const void *data, size_t datalen) -{ - struct chan_oss_pvt *o = c->tech_pvt; - int res = -1; - - switch (cond) { - case AST_CONTROL_BUSY: - case AST_CONTROL_CONGESTION: - case AST_CONTROL_RINGING: - res = cond; - break; - - case -1: - o->cursound = -1; - o->nosound = 0; /* when cursound is -1 nosound must be 0 */ - return 0; - - case AST_CONTROL_VIDUPDATE: - res = -1; - break; - case AST_CONTROL_HOLD: - ast_verbose(" << Console Has Been Placed on Hold >> \n"); - ast_moh_start(c, data, o->mohinterpret); - break; - case AST_CONTROL_UNHOLD: - ast_verbose(" << Console Has Been Retrieved from Hold >> \n"); - ast_moh_stop(c); - break; - case AST_CONTROL_SRCUPDATE: - break; - default: - ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, c->name); - return -1; - } - - if (res > -1) - ring(o, res); - - return 0; -} - -/* - * allocate a new channel. - */ -static struct ast_channel *oss_new(struct chan_oss_pvt *o, char *ext, char *ctx, int state) -{ - struct ast_channel *c; - - c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, 0, "Console/%s", o->device + 5); - if (c == NULL) - return NULL; - c->tech = &oss_tech; - if (o->sounddev < 0) - setformat(o, O_RDWR); - c->fds[0] = o->sounddev; /* -1 if device closed, override later */ - c->nativeformats = AST_FORMAT_SLINEAR; - c->readformat = AST_FORMAT_SLINEAR; - c->writeformat = AST_FORMAT_SLINEAR; - c->tech_pvt = o; - - if (!ast_strlen_zero(o->language)) - ast_string_field_set(c, language, o->language); - /* Don't use ast_set_callerid() here because it will - * generate a needless NewCallerID event */ - c->cid.cid_ani = ast_strdup(o->cid_num); - if (!ast_strlen_zero(ext)) - c->cid.cid_dnid = ast_strdup(ext); - - o->owner = c; - ast_module_ref(ast_module_info->self); - ast_jb_configure(c, &global_jbconf); - if (state != AST_STATE_DOWN) { - if (ast_pbx_start(c)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", c->name); - ast_hangup(c); - o->owner = c = NULL; - /* XXX what about the channel itself ? */ - /* XXX what about usecnt ? */ - } - } - - return c; -} - -static struct ast_channel *oss_request(const char *type, int format, void *data, int *cause) -{ - struct ast_channel *c; - struct chan_oss_pvt *o = find_desc(data); - - ast_log(LOG_WARNING, "oss_request ty <%s> data 0x%p <%s>\n", type, data, (char *) data); - if (o == NULL) { - ast_log(LOG_NOTICE, "Device %s not found\n", (char *) data); - /* XXX we could default to 'dsp' perhaps ? */ - return NULL; - } - if ((format & AST_FORMAT_SLINEAR) == 0) { - ast_log(LOG_NOTICE, "Format 0x%x unsupported\n", format); - return NULL; - } - if (o->owner) { - ast_log(LOG_NOTICE, "Already have a call (chan %p) on the OSS channel\n", o->owner); - *cause = AST_CAUSE_BUSY; - return NULL; - } - c = oss_new(o, NULL, NULL, AST_STATE_DOWN); - if (c == NULL) { - ast_log(LOG_WARNING, "Unable to create new OSS channel\n"); - return NULL; - } - return c; -} - -static int console_autoanswer_deprecated(int fd, int argc, char *argv[]) -{ - struct chan_oss_pvt *o = find_desc(oss_active); - - if (argc == 1) { - ast_cli(fd, "Auto answer is %s.\n", o->autoanswer ? "on" : "off"); - return RESULT_SUCCESS; - } - if (argc != 2) - return RESULT_SHOWUSAGE; - if (o == NULL) { - ast_log(LOG_WARNING, "Cannot find device %s (should not happen!)\n", oss_active); - return RESULT_FAILURE; - } - if (!strcasecmp(argv[1], "on")) - o->autoanswer = -1; - else if (!strcasecmp(argv[1], "off")) - o->autoanswer = 0; - else - return RESULT_SHOWUSAGE; - return RESULT_SUCCESS; -} - -static int console_autoanswer(int fd, int argc, char *argv[]) -{ - struct chan_oss_pvt *o = find_desc(oss_active); - - if (argc == 2) { - ast_cli(fd, "Auto answer is %s.\n", o->autoanswer ? "on" : "off"); - return RESULT_SUCCESS; - } - if (argc != 3) - return RESULT_SHOWUSAGE; - if (o == NULL) { - ast_log(LOG_WARNING, "Cannot find device %s (should not happen!)\n", - oss_active); - return RESULT_FAILURE; - } - if (!strcasecmp(argv[2], "on")) - o->autoanswer = -1; - else if (!strcasecmp(argv[2], "off")) - o->autoanswer = 0; - else - return RESULT_SHOWUSAGE; - return RESULT_SUCCESS; -} - -static char *autoanswer_complete_deprecated(const char *line, const char *word, int pos, int state) -{ - static char *choices[] = { "on", "off", NULL }; - - return (pos != 2) ? NULL : ast_cli_complete(word, choices, state); -} - -static char *autoanswer_complete(const char *line, const char *word, int pos, int state) -{ - static char *choices[] = { "on", "off", NULL }; - - return (pos != 3) ? NULL : ast_cli_complete(word, choices, state); -} - -static char autoanswer_usage[] = - "Usage: console autoanswer [on|off]\n" - " Enables or disables autoanswer feature. If used without\n" - " argument, displays the current on/off status of autoanswer.\n" - " The default value of autoanswer is in 'oss.conf'.\n"; - -/* - * answer command from the console - */ -static int console_answer_deprecated(int fd, int argc, char *argv[]) -{ - struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; - struct chan_oss_pvt *o = find_desc(oss_active); - - if (argc != 1) - return RESULT_SHOWUSAGE; - if (!o->owner) { - ast_cli(fd, "No one is calling us\n"); - return RESULT_FAILURE; - } - o->hookstate = 1; - o->cursound = -1; - o->nosound = 0; - ast_queue_frame(o->owner, &f); -#if 0 - /* XXX do we really need it ? considering we shut down immediately... */ - ring(o, AST_CONTROL_ANSWER); -#endif - return RESULT_SUCCESS; -} - -static int console_answer(int fd, int argc, char *argv[]) -{ - struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; - struct chan_oss_pvt *o = find_desc(oss_active); - - if (argc != 2) - return RESULT_SHOWUSAGE; - if (!o->owner) { - ast_cli(fd, "No one is calling us\n"); - return RESULT_FAILURE; - } - o->hookstate = 1; - o->cursound = -1; - o->nosound = 0; - ast_queue_frame(o->owner, &f); -#if 0 - /* XXX do we really need it ? considering we shut down immediately... */ - ring(o, AST_CONTROL_ANSWER); -#endif - return RESULT_SUCCESS; -} - -static char answer_usage[] = - "Usage: console answer\n" - " Answers an incoming call on the console (OSS) channel.\n"; - -/* - * concatenate all arguments into a single string. argv is NULL-terminated - * so we can use it right away - */ -static int console_sendtext_deprecated(int fd, int argc, char *argv[]) -{ - struct chan_oss_pvt *o = find_desc(oss_active); - char buf[TEXT_SIZE]; - - if (argc < 2) - return RESULT_SHOWUSAGE; - if (!o->owner) { - ast_cli(fd, "Not in a call\n"); - return RESULT_FAILURE; - } - ast_join(buf, sizeof(buf) - 1, argv + 2); - if (!ast_strlen_zero(buf)) { - struct ast_frame f = { 0, }; - int i = strlen(buf); - buf[i] = '\n'; - f.frametype = AST_FRAME_TEXT; - f.subclass = 0; - f.data = buf; - f.datalen = i + 1; - ast_queue_frame(o->owner, &f); - } - return RESULT_SUCCESS; -} - -static int console_sendtext(int fd, int argc, char *argv[]) -{ - struct chan_oss_pvt *o = find_desc(oss_active); - char buf[TEXT_SIZE]; - - if (argc < 3) - return RESULT_SHOWUSAGE; - if (!o->owner) { - ast_cli(fd, "Not in a call\n"); - return RESULT_FAILURE; - } - ast_join(buf, sizeof(buf) - 1, argv + 3); - if (!ast_strlen_zero(buf)) { - struct ast_frame f = { 0, }; - int i = strlen(buf); - buf[i] = '\n'; - f.frametype = AST_FRAME_TEXT; - f.subclass = 0; - f.data = buf; - f.datalen = i + 1; - ast_queue_frame(o->owner, &f); - } - return RESULT_SUCCESS; -} - -static char sendtext_usage[] = - "Usage: console send text <message>\n" - " Sends a text message for display on the remote terminal.\n"; - -static int console_hangup_deprecated(int fd, int argc, char *argv[]) -{ - struct chan_oss_pvt *o = find_desc(oss_active); - - if (argc != 1) - return RESULT_SHOWUSAGE; - o->cursound = -1; - o->nosound = 0; - if (!o->owner && !o->hookstate) { /* XXX maybe only one ? */ - ast_cli(fd, "No call to hang up\n"); - return RESULT_FAILURE; - } - o->hookstate = 0; - if (o->owner) - ast_queue_hangup(o->owner); - setformat(o, O_CLOSE); - return RESULT_SUCCESS; -} - -static int console_hangup(int fd, int argc, char *argv[]) -{ - struct chan_oss_pvt *o = find_desc(oss_active); - - if (argc != 2) - return RESULT_SHOWUSAGE; - o->cursound = -1; - o->nosound = 0; - if (!o->owner && !o->hookstate) { /* XXX maybe only one ? */ - ast_cli(fd, "No call to hang up\n"); - return RESULT_FAILURE; - } - o->hookstate = 0; - if (o->owner) - ast_queue_hangup(o->owner); - setformat(o, O_CLOSE); - return RESULT_SUCCESS; -} - -static char hangup_usage[] = - "Usage: console hangup\n" - " Hangs up any call currently placed on the console.\n"; - -static int console_flash_deprecated(int fd, int argc, char *argv[]) -{ - struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH }; - struct chan_oss_pvt *o = find_desc(oss_active); - - if (argc != 1) - return RESULT_SHOWUSAGE; - o->cursound = -1; - o->nosound = 0; /* when cursound is -1 nosound must be 0 */ - if (!o->owner) { /* XXX maybe !o->hookstate too ? */ - ast_cli(fd, "No call to flash\n"); - return RESULT_FAILURE; - } - o->hookstate = 0; - if (o->owner) /* XXX must be true, right ? */ - ast_queue_frame(o->owner, &f); - return RESULT_SUCCESS; -} - -static int console_flash(int fd, int argc, char *argv[]) -{ - struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH }; - struct chan_oss_pvt *o = find_desc(oss_active); - - if (argc != 2) - return RESULT_SHOWUSAGE; - o->cursound = -1; - o->nosound = 0; /* when cursound is -1 nosound must be 0 */ - if (!o->owner) { /* XXX maybe !o->hookstate too ? */ - ast_cli(fd, "No call to flash\n"); - return RESULT_FAILURE; - } - o->hookstate = 0; - if (o->owner) /* XXX must be true, right ? */ - ast_queue_frame(o->owner, &f); - return RESULT_SUCCESS; -} - -static char flash_usage[] = - "Usage: console flash\n" - " Flashes the call currently placed on the console.\n"; - -static int console_dial_deprecated(int fd, int argc, char *argv[]) -{ - char *s = NULL, *mye = NULL, *myc = NULL; - struct chan_oss_pvt *o = find_desc(oss_active); - - if (argc != 1 && argc != 2) - return RESULT_SHOWUSAGE; - if (o->owner) { /* already in a call */ - int i; - struct ast_frame f = { AST_FRAME_DTMF, 0 }; - - if (argc == 1) { /* argument is mandatory here */ - ast_cli(fd, "Already in a call. You can only dial digits until you hangup.\n"); - return RESULT_FAILURE; - } - s = argv[1]; - /* send the string one char at a time */ - for (i = 0; i < strlen(s); i++) { - f.subclass = s[i]; - ast_queue_frame(o->owner, &f); - } - return RESULT_SUCCESS; - } - /* if we have an argument split it into extension and context */ - if (argc == 2) - s = ast_ext_ctx(argv[1], &mye, &myc); - /* supply default values if needed */ - if (mye == NULL) - mye = o->ext; - if (myc == NULL) - myc = o->ctx; - if (ast_exists_extension(NULL, myc, mye, 1, NULL)) { - o->hookstate = 1; - oss_new(o, mye, myc, AST_STATE_RINGING); - } else - ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc); - if (s) - free(s); - return RESULT_SUCCESS; -} - -static int console_dial(int fd, int argc, char *argv[]) -{ - char *s = NULL, *mye = NULL, *myc = NULL; - struct chan_oss_pvt *o = find_desc(oss_active); - - if (argc != 2 && argc != 3) - return RESULT_SHOWUSAGE; - if (o->owner) { /* already in a call */ - int i; - struct ast_frame f = { AST_FRAME_DTMF, 0 }; - - if (argc == 2) { /* argument is mandatory here */ - ast_cli(fd, "Already in a call. You can only dial digits until you hangup.\n"); - return RESULT_FAILURE; - } - s = argv[2]; - /* send the string one char at a time */ - for (i = 0; i < strlen(s); i++) { - f.subclass = s[i]; - ast_queue_frame(o->owner, &f); - } - return RESULT_SUCCESS; - } - /* if we have an argument split it into extension and context */ - if (argc == 3) - s = ast_ext_ctx(argv[2], &mye, &myc); - /* supply default values if needed */ - if (mye == NULL) - mye = o->ext; - if (myc == NULL) - myc = o->ctx; - if (ast_exists_extension(NULL, myc, mye, 1, NULL)) { - o->hookstate = 1; - oss_new(o, mye, myc, AST_STATE_RINGING); - } else - ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc); - if (s) - free(s); - return RESULT_SUCCESS; -} - -static char dial_usage[] = - "Usage: console dial [extension[@context]]\n" - " Dials a given extension (and context if specified)\n"; - -static int __console_mute_unmute(int mute) -{ - struct chan_oss_pvt *o = find_desc(oss_active); - - o->mute = mute; - return RESULT_SUCCESS; -} - -static int console_mute_deprecated(int fd, int argc, char *argv[]) -{ - if (argc != 1) - return RESULT_SHOWUSAGE; - - return __console_mute_unmute(1); -} - -static int console_mute(int fd, int argc, char *argv[]) -{ - if (argc != 2) - return RESULT_SHOWUSAGE; - - return __console_mute_unmute(1); -} - -static char mute_usage[] = - "Usage: console mute\nMutes the microphone\n"; - -static int console_unmute_deprecated(int fd, int argc, char *argv[]) -{ - if (argc != 1) - return RESULT_SHOWUSAGE; - - return __console_mute_unmute(0); -} - -static int console_unmute(int fd, int argc, char *argv[]) -{ - if (argc != 2) - return RESULT_SHOWUSAGE; - - return __console_mute_unmute(0); -} - -static char unmute_usage[] = - "Usage: console unmute\nUnmutes the microphone\n"; - -static int console_transfer_deprecated(int fd, int argc, char *argv[]) -{ - struct chan_oss_pvt *o = find_desc(oss_active); - struct ast_channel *b = NULL; - char *tmp, *ext, *ctx; - - if (argc != 2) - return RESULT_SHOWUSAGE; - if (o == NULL) - return RESULT_FAILURE; - if (o->owner ==NULL || (b = ast_bridged_channel(o->owner)) == NULL) { - ast_cli(fd, "There is no call to transfer\n"); - return RESULT_SUCCESS; - } - - tmp = ast_ext_ctx(argv[1], &ext, &ctx); - if (ctx == NULL) /* supply default context if needed */ - ctx = o->owner->context; - if (!ast_exists_extension(b, ctx, ext, 1, b->cid.cid_num)) - ast_cli(fd, "No such extension exists\n"); - else { - ast_cli(fd, "Whee, transferring %s to %s@%s.\n", - b->name, ext, ctx); - if (ast_async_goto(b, ctx, ext, 1)) - ast_cli(fd, "Failed to transfer :(\n"); - } - if (tmp) - free(tmp); - return RESULT_SUCCESS; -} - -static int console_transfer(int fd, int argc, char *argv[]) -{ - struct chan_oss_pvt *o = find_desc(oss_active); - struct ast_channel *b = NULL; - char *tmp, *ext, *ctx; - - if (argc != 3) - return RESULT_SHOWUSAGE; - if (o == NULL) - return RESULT_FAILURE; - if (o->owner == NULL || (b = ast_bridged_channel(o->owner)) == NULL) { - ast_cli(fd, "There is no call to transfer\n"); - return RESULT_SUCCESS; - } - - tmp = ast_ext_ctx(argv[2], &ext, &ctx); - if (ctx == NULL) /* supply default context if needed */ - ctx = o->owner->context; - if (!ast_exists_extension(b, ctx, ext, 1, b->cid.cid_num)) - ast_cli(fd, "No such extension exists\n"); - else { - ast_cli(fd, "Whee, transferring %s to %s@%s.\n", b->name, ext, ctx); - if (ast_async_goto(b, ctx, ext, 1)) - ast_cli(fd, "Failed to transfer :(\n"); - } - if (tmp) - free(tmp); - return RESULT_SUCCESS; -} - -static char transfer_usage[] = - "Usage: console transfer <extension>[@context]\n" - " Transfers the currently connected call to the given extension (and\n" - "context if specified)\n"; - -static int console_active_deprecated(int fd, int argc, char *argv[]) -{ - if (argc == 1) - ast_cli(fd, "active console is [%s]\n", oss_active); - else if (argc != 2) - return RESULT_SHOWUSAGE; - else { - struct chan_oss_pvt *o; - if (strcmp(argv[1], "show") == 0) { - for (o = oss_default.next; o; o = o->next) - ast_cli(fd, "device [%s] exists\n", o->name); - return RESULT_SUCCESS; - } - o = find_desc(argv[1]); - if (o == NULL) - ast_cli(fd, "No device [%s] exists\n", argv[1]); - else - oss_active = o->name; - } - return RESULT_SUCCESS; -} - -static int console_active(int fd, int argc, char *argv[]) -{ - if (argc == 2) - ast_cli(fd, "active console is [%s]\n", oss_active); - else if (argc != 3) - return RESULT_SHOWUSAGE; - else { - struct chan_oss_pvt *o; - if (strcmp(argv[2], "show") == 0) { - for (o = oss_default.next; o; o = o->next) - ast_cli(fd, "device [%s] exists\n", o->name); - return RESULT_SUCCESS; - } - o = find_desc(argv[2]); - if (o == NULL) - ast_cli(fd, "No device [%s] exists\n", argv[2]); - else - oss_active = o->name; - } - return RESULT_SUCCESS; -} - -static char active_usage[] = - "Usage: console active [device]\n" - " If used without a parameter, displays which device is the current\n" - "console. If a device is specified, the console sound device is changed to\n" - "the device specified.\n"; - -/* - * store the boost factor - */ -static void store_boost(struct chan_oss_pvt *o, char *s) -{ - double boost = 0; - if (sscanf(s, "%lf", &boost) != 1) { - ast_log(LOG_WARNING, "invalid boost <%s>\n", s); - return; - } - if (boost < -BOOST_MAX) { - ast_log(LOG_WARNING, "boost %s too small, using %d\n", s, -BOOST_MAX); - boost = -BOOST_MAX; - } else if (boost > BOOST_MAX) { - ast_log(LOG_WARNING, "boost %s too large, using %d\n", s, BOOST_MAX); - boost = BOOST_MAX; - } - boost = exp(log(10) * boost / 20) * BOOST_SCALE; - o->boost = boost; - ast_log(LOG_WARNING, "setting boost %s to %d\n", s, o->boost); -} - -static int do_boost(int fd, int argc, char *argv[]) -{ - struct chan_oss_pvt *o = find_desc(oss_active); - - if (argc == 2) - ast_cli(fd, "boost currently %5.1f\n", 20 * log10(((double) o->boost / (double) BOOST_SCALE))); - else if (argc == 3) - store_boost(o, argv[2]); - return RESULT_SUCCESS; -} - -static struct ast_cli_entry cli_oss_answer_deprecated = { - { "answer", NULL }, - console_answer_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_oss_hangup_deprecated = { - { "hangup", NULL }, - console_hangup_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_oss_flash_deprecated = { - { "flash", NULL }, - console_flash_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_oss_dial_deprecated = { - { "dial", NULL }, - console_dial_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_oss_mute_deprecated = { - { "mute", NULL }, - console_mute_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_oss_unmute_deprecated = { - { "unmute", NULL }, - console_unmute_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_oss_transfer_deprecated = { - { "transfer", NULL }, - console_transfer_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_oss_send_text_deprecated = { - { "send", "text", NULL }, - console_sendtext_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_oss_autoanswer_deprecated = { - { "autoanswer", NULL }, - console_autoanswer_deprecated, NULL, - NULL, autoanswer_complete_deprecated }; - -static struct ast_cli_entry cli_oss_boost_deprecated = { - { "oss", "boost", NULL }, - do_boost, NULL, - NULL }; - -static struct ast_cli_entry cli_oss_active_deprecated = { - { "console", NULL }, - console_active_deprecated, NULL, - NULL }; - -static struct ast_cli_entry cli_oss[] = { - { { "console", "answer", NULL }, - console_answer, "Answer an incoming console call", - answer_usage, NULL, &cli_oss_answer_deprecated }, - - { { "console", "hangup", NULL }, - console_hangup, "Hangup a call on the console", - hangup_usage, NULL, &cli_oss_hangup_deprecated }, - - { { "console", "flash", NULL }, - console_flash, "Flash a call on the console", - flash_usage, NULL, &cli_oss_flash_deprecated }, - - { { "console", "dial", NULL }, - console_dial, "Dial an extension on the console", - dial_usage, NULL, &cli_oss_dial_deprecated }, - - { { "console", "mute", NULL }, - console_mute, "Disable mic input", - mute_usage, NULL, &cli_oss_mute_deprecated }, - - { { "console", "unmute", NULL }, - console_unmute, "Enable mic input", - unmute_usage, NULL, &cli_oss_unmute_deprecated }, - - { { "console", "transfer", NULL }, - console_transfer, "Transfer a call to a different extension", - transfer_usage, NULL, &cli_oss_transfer_deprecated }, - - { { "console", "send", "text", NULL }, - console_sendtext, "Send text to the remote device", - sendtext_usage, NULL, &cli_oss_send_text_deprecated }, - - { { "console", "autoanswer", NULL }, - console_autoanswer, "Sets/displays autoanswer", - autoanswer_usage, autoanswer_complete, &cli_oss_autoanswer_deprecated }, - - { { "console", "boost", NULL }, - do_boost, "Sets/displays mic boost in dB", - NULL, NULL, &cli_oss_boost_deprecated }, - - { { "console", "active", NULL }, - console_active, "Sets/displays active console", - active_usage, NULL, &cli_oss_active_deprecated }, -}; - -/* - * store the mixer argument from the config file, filtering possibly - * invalid or dangerous values (the string is used as argument for - * system("mixer %s") - */ -static void store_mixer(struct chan_oss_pvt *o, char *s) -{ - int i; - - for (i = 0; i < strlen(s); i++) { - if (!isalnum(s[i]) && index(" \t-/", s[i]) == NULL) { - ast_log(LOG_WARNING, "Suspect char %c in mixer cmd, ignoring:\n\t%s\n", s[i], s); - return; - } - } - if (o->mixer_cmd) - free(o->mixer_cmd); - o->mixer_cmd = ast_strdup(s); - ast_log(LOG_WARNING, "setting mixer %s\n", s); -} - -/* - * store the callerid components - */ -static void store_callerid(struct chan_oss_pvt *o, char *s) -{ - ast_callerid_split(s, o->cid_name, sizeof(o->cid_name), o->cid_num, sizeof(o->cid_num)); -} - -/* - * grab fields from the config file, init the descriptor and open the device. - */ -static struct chan_oss_pvt *store_config(struct ast_config *cfg, char *ctg) -{ - struct ast_variable *v; - struct chan_oss_pvt *o; - - if (ctg == NULL) { - o = &oss_default; - ctg = "general"; - } else { - if (!(o = ast_calloc(1, sizeof(*o)))) - return NULL; - *o = oss_default; - /* "general" is also the default thing */ - if (strcmp(ctg, "general") == 0) { - o->name = ast_strdup("dsp"); - oss_active = o->name; - goto openit; - } - o->name = ast_strdup(ctg); - } - - strcpy(o->mohinterpret, "default"); - - o->lastopen = ast_tvnow(); /* don't leave it 0 or tvdiff may wrap */ - /* fill other fields from configuration */ - for (v = ast_variable_browse(cfg, ctg); v; v = v->next) { - M_START(v->name, v->value); - - /* handle jb conf */ - if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) - continue; - - M_BOOL("autoanswer", o->autoanswer) - M_BOOL("autohangup", o->autohangup) - M_BOOL("overridecontext", o->overridecontext) - M_STR("device", o->device) - M_UINT("frags", o->frags) - M_UINT("debug", oss_debug) - M_UINT("queuesize", o->queuesize) - M_STR("context", o->ctx) - M_STR("language", o->language) - M_STR("mohinterpret", o->mohinterpret) - M_STR("extension", o->ext) - M_F("mixer", store_mixer(o, v->value)) - M_F("callerid", store_callerid(o, v->value)) - M_F("boost", store_boost(o, v->value)) - M_END(; - ); - } - if (ast_strlen_zero(o->device)) - ast_copy_string(o->device, DEV_DSP, sizeof(o->device)); - if (o->mixer_cmd) { - char *cmd; - - if (asprintf(&cmd, "mixer %s", o->mixer_cmd) < 0) { - ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); - } else { - ast_log(LOG_WARNING, "running [%s]\n", cmd); - if (system(cmd) < 0) { - ast_log(LOG_WARNING, "system() failed: %s\n", strerror(errno)); - } - free(cmd); - } - } - if (o == &oss_default) /* we are done with the default */ - return NULL; - - openit: -#if TRYOPEN - if (setformat(o, O_RDWR) < 0) { /* open device */ - if (option_verbose > 0) { - ast_verbose(VERBOSE_PREFIX_2 "Device %s not detected\n", ctg); - ast_verbose(VERBOSE_PREFIX_2 "Turn off OSS support by adding " "'noload=chan_oss.so' in /etc/asterisk/modules.conf\n"); - } - goto error; - } - if (o->duplex != M_FULL) - ast_log(LOG_WARNING, "XXX I don't work right with non " "full-duplex sound cards XXX\n"); -#endif /* TRYOPEN */ - if (pipe(o->sndcmd) != 0) { - ast_log(LOG_ERROR, "Unable to create pipe\n"); - goto error; - } - ast_pthread_create_background(&o->sthread, NULL, sound_thread, o); - /* link into list of devices */ - if (o != &oss_default) { - o->next = oss_default.next; - oss_default.next = o; - } - return o; - - error: - if (o != &oss_default) - free(o); - return NULL; -} - -static int load_module(void) -{ - struct ast_config *cfg = NULL; - char *ctg = NULL; - - /* Copy the default jb config over global_jbconf */ - memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); - - /* load config file */ - if (!(cfg = ast_config_load(config))) { - ast_log(LOG_NOTICE, "Unable to load config %s\n", config); - return AST_MODULE_LOAD_DECLINE; - } - - do { - store_config(cfg, ctg); - } while ( (ctg = ast_category_browse(cfg, ctg)) != NULL); - - ast_config_destroy(cfg); - - if (find_desc(oss_active) == NULL) { - ast_log(LOG_NOTICE, "Device %s not found\n", oss_active); - /* XXX we could default to 'dsp' perhaps ? */ - /* XXX should cleanup allocated memory etc. */ - return AST_MODULE_LOAD_FAILURE; - } - - if (ast_channel_register(&oss_tech)) { - ast_log(LOG_ERROR, "Unable to register channel type 'OSS'\n"); - return AST_MODULE_LOAD_FAILURE; - } - - ast_cli_register_multiple(cli_oss, sizeof(cli_oss) / sizeof(struct ast_cli_entry)); - - return AST_MODULE_LOAD_SUCCESS; -} - - -static int unload_module(void) -{ - struct chan_oss_pvt *o; - - ast_channel_unregister(&oss_tech); - ast_cli_unregister_multiple(cli_oss, sizeof(cli_oss) / sizeof(struct ast_cli_entry)); - - for (o = oss_default.next; o; o = o->next) { - close(o->sounddev); - if (o->sndcmd[0] > 0) { - close(o->sndcmd[0]); - close(o->sndcmd[1]); - } - if (o->owner) - ast_softhangup(o->owner, AST_SOFTHANGUP_APPUNLOAD); - if (o->owner) /* XXX how ??? */ - return -1; - /* XXX what about the thread ? */ - /* XXX what about the memory allocated ? */ - } - return 0; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "OSS Console Channel Driver"); diff --git a/1.4.23-rc4/channels/chan_phone.c b/1.4.23-rc4/channels/chan_phone.c deleted file mode 100644 index 55bda8b72..000000000 --- a/1.4.23-rc4/channels/chan_phone.c +++ /dev/null @@ -1,1433 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief Generic Linux Telephony Interface driver - * - * \author Mark Spencer <markster@digium.com> - * - * \ingroup channel_drivers - */ - -/*** MODULEINFO - <depend>ixjuser</depend> - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <string.h> -#include <ctype.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> -#include <arpa/inet.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <signal.h> -#ifdef HAVE_LINUX_COMPILER_H -#include <linux/compiler.h> -#endif -#include <linux/telephony.h> -/* Still use some IXJ specific stuff */ -#include <linux/version.h> -#include <linux/ixjuser.h> - -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/utils.h" -#include "asterisk/callerid.h" -#include "asterisk/causes.h" -#include "asterisk/stringfields.h" -#include "asterisk/musiconhold.h" - -#include "DialTone.h" - -#ifdef QTI_PHONEJACK_TJ_PCI /* check for the newer quicknet driver v.3.1.0 which has this symbol */ -#define QNDRV_VER 310 -#else -#define QNDRV_VER 100 -#endif - -#if QNDRV_VER > 100 -#ifdef __linux__ -#define IXJ_PHONE_RING_START(x) ioctl(p->fd, PHONE_RING_START, &x); -#else /* FreeBSD and others */ -#define IXJ_PHONE_RING_START(x) ioctl(p->fd, PHONE_RING_START, x); -#endif /* __linux__ */ -#else /* older driver */ -#define IXJ_PHONE_RING_START(x) ioctl(p->fd, PHONE_RING_START, &x); -#endif - -#define DEFAULT_CALLER_ID "Unknown" -#define PHONE_MAX_BUF 480 -#define DEFAULT_GAIN 0x100 - -static const char tdesc[] = "Standard Linux Telephony API Driver"; -static const char config[] = "phone.conf"; - -/* Default context for dialtone mode */ -static char context[AST_MAX_EXTENSION] = "default"; - -/* Default language */ -static char language[MAX_LANGUAGE] = ""; - -static int echocancel = AEC_OFF; - -static int silencesupression = 0; - -static int prefformat = AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW; - -/* Protect the interface list (of phone_pvt's) */ -AST_MUTEX_DEFINE_STATIC(iflock); - -/* Protect the monitoring thread, so only one process can kill or start it, and not - when it's doing something critical. */ -AST_MUTEX_DEFINE_STATIC(monlock); - -/* Boolean value whether the monitoring thread shall continue. */ -static unsigned int monitor; - -/* This is the thread for the monitor which checks for input on the channels - which are not currently in use. */ -static pthread_t monitor_thread = AST_PTHREADT_NULL; - -static int restart_monitor(void); - -/* The private structures of the Phone Jack channels are linked for - selecting outgoing channels */ - -#define MODE_DIALTONE 1 -#define MODE_IMMEDIATE 2 -#define MODE_FXO 3 -#define MODE_FXS 4 -#define MODE_SIGMA 5 - -static struct phone_pvt { - int fd; /* Raw file descriptor for this device */ - struct ast_channel *owner; /* Channel we belong to, possibly NULL */ - int mode; /* Is this in the */ - int lastformat; /* Last output format */ - int lastinput; /* Last input format */ - int ministate; /* Miniature state, for dialtone mode */ - char dev[256]; /* Device name */ - struct phone_pvt *next; /* Next channel in list */ - struct ast_frame fr; /* Frame */ - char offset[AST_FRIENDLY_OFFSET]; - char buf[PHONE_MAX_BUF]; /* Static buffer for reading frames */ - int obuflen; - int dialtone; - int txgain, rxgain; /* gain control for playing, recording */ - /* 0x100 - 1.0, 0x200 - 2.0, 0x80 - 0.5 */ - int cpt; /* Call Progress Tone playing? */ - int silencesupression; - char context[AST_MAX_EXTENSION]; - char obuf[PHONE_MAX_BUF * 2]; - char ext[AST_MAX_EXTENSION]; - char language[MAX_LANGUAGE]; - char cid_num[AST_MAX_EXTENSION]; - char cid_name[AST_MAX_EXTENSION]; -} *iflist = NULL; - -static char cid_num[AST_MAX_EXTENSION]; -static char cid_name[AST_MAX_EXTENSION]; - -static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause); -static int phone_digit_begin(struct ast_channel *ast, char digit); -static int phone_digit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int phone_call(struct ast_channel *ast, char *dest, int timeout); -static int phone_hangup(struct ast_channel *ast); -static int phone_answer(struct ast_channel *ast); -static struct ast_frame *phone_read(struct ast_channel *ast); -static int phone_write(struct ast_channel *ast, struct ast_frame *frame); -static struct ast_frame *phone_exception(struct ast_channel *ast); -static int phone_send_text(struct ast_channel *ast, const char *text); -static int phone_fixup(struct ast_channel *old, struct ast_channel *new); -static int phone_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen); - -static const struct ast_channel_tech phone_tech = { - .type = "Phone", - .description = tdesc, - .capabilities = AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW, - .requester = phone_request, - .send_digit_begin = phone_digit_begin, - .send_digit_end = phone_digit_end, - .call = phone_call, - .hangup = phone_hangup, - .answer = phone_answer, - .read = phone_read, - .write = phone_write, - .exception = phone_exception, - .indicate = phone_indicate, - .fixup = phone_fixup -}; - -static struct ast_channel_tech phone_tech_fxs = { - .type = "Phone", - .description = tdesc, - .requester = phone_request, - .send_digit_begin = phone_digit_begin, - .send_digit_end = phone_digit_end, - .call = phone_call, - .hangup = phone_hangup, - .answer = phone_answer, - .read = phone_read, - .write = phone_write, - .exception = phone_exception, - .write_video = phone_write, - .send_text = phone_send_text, - .indicate = phone_indicate, - .fixup = phone_fixup -}; - -static struct ast_channel_tech *cur_tech; - -static int phone_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen) -{ - struct phone_pvt *p = chan->tech_pvt; - int res=-1; - ast_log(LOG_DEBUG, "Requested indication %d on channel %s\n", condition, chan->name); - switch(condition) { - case AST_CONTROL_FLASH: - ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_ON_HOOK); - usleep(320000); - ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_OFF_HOOK); - p->lastformat = -1; - res = 0; - break; - case AST_CONTROL_HOLD: - ast_moh_start(chan, data, NULL); - break; - case AST_CONTROL_UNHOLD: - ast_moh_stop(chan); - break; - case AST_CONTROL_SRCUPDATE: - res = 0; - break; - default: - ast_log(LOG_WARNING, "Condition %d is not supported on channel %s\n", condition, chan->name); - } - return res; -} - -static int phone_fixup(struct ast_channel *old, struct ast_channel *new) -{ - struct phone_pvt *pvt = old->tech_pvt; - if (pvt && pvt->owner == old) - pvt->owner = new; - return 0; -} - -static int phone_digit_begin(struct ast_channel *chan, char digit) -{ - /* XXX Modify this callback to let Asterisk support controlling the length of DTMF */ - return 0; -} - -static int phone_digit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ - struct phone_pvt *p; - int outdigit; - p = ast->tech_pvt; - ast_log(LOG_DEBUG, "Dialed %c\n", digit); - switch(digit) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - outdigit = digit - '0'; - break; - case '*': - outdigit = 11; - break; - case '#': - outdigit = 12; - break; - case 'f': /*flash*/ - case 'F': - ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_ON_HOOK); - usleep(320000); - ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_OFF_HOOK); - p->lastformat = -1; - return 0; - default: - ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit); - return -1; - } - ast_log(LOG_DEBUG, "Dialed %d\n", outdigit); - ioctl(p->fd, PHONE_PLAY_TONE, outdigit); - p->lastformat = -1; - return 0; -} - -static int phone_call(struct ast_channel *ast, char *dest, int timeout) -{ - struct phone_pvt *p; - - PHONE_CID cid; - time_t UtcTime; - struct tm tm; - int start; - - time(&UtcTime); - ast_localtime(&UtcTime, &tm, NULL); - - memset(&cid, 0, sizeof(PHONE_CID)); - if(&tm != NULL) { - snprintf(cid.month, sizeof(cid.month), "%02d",(tm.tm_mon + 1)); - snprintf(cid.day, sizeof(cid.day), "%02d", tm.tm_mday); - snprintf(cid.hour, sizeof(cid.hour), "%02d", tm.tm_hour); - snprintf(cid.min, sizeof(cid.min), "%02d", tm.tm_min); - } - /* the standard format of ast->callerid is: "name" <number>, but not always complete */ - if (ast_strlen_zero(ast->cid.cid_name)) - strcpy(cid.name, DEFAULT_CALLER_ID); - else - ast_copy_string(cid.name, ast->cid.cid_name, sizeof(cid.name)); - - if (ast->cid.cid_num) - ast_copy_string(cid.number, ast->cid.cid_num, sizeof(cid.number)); - - p = ast->tech_pvt; - - if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { - ast_log(LOG_WARNING, "phone_call called on %s, neither down nor reserved\n", ast->name); - return -1; - } - if (option_debug) - ast_log(LOG_DEBUG, "Ringing %s on %s (%d)\n", dest, ast->name, ast->fds[0]); - - start = IXJ_PHONE_RING_START(cid); - if (start == -1) - return -1; - - if (p->mode == MODE_FXS) { - char *digit = strchr(dest, '/'); - if (digit) - { - digit++; - while (*digit) - phone_digit_end(ast, *digit++, 0); - } - } - - ast_setstate(ast, AST_STATE_RINGING); - ast_queue_control(ast, AST_CONTROL_RINGING); - return 0; -} - -static int phone_hangup(struct ast_channel *ast) -{ - struct phone_pvt *p; - p = ast->tech_pvt; - if (option_debug) - ast_log(LOG_DEBUG, "phone_hangup(%s)\n", ast->name); - if (!ast->tech_pvt) { - ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); - return 0; - } - /* XXX Is there anything we can do to really hang up except stop recording? */ - ast_setstate(ast, AST_STATE_DOWN); - if (ioctl(p->fd, PHONE_REC_STOP)) - ast_log(LOG_WARNING, "Failed to stop recording\n"); - if (ioctl(p->fd, PHONE_PLAY_STOP)) - ast_log(LOG_WARNING, "Failed to stop playing\n"); - if (ioctl(p->fd, PHONE_RING_STOP)) - ast_log(LOG_WARNING, "Failed to stop ringing\n"); - if (ioctl(p->fd, PHONE_CPT_STOP)) - ast_log(LOG_WARNING, "Failed to stop sounds\n"); - - /* If it's an FXO, hang them up */ - if (p->mode == MODE_FXO) { - if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_ON_HOOK)) - ast_log(LOG_DEBUG, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n",ast->name, strerror(errno)); - } - - /* If they're off hook, give a busy signal */ - if (ioctl(p->fd, PHONE_HOOKSTATE)) { - if (option_debug) - ast_log(LOG_DEBUG, "Got hunghup, giving busy signal\n"); - ioctl(p->fd, PHONE_BUSY); - p->cpt = 1; - } - p->lastformat = -1; - p->lastinput = -1; - p->ministate = 0; - p->obuflen = 0; - p->dialtone = 0; - memset(p->ext, 0, sizeof(p->ext)); - ((struct phone_pvt *)(ast->tech_pvt))->owner = NULL; - ast_module_unref(ast_module_info->self); - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Hungup '%s'\n", ast->name); - ast->tech_pvt = NULL; - ast_setstate(ast, AST_STATE_DOWN); - restart_monitor(); - return 0; -} - -static int phone_setup(struct ast_channel *ast) -{ - struct phone_pvt *p; - p = ast->tech_pvt; - ioctl(p->fd, PHONE_CPT_STOP); - /* Nothing to answering really, just start recording */ - if (ast->rawreadformat == AST_FORMAT_G723_1) { - /* Prefer g723 */ - ioctl(p->fd, PHONE_REC_STOP); - if (p->lastinput != AST_FORMAT_G723_1) { - p->lastinput = AST_FORMAT_G723_1; - if (ioctl(p->fd, PHONE_REC_CODEC, G723_63)) { - ast_log(LOG_WARNING, "Failed to set codec to g723.1\n"); - return -1; - } - } - } else if (ast->rawreadformat == AST_FORMAT_SLINEAR) { - ioctl(p->fd, PHONE_REC_STOP); - if (p->lastinput != AST_FORMAT_SLINEAR) { - p->lastinput = AST_FORMAT_SLINEAR; - if (ioctl(p->fd, PHONE_REC_CODEC, LINEAR16)) { - ast_log(LOG_WARNING, "Failed to set codec to signed linear 16\n"); - return -1; - } - } - } else if (ast->rawreadformat == AST_FORMAT_ULAW) { - ioctl(p->fd, PHONE_REC_STOP); - if (p->lastinput != AST_FORMAT_ULAW) { - p->lastinput = AST_FORMAT_ULAW; - if (ioctl(p->fd, PHONE_REC_CODEC, ULAW)) { - ast_log(LOG_WARNING, "Failed to set codec to uLaw\n"); - return -1; - } - } - } else if (p->mode == MODE_FXS) { - ioctl(p->fd, PHONE_REC_STOP); - if (p->lastinput != ast->rawreadformat) { - p->lastinput = ast->rawreadformat; - if (ioctl(p->fd, PHONE_REC_CODEC, ast->rawreadformat)) { - ast_log(LOG_WARNING, "Failed to set codec to %d\n", - ast->rawreadformat); - return -1; - } - } - } else { - ast_log(LOG_WARNING, "Can't do format %s\n", ast_getformatname(ast->rawreadformat)); - return -1; - } - if (ioctl(p->fd, PHONE_REC_START)) { - ast_log(LOG_WARNING, "Failed to start recording\n"); - return -1; - } - /* set the DTMF times (the default is too short) */ - ioctl(p->fd, PHONE_SET_TONE_ON_TIME, 300); - ioctl(p->fd, PHONE_SET_TONE_OFF_TIME, 200); - return 0; -} - -static int phone_answer(struct ast_channel *ast) -{ - struct phone_pvt *p; - p = ast->tech_pvt; - /* In case it's a LineJack, take it off hook */ - if (p->mode == MODE_FXO) { - if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_OFF_HOOK)) - ast_log(LOG_DEBUG, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n", ast->name, strerror(errno)); - else - ast_log(LOG_DEBUG, "Took linejack off hook\n"); - } - phone_setup(ast); - if (option_debug) - ast_log(LOG_DEBUG, "phone_answer(%s)\n", ast->name); - ast->rings = 0; - ast_setstate(ast, AST_STATE_UP); - return 0; -} - -#if 0 -static char phone_2digit(char c) -{ - if (c == 12) - return '#'; - else if (c == 11) - return '*'; - else if ((c < 10) && (c >= 0)) - return '0' + c - 1; - else - return '?'; -} -#endif - -static struct ast_frame *phone_exception(struct ast_channel *ast) -{ - int res; - union telephony_exception phonee; - struct phone_pvt *p = ast->tech_pvt; - char digit; - - /* Some nice norms */ - p->fr.datalen = 0; - p->fr.samples = 0; - p->fr.data = NULL; - p->fr.src = "Phone"; - p->fr.offset = 0; - p->fr.mallocd=0; - p->fr.delivery = ast_tv(0,0); - - phonee.bytes = ioctl(p->fd, PHONE_EXCEPTION); - if (phonee.bits.dtmf_ready) { - if (option_debug) - ast_log(LOG_DEBUG, "phone_exception(): DTMF\n"); - - /* We've got a digit -- Just handle this nicely and easily */ - digit = ioctl(p->fd, PHONE_GET_DTMF_ASCII); - p->fr.subclass = digit; - p->fr.frametype = AST_FRAME_DTMF; - return &p->fr; - } - if (phonee.bits.hookstate) { - if (option_debug) - ast_log(LOG_DEBUG, "Hookstate changed\n"); - res = ioctl(p->fd, PHONE_HOOKSTATE); - /* See if we've gone on hook, if so, notify by returning NULL */ - if (option_debug) - ast_log(LOG_DEBUG, "New hookstate: %d\n", res); - if (!res && (p->mode != MODE_FXO)) - return NULL; - else { - if (ast->_state == AST_STATE_RINGING) { - /* They've picked up the phone */ - p->fr.frametype = AST_FRAME_CONTROL; - p->fr.subclass = AST_CONTROL_ANSWER; - phone_setup(ast); - ast_setstate(ast, AST_STATE_UP); - return &p->fr; - } else - ast_log(LOG_WARNING, "Got off hook in weird state %d\n", ast->_state); - } - } -#if 1 - if (phonee.bits.pstn_ring) - ast_verbose("Unit is ringing\n"); - if (phonee.bits.caller_id) { - ast_verbose("We have caller ID\n"); - } - if (phonee.bits.pstn_wink) - ast_verbose("Detected Wink\n"); -#endif - /* Strange -- nothing there.. */ - p->fr.frametype = AST_FRAME_NULL; - p->fr.subclass = 0; - return &p->fr; -} - -static struct ast_frame *phone_read(struct ast_channel *ast) -{ - int res; - struct phone_pvt *p = ast->tech_pvt; - - - /* Some nice norms */ - p->fr.datalen = 0; - p->fr.samples = 0; - p->fr.data = NULL; - p->fr.src = "Phone"; - p->fr.offset = 0; - p->fr.mallocd=0; - p->fr.delivery = ast_tv(0,0); - - /* Try to read some data... */ - CHECK_BLOCKING(ast); - res = read(p->fd, p->buf, PHONE_MAX_BUF); - ast_clear_flag(ast, AST_FLAG_BLOCKING); - if (res < 0) { -#if 0 - if (errno == EAGAIN) { - ast_log(LOG_WARNING, "Null frame received\n"); - p->fr.frametype = AST_FRAME_NULL; - p->fr.subclass = 0; - return &p->fr; - } -#endif - ast_log(LOG_WARNING, "Error reading: %s\n", strerror(errno)); - return NULL; - } - p->fr.data = p->buf; - if (p->mode != MODE_FXS) - switch(p->buf[0] & 0x3) { - case '0': - case '1': - /* Normal */ - break; - case '2': - case '3': - /* VAD/CNG, only send two words */ - res = 4; - break; - } - p->fr.samples = 240; - p->fr.datalen = res; - p->fr.frametype = p->lastinput <= AST_FORMAT_MAX_AUDIO ? - AST_FRAME_VOICE : - p->lastinput <= AST_FORMAT_PNG ? AST_FRAME_IMAGE - : AST_FRAME_VIDEO; - p->fr.subclass = p->lastinput; - p->fr.offset = AST_FRIENDLY_OFFSET; - /* Byteswap from little-endian to native-endian */ - if (p->fr.subclass == AST_FORMAT_SLINEAR) - ast_frame_byteswap_le(&p->fr); - return &p->fr; -} - -static int phone_write_buf(struct phone_pvt *p, const char *buf, int len, int frlen, int swap) -{ - int res; - /* Store as much of the buffer as we can, then write fixed frames */ - int space = sizeof(p->obuf) - p->obuflen; - /* Make sure we have enough buffer space to store the frame */ - if (space < len) - len = space; - if (swap) - ast_swapcopy_samples(p->obuf+p->obuflen, buf, len/2); - else - memcpy(p->obuf + p->obuflen, buf, len); - p->obuflen += len; - while(p->obuflen > frlen) { - res = write(p->fd, p->obuf, frlen); - if (res != frlen) { - if (res < 1) { -/* - * Card is in non-blocking mode now and it works well now, but there are - * lot of messages like this. So, this message is temporarily disabled. - */ - return 0; - } else { - ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frlen); - } - } - p->obuflen -= frlen; - /* Move memory if necessary */ - if (p->obuflen) - memmove(p->obuf, p->obuf + frlen, p->obuflen); - } - return len; -} - -static int phone_send_text(struct ast_channel *ast, const char *text) -{ - int length = strlen(text); - return phone_write_buf(ast->tech_pvt, text, length, length, 0) == - length ? 0 : -1; -} - -static int phone_write(struct ast_channel *ast, struct ast_frame *frame) -{ - struct phone_pvt *p = ast->tech_pvt; - int res; - int maxfr=0; - char *pos; - int sofar; - int expected; - int codecset = 0; - char tmpbuf[4]; - /* Write a frame of (presumably voice) data */ - if (frame->frametype != AST_FRAME_VOICE && p->mode != MODE_FXS) { - if (frame->frametype != AST_FRAME_IMAGE) - ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype); - return 0; - } - if (!(frame->subclass & - (AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW)) && - p->mode != MODE_FXS) { - ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass); - return -1; - } -#if 0 - /* If we're not in up mode, go into up mode now */ - if (ast->_state != AST_STATE_UP) { - ast_setstate(ast, AST_STATE_UP); - phone_setup(ast); - } -#else - if (ast->_state != AST_STATE_UP) { - /* Don't try tos end audio on-hook */ - return 0; - } -#endif - if (frame->subclass == AST_FORMAT_G723_1) { - if (p->lastformat != AST_FORMAT_G723_1) { - ioctl(p->fd, PHONE_PLAY_STOP); - ioctl(p->fd, PHONE_REC_STOP); - if (ioctl(p->fd, PHONE_PLAY_CODEC, G723_63)) { - ast_log(LOG_WARNING, "Unable to set G723.1 mode\n"); - return -1; - } - if (ioctl(p->fd, PHONE_REC_CODEC, G723_63)) { - ast_log(LOG_WARNING, "Unable to set G723.1 mode\n"); - return -1; - } - p->lastformat = AST_FORMAT_G723_1; - p->lastinput = AST_FORMAT_G723_1; - /* Reset output buffer */ - p->obuflen = 0; - codecset = 1; - } - if (frame->datalen > 24) { - ast_log(LOG_WARNING, "Frame size too large for G.723.1 (%d bytes)\n", frame->datalen); - return -1; - } - maxfr = 24; - } else if (frame->subclass == AST_FORMAT_SLINEAR) { - if (p->lastformat != AST_FORMAT_SLINEAR) { - ioctl(p->fd, PHONE_PLAY_STOP); - ioctl(p->fd, PHONE_REC_STOP); - if (ioctl(p->fd, PHONE_PLAY_CODEC, LINEAR16)) { - ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n"); - return -1; - } - if (ioctl(p->fd, PHONE_REC_CODEC, LINEAR16)) { - ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n"); - return -1; - } - p->lastformat = AST_FORMAT_SLINEAR; - p->lastinput = AST_FORMAT_SLINEAR; - codecset = 1; - /* Reset output buffer */ - p->obuflen = 0; - } - maxfr = 480; - } else if (frame->subclass == AST_FORMAT_ULAW) { - if (p->lastformat != AST_FORMAT_ULAW) { - ioctl(p->fd, PHONE_PLAY_STOP); - ioctl(p->fd, PHONE_REC_STOP); - if (ioctl(p->fd, PHONE_PLAY_CODEC, ULAW)) { - ast_log(LOG_WARNING, "Unable to set uLaw mode\n"); - return -1; - } - if (ioctl(p->fd, PHONE_REC_CODEC, ULAW)) { - ast_log(LOG_WARNING, "Unable to set uLaw mode\n"); - return -1; - } - p->lastformat = AST_FORMAT_ULAW; - p->lastinput = AST_FORMAT_ULAW; - codecset = 1; - /* Reset output buffer */ - p->obuflen = 0; - } - maxfr = 240; - } else { - if (p->lastformat != frame->subclass) { - ioctl(p->fd, PHONE_PLAY_STOP); - ioctl(p->fd, PHONE_REC_STOP); - if (ioctl(p->fd, PHONE_PLAY_CODEC, frame->subclass)) { - ast_log(LOG_WARNING, "Unable to set %d mode\n", - frame->subclass); - return -1; - } - if (ioctl(p->fd, PHONE_REC_CODEC, frame->subclass)) { - ast_log(LOG_WARNING, "Unable to set %d mode\n", - frame->subclass); - return -1; - } - p->lastformat = frame->subclass; - p->lastinput = frame->subclass; - codecset = 1; - /* Reset output buffer */ - p->obuflen = 0; - } - maxfr = 480; - } - if (codecset) { - ioctl(p->fd, PHONE_REC_DEPTH, 3); - ioctl(p->fd, PHONE_PLAY_DEPTH, 3); - if (ioctl(p->fd, PHONE_PLAY_START)) { - ast_log(LOG_WARNING, "Failed to start playback\n"); - return -1; - } - if (ioctl(p->fd, PHONE_REC_START)) { - ast_log(LOG_WARNING, "Failed to start recording\n"); - return -1; - } - } - /* If we get here, we have a frame of Appropriate data */ - sofar = 0; - pos = frame->data; - while(sofar < frame->datalen) { - /* Write in no more than maxfr sized frames */ - expected = frame->datalen - sofar; - if (maxfr < expected) - expected = maxfr; - /* XXX Internet Phone Jack does not handle the 4-byte VAD frame properly! XXX - we have to pad it to 24 bytes still. */ - if (frame->datalen == 4) { - if (p->silencesupression) { - memset(tmpbuf, 0, sizeof(tmpbuf)); - memcpy(tmpbuf, frame->data, 4); - expected = 24; - res = phone_write_buf(p, tmpbuf, expected, maxfr, 0); - } - res = 4; - expected=4; - } else { - int swap = 0; -#if __BYTE_ORDER == __BIG_ENDIAN - if (frame->subclass == AST_FORMAT_SLINEAR) - swap = 1; /* Swap big-endian samples to little-endian as we copy */ -#endif - res = phone_write_buf(p, pos, expected, maxfr, swap); - } - if (res != expected) { - if ((errno != EAGAIN) && (errno != EINTR)) { - if (res < 0) - ast_log(LOG_WARNING, "Write returned error (%s)\n", strerror(errno)); - /* - * Card is in non-blocking mode now and it works well now, but there are - * lot of messages like this. So, this message is temporarily disabled. - */ -#if 0 - else - ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frame->datalen); -#endif - return -1; - } else /* Pretend it worked */ - res = expected; - } - sofar += res; - pos += res; - } - return 0; -} - -static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *context) -{ - struct ast_channel *tmp; - struct phone_codec_data codec; - tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", i->ext, i->context, 0, "Phone/%s", i->dev + 5); - if (tmp) { - tmp->tech = cur_tech; - tmp->fds[0] = i->fd; - /* XXX Switching formats silently causes kernel panics XXX */ - if (i->mode == MODE_FXS && - ioctl(i->fd, PHONE_QUERY_CODEC, &codec) == 0) { - if (codec.type == LINEAR16) - tmp->nativeformats = - tmp->rawreadformat = - tmp->rawwriteformat = - AST_FORMAT_SLINEAR; - else { - tmp->nativeformats = - tmp->rawreadformat = - tmp->rawwriteformat = - prefformat & ~AST_FORMAT_SLINEAR; - } - } - else { - tmp->nativeformats = prefformat; - tmp->rawreadformat = prefformat; - tmp->rawwriteformat = prefformat; - } - /* no need to call ast_setstate: the channel_alloc already did its job */ - if (state == AST_STATE_RING) - tmp->rings = 1; - tmp->tech_pvt = i; - ast_copy_string(tmp->context, context, sizeof(tmp->context)); - if (!ast_strlen_zero(i->ext)) - ast_copy_string(tmp->exten, i->ext, sizeof(tmp->exten)); - else - strcpy(tmp->exten, "s"); - if (!ast_strlen_zero(i->language)) - ast_string_field_set(tmp, language, i->language); - - /* Don't use ast_set_callerid() here because it will - * generate a NewCallerID event before the NewChannel event */ - tmp->cid.cid_ani = ast_strdup(i->cid_num); - - i->owner = tmp; - ast_module_ref(ast_module_info->self); - if (state != AST_STATE_DOWN) { - if (state == AST_STATE_RING) { - ioctl(tmp->fds[0], PHONE_RINGBACK); - i->cpt = 1; - } - if (ast_pbx_start(tmp)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); - ast_hangup(tmp); - } - } - } else - ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); - return tmp; -} - -static void phone_mini_packet(struct phone_pvt *i) -{ - int res; - char buf[1024]; - /* Ignore stuff we read... */ - res = read(i->fd, buf, sizeof(buf)); - if (res < 1) { - ast_log(LOG_WARNING, "Read returned %d: %s\n", res, strerror(errno)); - return; - } -} - -static void phone_check_exception(struct phone_pvt *i) -{ - int offhook=0; - char digit[2] = {0 , 0}; - union telephony_exception phonee; - /* XXX Do something XXX */ -#if 0 - ast_log(LOG_DEBUG, "Exception!\n"); -#endif - phonee.bytes = ioctl(i->fd, PHONE_EXCEPTION); - if (phonee.bits.dtmf_ready) { - digit[0] = ioctl(i->fd, PHONE_GET_DTMF_ASCII); - if (i->mode == MODE_DIALTONE || i->mode == MODE_FXS || i->mode == MODE_SIGMA) { - ioctl(i->fd, PHONE_PLAY_STOP); - ioctl(i->fd, PHONE_REC_STOP); - ioctl(i->fd, PHONE_CPT_STOP); - i->dialtone = 0; - if (strlen(i->ext) < AST_MAX_EXTENSION - 1) - strncat(i->ext, digit, sizeof(i->ext) - strlen(i->ext) - 1); - if ((i->mode != MODE_FXS || - !(phonee.bytes = ioctl(i->fd, PHONE_EXCEPTION)) || - !phonee.bits.dtmf_ready) && - ast_exists_extension(NULL, i->context, i->ext, 1, i->cid_num)) { - /* It's a valid extension in its context, get moving! */ - phone_new(i, AST_STATE_RING, i->context); - /* No need to restart monitor, we are the monitor */ - } else if (!ast_canmatch_extension(NULL, i->context, i->ext, 1, i->cid_num)) { - /* There is nothing in the specified extension that can match anymore. - Try the default */ - if (ast_exists_extension(NULL, "default", i->ext, 1, i->cid_num)) { - /* Check the default, too... */ - phone_new(i, AST_STATE_RING, "default"); - /* XXX This should probably be justified better XXX */ - } else if (!ast_canmatch_extension(NULL, "default", i->ext, 1, i->cid_num)) { - /* It's not a valid extension, give a busy signal */ - if (option_debug) - ast_log(LOG_DEBUG, "%s can't match anything in %s or default\n", i->ext, i->context); - ioctl(i->fd, PHONE_BUSY); - i->cpt = 1; - } - } -#if 0 - ast_verbose("Extension is %s\n", i->ext); -#endif - } - } - if (phonee.bits.hookstate) { - offhook = ioctl(i->fd, PHONE_HOOKSTATE); - if (offhook) { - if (i->mode == MODE_IMMEDIATE) { - phone_new(i, AST_STATE_RING, i->context); - } else if (i->mode == MODE_DIALTONE) { - ast_module_ref(ast_module_info->self); - /* Reset the extension */ - i->ext[0] = '\0'; - /* Play the dialtone */ - i->dialtone++; - ioctl(i->fd, PHONE_PLAY_STOP); - ioctl(i->fd, PHONE_PLAY_CODEC, ULAW); - ioctl(i->fd, PHONE_PLAY_START); - i->lastformat = -1; - } else if (i->mode == MODE_SIGMA) { - ast_module_ref(ast_module_info->self); - /* Reset the extension */ - i->ext[0] = '\0'; - /* Play the dialtone */ - i->dialtone++; - ioctl(i->fd, PHONE_DIALTONE); - } - } else { - if (i->dialtone) - ast_module_unref(ast_module_info->self); - memset(i->ext, 0, sizeof(i->ext)); - if (i->cpt) - { - ioctl(i->fd, PHONE_CPT_STOP); - i->cpt = 0; - } - ioctl(i->fd, PHONE_PLAY_STOP); - ioctl(i->fd, PHONE_REC_STOP); - i->dialtone = 0; - i->lastformat = -1; - } - } - if (phonee.bits.pstn_ring) { - ast_verbose("Unit is ringing\n"); - phone_new(i, AST_STATE_RING, i->context); - } - if (phonee.bits.caller_id) - ast_verbose("We have caller ID\n"); - - -} - -static void *do_monitor(void *data) -{ - fd_set rfds, efds; - int n, res; - struct phone_pvt *i; - int tonepos = 0; - /* The tone we're playing this round */ - struct timeval tv = {0,0}; - int dotone; - /* This thread monitors all the frame relay interfaces which are not yet in use - (and thus do not have a separate thread) indefinitely */ - while (monitor) { - /* Don't let anybody kill us right away. Nobody should lock the interface list - and wait for the monitor list, but the other way around is okay. */ - /* Lock the interface list */ - if (ast_mutex_lock(&iflock)) { - ast_log(LOG_ERROR, "Unable to grab interface lock\n"); - return NULL; - } - /* Build the stuff we're going to select on, that is the socket of every - phone_pvt that does not have an associated owner channel */ - n = -1; - FD_ZERO(&rfds); - FD_ZERO(&efds); - i = iflist; - dotone = 0; - while (i) { - if (FD_ISSET(i->fd, &rfds)) - ast_log(LOG_WARNING, "Descriptor %d appears twice (%s)?\n", i->fd, i->dev); - if (!i->owner) { - /* This needs to be watched, as it lacks an owner */ - FD_SET(i->fd, &rfds); - FD_SET(i->fd, &efds); - if (i->fd > n) - n = i->fd; - if (i->dialtone && i->mode != MODE_SIGMA) { - /* Remember we're going to have to come back and play - more dialtones */ - if (ast_tvzero(tv)) { - /* If we're due for a dialtone, play one */ - if (write(i->fd, DialTone + tonepos, 240) != 240) - ast_log(LOG_WARNING, "Dial tone write error\n"); - } - dotone++; - } - } - - i = i->next; - } - /* Okay, now that we know what to do, release the interface lock */ - ast_mutex_unlock(&iflock); - - /* Wait indefinitely for something to happen */ - if (dotone && i && i->mode != MODE_SIGMA) { - /* If we're ready to recycle the time, set it to 30 ms */ - tonepos += 240; - if (tonepos >= sizeof(DialTone)) - tonepos = 0; - if (ast_tvzero(tv)) { - tv = ast_tv(30000, 0); - } - res = ast_select(n + 1, &rfds, NULL, &efds, &tv); - } else { - res = ast_select(n + 1, &rfds, NULL, &efds, NULL); - tv = ast_tv(0,0); - tonepos = 0; - } - /* Okay, select has finished. Let's see what happened. */ - if (res < 0) { - ast_log(LOG_DEBUG, "select return %d: %s\n", res, strerror(errno)); - continue; - } - /* If there are no fd's changed, just continue, it's probably time - to play some more dialtones */ - if (!res) - continue; - /* Alright, lock the interface list again, and let's look and see what has - happened */ - if (ast_mutex_lock(&iflock)) { - ast_log(LOG_WARNING, "Unable to lock the interface list\n"); - continue; - } - - i = iflist; - for(; i; i=i->next) { - if (FD_ISSET(i->fd, &rfds)) { - if (i->owner) { - continue; - } - phone_mini_packet(i); - } - if (FD_ISSET(i->fd, &efds)) { - if (i->owner) { - continue; - } - phone_check_exception(i); - } - } - ast_mutex_unlock(&iflock); - } - return NULL; - -} - -static int restart_monitor() -{ - /* If we're supposed to be stopped -- stay stopped */ - if (monitor_thread == AST_PTHREADT_STOP) - return 0; - if (ast_mutex_lock(&monlock)) { - ast_log(LOG_WARNING, "Unable to lock monitor\n"); - return -1; - } - if (monitor_thread == pthread_self()) { - ast_mutex_unlock(&monlock); - ast_log(LOG_WARNING, "Cannot kill myself\n"); - return -1; - } - if (monitor_thread != AST_PTHREADT_NULL) { - if (ast_mutex_lock(&iflock)) { - ast_mutex_unlock(&monlock); - ast_log(LOG_WARNING, "Unable to lock the interface list\n"); - return -1; - } - monitor = 0; - while (pthread_kill(monitor_thread, SIGURG) == 0) - sched_yield(); - pthread_join(monitor_thread, NULL); - ast_mutex_unlock(&iflock); - } - monitor = 1; - /* Start a new monitor */ - if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) { - ast_mutex_unlock(&monlock); - ast_log(LOG_ERROR, "Unable to start monitor thread.\n"); - return -1; - } - ast_mutex_unlock(&monlock); - return 0; -} - -static struct phone_pvt *mkif(char *iface, int mode, int txgain, int rxgain) -{ - /* Make a phone_pvt structure for this interface */ - struct phone_pvt *tmp; - int flags; - - tmp = malloc(sizeof(struct phone_pvt)); - if (tmp) { - tmp->fd = open(iface, O_RDWR); - if (tmp->fd < 0) { - ast_log(LOG_WARNING, "Unable to open '%s'\n", iface); - free(tmp); - return NULL; - } - if (mode == MODE_FXO) { - if (ioctl(tmp->fd, IXJCTL_PORT, PORT_PSTN)) - ast_log(LOG_DEBUG, "Unable to set port to PSTN\n"); - } else { - if (ioctl(tmp->fd, IXJCTL_PORT, PORT_POTS)) - if (mode != MODE_FXS) - ast_log(LOG_DEBUG, "Unable to set port to POTS\n"); - } - ioctl(tmp->fd, PHONE_PLAY_STOP); - ioctl(tmp->fd, PHONE_REC_STOP); - ioctl(tmp->fd, PHONE_RING_STOP); - ioctl(tmp->fd, PHONE_CPT_STOP); - if (ioctl(tmp->fd, PHONE_PSTN_SET_STATE, PSTN_ON_HOOK)) - ast_log(LOG_DEBUG, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n",iface, strerror(errno)); - if (echocancel != AEC_OFF) - ioctl(tmp->fd, IXJCTL_AEC_START, echocancel); - if (silencesupression) - tmp->silencesupression = 1; -#ifdef PHONE_VAD - ioctl(tmp->fd, PHONE_VAD, tmp->silencesupression); -#endif - tmp->mode = mode; - flags = fcntl(tmp->fd, F_GETFL); - fcntl(tmp->fd, F_SETFL, flags | O_NONBLOCK); - tmp->owner = NULL; - tmp->lastformat = -1; - tmp->lastinput = -1; - tmp->ministate = 0; - memset(tmp->ext, 0, sizeof(tmp->ext)); - ast_copy_string(tmp->language, language, sizeof(tmp->language)); - ast_copy_string(tmp->dev, iface, sizeof(tmp->dev)); - ast_copy_string(tmp->context, context, sizeof(tmp->context)); - tmp->next = NULL; - tmp->obuflen = 0; - tmp->dialtone = 0; - tmp->cpt = 0; - ast_copy_string(tmp->cid_num, cid_num, sizeof(tmp->cid_num)); - ast_copy_string(tmp->cid_name, cid_name, sizeof(tmp->cid_name)); - tmp->txgain = txgain; - ioctl(tmp->fd, PHONE_PLAY_VOLUME, tmp->txgain); - tmp->rxgain = rxgain; - ioctl(tmp->fd, PHONE_REC_VOLUME, tmp->rxgain); - } - return tmp; -} - -static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause) -{ - int oldformat; - struct phone_pvt *p; - struct ast_channel *tmp = NULL; - char *name = data; - - /* Search for an unowned channel */ - if (ast_mutex_lock(&iflock)) { - ast_log(LOG_ERROR, "Unable to lock interface list???\n"); - return NULL; - } - p = iflist; - while(p) { - if (p->mode == MODE_FXS || - format & (AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW)) { - size_t length = strlen(p->dev + 5); - if (strncmp(name, p->dev + 5, length) == 0 && - !isalnum(name[length])) { - if (!p->owner) { - tmp = phone_new(p, AST_STATE_DOWN, p->context); - break; - } else - *cause = AST_CAUSE_BUSY; - } - } - p = p->next; - } - ast_mutex_unlock(&iflock); - restart_monitor(); - if (tmp == NULL) { - oldformat = format; - format &= (AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW); - if (!format) { - ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat); - return NULL; - } - } - return tmp; -} - -/* parse gain value from config file */ -static int parse_gain_value(char *gain_type, char *value) -{ - float gain; - - /* try to scan number */ - if (sscanf(value, "%f", &gain) != 1) - { - ast_log(LOG_ERROR, "Invalid %s value '%s' in '%s' config\n", - value, gain_type, config); - return DEFAULT_GAIN; - } - - /* multiplicate gain by 1.0 gain value */ - gain = gain * (float)DEFAULT_GAIN; - - /* percentage? */ - if (value[strlen(value) - 1] == '%') - return (int)(gain / (float)100); - - return (int)gain; -} - -static int __unload_module(void) -{ - struct phone_pvt *p, *pl; - /* First, take us out of the channel loop */ - if (cur_tech) - ast_channel_unregister(cur_tech); - if (!ast_mutex_lock(&iflock)) { - /* Hangup all interfaces if they have an owner */ - p = iflist; - while(p) { - if (p->owner) - ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD); - p = p->next; - } - iflist = NULL; - ast_mutex_unlock(&iflock); - } else { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - return -1; - } - if (!ast_mutex_lock(&monlock)) { - if (monitor_thread > AST_PTHREADT_NULL) { - monitor = 0; - while (pthread_kill(monitor_thread, SIGURG) == 0) - sched_yield(); - pthread_join(monitor_thread, NULL); - } - monitor_thread = AST_PTHREADT_STOP; - ast_mutex_unlock(&monlock); - } else { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - return -1; - } - - if (!ast_mutex_lock(&iflock)) { - /* Destroy all the interfaces and free their memory */ - p = iflist; - while(p) { - /* Close the socket, assuming it's real */ - if (p->fd > -1) - close(p->fd); - pl = p; - p = p->next; - /* Free associated memory */ - free(pl); - } - iflist = NULL; - ast_mutex_unlock(&iflock); - } else { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - return -1; - } - - return 0; -} - -static int unload_module(void) -{ - return __unload_module(); -} - -static int load_module(void) -{ - struct ast_config *cfg; - struct ast_variable *v; - struct phone_pvt *tmp; - int mode = MODE_IMMEDIATE; - int txgain = DEFAULT_GAIN, rxgain = DEFAULT_GAIN; /* default gain 1.0 */ - cfg = ast_config_load(config); - - /* We *must* have a config file otherwise stop immediately */ - if (!cfg) { - ast_log(LOG_ERROR, "Unable to load config %s\n", config); - return AST_MODULE_LOAD_DECLINE; - } - if (ast_mutex_lock(&iflock)) { - /* It's a little silly to lock it, but we mind as well just to be sure */ - ast_log(LOG_ERROR, "Unable to lock interface list???\n"); - return AST_MODULE_LOAD_FAILURE; - } - v = ast_variable_browse(cfg, "interfaces"); - while(v) { - /* Create the interface list */ - if (!strcasecmp(v->name, "device")) { - tmp = mkif(v->value, mode, txgain, rxgain); - if (tmp) { - tmp->next = iflist; - iflist = tmp; - - } else { - ast_log(LOG_ERROR, "Unable to register channel '%s'\n", v->value); - ast_config_destroy(cfg); - ast_mutex_unlock(&iflock); - __unload_module(); - return AST_MODULE_LOAD_FAILURE; - } - } else if (!strcasecmp(v->name, "silencesupression")) { - silencesupression = ast_true(v->value); - } else if (!strcasecmp(v->name, "language")) { - ast_copy_string(language, v->value, sizeof(language)); - } else if (!strcasecmp(v->name, "callerid")) { - ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num)); - } else if (!strcasecmp(v->name, "mode")) { - if (!strncasecmp(v->value, "di", 2)) - mode = MODE_DIALTONE; - else if (!strncasecmp(v->value, "sig", 3)) - mode = MODE_SIGMA; - else if (!strncasecmp(v->value, "im", 2)) - mode = MODE_IMMEDIATE; - else if (!strncasecmp(v->value, "fxs", 3)) { - mode = MODE_FXS; - prefformat = 0x01ff0000; /* All non-voice */ - } - else if (!strncasecmp(v->value, "fx", 2)) - mode = MODE_FXO; - else - ast_log(LOG_WARNING, "Unknown mode: %s\n", v->value); - } else if (!strcasecmp(v->name, "context")) { - ast_copy_string(context, v->value, sizeof(context)); - } else if (!strcasecmp(v->name, "format")) { - if (!strcasecmp(v->value, "g723.1")) { - prefformat = AST_FORMAT_G723_1; - } else if (!strcasecmp(v->value, "slinear")) { - if (mode == MODE_FXS) - prefformat |= AST_FORMAT_SLINEAR; - else prefformat = AST_FORMAT_SLINEAR; - } else if (!strcasecmp(v->value, "ulaw")) { - prefformat = AST_FORMAT_ULAW; - } else - ast_log(LOG_WARNING, "Unknown format '%s'\n", v->value); - } else if (!strcasecmp(v->name, "echocancel")) { - if (!strcasecmp(v->value, "off")) { - echocancel = AEC_OFF; - } else if (!strcasecmp(v->value, "low")) { - echocancel = AEC_LOW; - } else if (!strcasecmp(v->value, "medium")) { - echocancel = AEC_MED; - } else if (!strcasecmp(v->value, "high")) { - echocancel = AEC_HIGH; - } else - ast_log(LOG_WARNING, "Unknown echo cancellation '%s'\n", v->value); - } else if (!strcasecmp(v->name, "txgain")) { - txgain = parse_gain_value(v->name, v->value); - } else if (!strcasecmp(v->name, "rxgain")) { - rxgain = parse_gain_value(v->name, v->value); - } - v = v->next; - } - ast_mutex_unlock(&iflock); - - if (mode == MODE_FXS) { - phone_tech_fxs.capabilities = prefformat; - cur_tech = &phone_tech_fxs; - } else - cur_tech = (struct ast_channel_tech *) &phone_tech; - - /* Make sure we can register our Adtranphone channel type */ - - if (ast_channel_register(cur_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class 'Phone'\n"); - ast_config_destroy(cfg); - __unload_module(); - return AST_MODULE_LOAD_FAILURE; - } - ast_config_destroy(cfg); - /* And start the monitor for the first time */ - restart_monitor(); - return AST_MODULE_LOAD_SUCCESS; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Linux Telephony API Support"); diff --git a/1.4.23-rc4/channels/chan_sip.c b/1.4.23-rc4/channels/chan_sip.c deleted file mode 100644 index 004aaaabe..000000000 --- a/1.4.23-rc4/channels/chan_sip.c +++ /dev/null @@ -1,18893 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! - * \file - * \brief Implementation of Session Initiation Protocol - * - * \author Mark Spencer <markster@digium.com> - * - * See Also: - * \arg \ref AstCREDITS - * - * Implementation of RFC 3261 - without S/MIME, TCP and TLS support - * Configuration file \link Config_sip sip.conf \endlink - * - * - * \todo SIP over TCP - * \todo SIP over TLS - * \todo Better support of forking - * \todo VIA branch tag transaction checking - * \todo Transaction support - * - * \ingroup channel_drivers - * - * \par Overview of the handling of SIP sessions - * The SIP channel handles several types of SIP sessions, or dialogs, - * not all of them being "telephone calls". - * - Incoming calls that will be sent to the PBX core - * - Outgoing calls, generated by the PBX - * - SIP subscriptions and notifications of states and voicemail messages - * - SIP registrations, both inbound and outbound - * - SIP peer management (peerpoke, OPTIONS) - * - SIP text messages - * - * In the SIP channel, there's a list of active SIP dialogs, which includes - * all of these when they are active. "sip show channels" in the CLI will - * show most of these, excluding subscriptions which are shown by - * "sip show subscriptions" - * - * \par incoming packets - * Incoming packets are received in the monitoring thread, then handled by - * sipsock_read(). This function parses the packet and matches an existing - * dialog or starts a new SIP dialog. - * - * sipsock_read sends the packet to handle_request(), that parses a bit more. - * if it's a response to an outbound request, it's sent to handle_response(). - * If it is a request, handle_request sends it to one of a list of functions - * depending on the request type - INVITE, OPTIONS, REFER, BYE, CANCEL etc - * sipsock_read locks the ast_channel if it exists (an active call) and - * unlocks it after we have processed the SIP message. - * - * A new INVITE is sent to handle_request_invite(), that will end up - * starting a new channel in the PBX, the new channel after that executing - * in a separate channel thread. This is an incoming "call". - * When the call is answered, either by a bridged channel or the PBX itself - * the sip_answer() function is called. - * - * The actual media - Video or Audio - is mostly handled by the RTP subsystem - * in rtp.c - * - * \par Outbound calls - * Outbound calls are set up by the PBX through the sip_request_call() - * function. After that, they are activated by sip_call(). - * - * \par Hanging up - * The PBX issues a hangup on both incoming and outgoing calls through - * the sip_hangup() function - * - * \par Deprecated stuff - * This is deprecated and will be removed after the 1.4 release - * - the SIPUSERAGENT dialplan variable - * - the ALERT_INFO dialplan variable - */ - -/*** MODULEINFO - <depend>res_features</depend> - ***/ - - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include <unistd.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <net/if.h> -#include <errno.h> -#include <stdlib.h> -#include <fcntl.h> -#include <netdb.h> -#include <signal.h> -#include <sys/signal.h> -#include <netinet/in.h> -#include <netinet/in_systm.h> -#include <arpa/inet.h> -#include <netinet/ip.h> -#include <regex.h> - -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/sched.h" -#include "asterisk/io.h" -#include "asterisk/rtp.h" -#include "asterisk/udptl.h" -#include "asterisk/acl.h" -#include "asterisk/manager.h" -#include "asterisk/callerid.h" -#include "asterisk/cli.h" -#include "asterisk/app.h" -#include "asterisk/musiconhold.h" -#include "asterisk/dsp.h" -#include "asterisk/features.h" -#include "asterisk/srv.h" -#include "asterisk/astdb.h" -#include "asterisk/causes.h" -#include "asterisk/utils.h" -#include "asterisk/file.h" -#include "asterisk/astobj.h" -#include "asterisk/devicestate.h" -#include "asterisk/linkedlists.h" -#include "asterisk/stringfields.h" -#include "asterisk/monitor.h" -#include "asterisk/localtime.h" -#include "asterisk/abstract_jb.h" -#include "asterisk/compiler.h" -#include "asterisk/threadstorage.h" -#include "asterisk/translate.h" - -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef TRUE -#define TRUE 1 -#endif - -#define SIPBUFSIZE 512 - -#define XMIT_ERROR -2 - -#define VIDEO_CODEC_MASK 0x1fc0000 /*!< Video codecs from H.261 thru AST_FORMAT_MAX_VIDEO */ -#ifndef IPTOS_MINCOST -#define IPTOS_MINCOST 0x02 -#endif - -/* #define VOCAL_DATA_HACK */ - -#define DEFAULT_DEFAULT_EXPIRY 120 -#define DEFAULT_MIN_EXPIRY 60 -#define DEFAULT_MAX_EXPIRY 3600 -#define DEFAULT_REGISTRATION_TIMEOUT 20 -#define DEFAULT_MAX_FORWARDS "70" - -/* guard limit must be larger than guard secs */ -/* guard min must be < 1000, and should be >= 250 */ -#define EXPIRY_GUARD_SECS 15 /*!< How long before expiry do we reregister */ -#define EXPIRY_GUARD_LIMIT 30 /*!< Below here, we use EXPIRY_GUARD_PCT instead of - EXPIRY_GUARD_SECS */ -#define EXPIRY_GUARD_MIN 500 /*!< This is the minimum guard time applied. If - GUARD_PCT turns out to be lower than this, it - will use this time instead. - This is in milliseconds. */ -#define EXPIRY_GUARD_PCT 0.20 /*!< Percentage of expires timeout to use when - below EXPIRY_GUARD_LIMIT */ -#define DEFAULT_EXPIRY 900 /*!< Expire slowly */ - -static int min_expiry = DEFAULT_MIN_EXPIRY; /*!< Minimum accepted registration time */ -static int max_expiry = DEFAULT_MAX_EXPIRY; /*!< Maximum accepted registration time */ -static int default_expiry = DEFAULT_DEFAULT_EXPIRY; -static int expiry = DEFAULT_EXPIRY; - -#ifndef MAX -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#endif - -#define CALLERID_UNKNOWN "Unknown" - -#define DEFAULT_MAXMS 2000 /*!< Qualification: Must be faster than 2 seconds by default */ -#define DEFAULT_FREQ_OK 60 * 1000 /*!< Qualification: How often to check for the host to be up */ -#define DEFAULT_FREQ_NOTOK 10 * 1000 /*!< Qualification: How often to check, if the host is down... */ - -#define DEFAULT_RETRANS 1000 /*!< How frequently to retransmit Default: 2 * 500 ms in RFC 3261 */ -#define MAX_RETRANS 6 /*!< Try only 6 times for retransmissions, a total of 7 transmissions */ -#define SIP_TRANS_TIMEOUT 32000 /*!< SIP request timeout (rfc 3261) 64*T1 - \todo Use known T1 for timeout (peerpoke) - */ -#define DEFAULT_TRANS_TIMEOUT -1 /* Use default SIP transaction timeout */ -#define MAX_AUTHTRIES 3 /*!< Try authentication three times, then fail */ - -#define SIP_MAX_HEADERS 64 /*!< Max amount of SIP headers to read */ -#define SIP_MAX_LINES 64 /*!< Max amount of lines in SIP attachment (like SDP) */ -#define SIP_MAX_PACKET 4096 /*!< Also from RFC 3261 (2543), should sub headers tho */ - -#define SDP_MAX_RTPMAP_CODECS 32 /*!< Maximum number of codecs allowed in received SDP */ - -#define INITIAL_CSEQ 101 /*!< our initial sip sequence number */ - -/*! \brief Global jitterbuffer configuration - by default, jb is disabled */ -static struct ast_jb_conf default_jbconf = -{ - .flags = 0, - .max_size = -1, - .resync_threshold = -1, - .impl = "" -}; -static struct ast_jb_conf global_jbconf; - -static const char config[] = "sip.conf"; -static const char notify_config[] = "sip_notify.conf"; - -#define RTP 1 -#define NO_RTP 0 - -/*! \brief Authorization scheme for call transfers -\note Not a bitfield flag, since there are plans for other modes, - like "only allow transfers for authenticated devices" */ -enum transfermodes { - TRANSFER_OPENFORALL, /*!< Allow all SIP transfers */ - TRANSFER_CLOSED, /*!< Allow no SIP transfers */ -}; - - -enum sip_result { - AST_SUCCESS = 0, - AST_FAILURE = -1, -}; - -/*! \brief States for the INVITE transaction, not the dialog - \note this is for the INVITE that sets up the dialog -*/ -enum invitestates { - INV_NONE = 0, /*!< No state at all, maybe not an INVITE dialog */ - INV_CALLING = 1, /*!< Invite sent, no answer */ - INV_PROCEEDING = 2, /*!< We got/sent 1xx message */ - INV_EARLY_MEDIA = 3, /*!< We got/sent 18x message with to-tag back */ - INV_COMPLETED = 4, /*!< Got final response with error. Wait for ACK, then CONFIRMED */ - INV_CONFIRMED = 5, /*!< Confirmed response - we've got an ack (Incoming calls only) */ - INV_TERMINATED = 6, /*!< Transaction done - either successful (AST_STATE_UP) or failed, but done - The only way out of this is a BYE from one side */ - INV_CANCELLED = 7, /*!< Transaction cancelled by client or server in non-terminated state */ -}; - -/* Do _NOT_ make any changes to this enum, or the array following it; - if you think you are doing the right thing, you are probably - not doing the right thing. If you think there are changes - needed, get someone else to review them first _before_ - submitting a patch. If these two lists do not match properly - bad things will happen. -*/ - -enum xmittype { - XMIT_CRITICAL = 2, /*!< Transmit critical SIP message reliably, with re-transmits. - If it fails, it's critical and will cause a teardown of the session */ - XMIT_RELIABLE = 1, /*!< Transmit SIP message reliably, with re-transmits */ - XMIT_UNRELIABLE = 0, /*!< Transmit SIP message without bothering with re-transmits */ -}; - -enum parse_register_result { - PARSE_REGISTER_FAILED, - PARSE_REGISTER_UPDATE, - PARSE_REGISTER_QUERY, -}; - -enum subscriptiontype { - NONE = 0, - XPIDF_XML, - DIALOG_INFO_XML, - CPIM_PIDF_XML, - PIDF_XML, - MWI_NOTIFICATION -}; - -static const struct cfsubscription_types { - enum subscriptiontype type; - const char * const event; - const char * const mediatype; - const char * const text; -} subscription_types[] = { - { NONE, "-", "unknown", "unknown" }, - /* RFC 4235: SIP Dialog event package */ - { DIALOG_INFO_XML, "dialog", "application/dialog-info+xml", "dialog-info+xml" }, - { CPIM_PIDF_XML, "presence", "application/cpim-pidf+xml", "cpim-pidf+xml" }, /* RFC 3863 */ - { PIDF_XML, "presence", "application/pidf+xml", "pidf+xml" }, /* RFC 3863 */ - { XPIDF_XML, "presence", "application/xpidf+xml", "xpidf+xml" }, /* Pre-RFC 3863 with MS additions */ - { MWI_NOTIFICATION, "message-summary", "application/simple-message-summary", "mwi" } /* RFC 3842: Mailbox notification */ -}; - -/*! \brief SIP Request methods known by Asterisk */ -enum sipmethod { - SIP_UNKNOWN, /* Unknown response */ - SIP_RESPONSE, /* Not request, response to outbound request */ - SIP_REGISTER, - SIP_OPTIONS, - SIP_NOTIFY, - SIP_INVITE, - SIP_ACK, - SIP_PRACK, /* Not supported at all */ - SIP_BYE, - SIP_REFER, - SIP_SUBSCRIBE, - SIP_MESSAGE, - SIP_UPDATE, /* We can send UPDATE; but not accept it */ - SIP_INFO, - SIP_CANCEL, - SIP_PUBLISH, /* Not supported at all */ - SIP_PING, /* Not supported at all, no standard but still implemented out there */ -}; - -/*! \brief Authentication types - proxy or www authentication - \note Endpoints, like Asterisk, should always use WWW authentication to - allow multiple authentications in the same call - to the proxy and - to the end point. -*/ -enum sip_auth_type { - PROXY_AUTH, - WWW_AUTH, -}; - -/*! \brief Authentication result from check_auth* functions */ -enum check_auth_result { - AUTH_SUCCESSFUL = 0, - AUTH_CHALLENGE_SENT = 1, - AUTH_SECRET_FAILED = -1, - AUTH_USERNAME_MISMATCH = -2, - AUTH_NOT_FOUND = -3, - AUTH_FAKE_AUTH = -4, - AUTH_UNKNOWN_DOMAIN = -5, - AUTH_PEER_NOT_DYNAMIC = -6, - AUTH_ACL_FAILED = -7, -}; - -/*! \brief States for outbound registrations (with register= lines in sip.conf */ -enum sipregistrystate { - REG_STATE_UNREGISTERED = 0, /*!< We are not registred */ - REG_STATE_REGSENT, /*!< Registration request sent */ - REG_STATE_AUTHSENT, /*!< We have tried to authenticate */ - REG_STATE_REGISTERED, /*!< Registred and done */ - REG_STATE_REJECTED, /*!< Registration rejected */ - REG_STATE_TIMEOUT, /*!< Registration timed out */ - REG_STATE_NOAUTH, /*!< We have no accepted credentials */ - REG_STATE_FAILED, /*!< Registration failed after several tries */ -}; - -#define CAN_NOT_CREATE_DIALOG 0 -#define CAN_CREATE_DIALOG 1 -#define CAN_CREATE_DIALOG_UNSUPPORTED_METHOD 2 - -/*! XXX Note that sip_methods[i].id == i must hold or the code breaks */ -static const struct cfsip_methods { - enum sipmethod id; - int need_rtp; /*!< when this is the 'primary' use for a pvt structure, does it need RTP? */ - char * const text; - int can_create; -} sip_methods[] = { - { SIP_UNKNOWN, RTP, "-UNKNOWN-", CAN_CREATE_DIALOG }, - { SIP_RESPONSE, NO_RTP, "SIP/2.0", CAN_NOT_CREATE_DIALOG }, - { SIP_REGISTER, NO_RTP, "REGISTER", CAN_CREATE_DIALOG }, - { SIP_OPTIONS, NO_RTP, "OPTIONS", CAN_CREATE_DIALOG }, - { SIP_NOTIFY, NO_RTP, "NOTIFY", CAN_CREATE_DIALOG }, - { SIP_INVITE, RTP, "INVITE", CAN_CREATE_DIALOG }, - { SIP_ACK, NO_RTP, "ACK", CAN_NOT_CREATE_DIALOG }, - { SIP_PRACK, NO_RTP, "PRACK", CAN_NOT_CREATE_DIALOG }, - { SIP_BYE, NO_RTP, "BYE", CAN_NOT_CREATE_DIALOG }, - { SIP_REFER, NO_RTP, "REFER", CAN_CREATE_DIALOG }, - { SIP_SUBSCRIBE, NO_RTP, "SUBSCRIBE", CAN_CREATE_DIALOG }, - { SIP_MESSAGE, NO_RTP, "MESSAGE", CAN_CREATE_DIALOG }, - { SIP_UPDATE, NO_RTP, "UPDATE", CAN_NOT_CREATE_DIALOG }, - { SIP_INFO, NO_RTP, "INFO", CAN_NOT_CREATE_DIALOG }, - { SIP_CANCEL, NO_RTP, "CANCEL", CAN_NOT_CREATE_DIALOG }, - { SIP_PUBLISH, NO_RTP, "PUBLISH", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD }, - { SIP_PING, NO_RTP, "PING", CAN_CREATE_DIALOG_UNSUPPORTED_METHOD } -}; - -/*! Define SIP option tags, used in Require: and Supported: headers - We need to be aware of these properties in the phones to use - the replace: header. We should not do that without knowing - that the other end supports it... - This is nothing we can configure, we learn by the dialog - Supported: header on the REGISTER (peer) or the INVITE - (other devices) - We are not using many of these today, but will in the future. - This is documented in RFC 3261 -*/ -#define SUPPORTED 1 -#define NOT_SUPPORTED 0 - -#define SIP_OPT_REPLACES (1 << 0) -#define SIP_OPT_100REL (1 << 1) -#define SIP_OPT_TIMER (1 << 2) -#define SIP_OPT_EARLY_SESSION (1 << 3) -#define SIP_OPT_JOIN (1 << 4) -#define SIP_OPT_PATH (1 << 5) -#define SIP_OPT_PREF (1 << 6) -#define SIP_OPT_PRECONDITION (1 << 7) -#define SIP_OPT_PRIVACY (1 << 8) -#define SIP_OPT_SDP_ANAT (1 << 9) -#define SIP_OPT_SEC_AGREE (1 << 10) -#define SIP_OPT_EVENTLIST (1 << 11) -#define SIP_OPT_GRUU (1 << 12) -#define SIP_OPT_TARGET_DIALOG (1 << 13) -#define SIP_OPT_NOREFERSUB (1 << 14) -#define SIP_OPT_HISTINFO (1 << 15) -#define SIP_OPT_RESPRIORITY (1 << 16) - -/*! \brief List of well-known SIP options. If we get this in a require, - we should check the list and answer accordingly. */ -static const struct cfsip_options { - int id; /*!< Bitmap ID */ - int supported; /*!< Supported by Asterisk ? */ - char * const text; /*!< Text id, as in standard */ -} sip_options[] = { /* XXX used in 3 places */ - /* RFC3891: Replaces: header for transfer */ - { SIP_OPT_REPLACES, SUPPORTED, "replaces" }, - /* One version of Polycom firmware has the wrong label */ - { SIP_OPT_REPLACES, SUPPORTED, "replace" }, - /* RFC3262: PRACK 100% reliability */ - { SIP_OPT_100REL, NOT_SUPPORTED, "100rel" }, - /* RFC4028: SIP Session Timers */ - { SIP_OPT_TIMER, NOT_SUPPORTED, "timer" }, - /* RFC3959: SIP Early session support */ - { SIP_OPT_EARLY_SESSION, NOT_SUPPORTED, "early-session" }, - /* RFC3911: SIP Join header support */ - { SIP_OPT_JOIN, NOT_SUPPORTED, "join" }, - /* RFC3327: Path support */ - { SIP_OPT_PATH, NOT_SUPPORTED, "path" }, - /* RFC3840: Callee preferences */ - { SIP_OPT_PREF, NOT_SUPPORTED, "pref" }, - /* RFC3312: Precondition support */ - { SIP_OPT_PRECONDITION, NOT_SUPPORTED, "precondition" }, - /* RFC3323: Privacy with proxies*/ - { SIP_OPT_PRIVACY, NOT_SUPPORTED, "privacy" }, - /* RFC4092: Usage of the SDP ANAT Semantics in the SIP */ - { SIP_OPT_SDP_ANAT, NOT_SUPPORTED, "sdp-anat" }, - /* RFC3329: Security agreement mechanism */ - { SIP_OPT_SEC_AGREE, NOT_SUPPORTED, "sec_agree" }, - /* SIMPLE events: RFC4662 */ - { SIP_OPT_EVENTLIST, NOT_SUPPORTED, "eventlist" }, - /* GRUU: Globally Routable User Agent URI's */ - { SIP_OPT_GRUU, NOT_SUPPORTED, "gruu" }, - /* RFC4538: Target-dialog */ - { SIP_OPT_TARGET_DIALOG,NOT_SUPPORTED, "tdialog" }, - /* Disable the REFER subscription, RFC 4488 */ - { SIP_OPT_NOREFERSUB, NOT_SUPPORTED, "norefersub" }, - /* ietf-sip-history-info-06.txt */ - { SIP_OPT_HISTINFO, NOT_SUPPORTED, "histinfo" }, - /* ietf-sip-resource-priority-10.txt */ - { SIP_OPT_RESPRIORITY, NOT_SUPPORTED, "resource-priority" }, -}; - - -/*! \brief SIP Methods we support */ -#define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY" - -/*! \brief SIP Extensions we support */ -#define SUPPORTED_EXTENSIONS "replaces" - -/*! \brief Standard SIP port from RFC 3261. DO NOT CHANGE THIS */ -#define STANDARD_SIP_PORT 5060 -/* Note: in many SIP headers, absence of a port number implies port 5060, - * and this is why we cannot change the above constant. - * There is a limited number of places in asterisk where we could, - * in principle, use a different "default" port number, but - * we do not support this feature at the moment. - */ - -/* Default values, set and reset in reload_config before reading configuration */ -/* These are default values in the source. There are other recommended values in the - sip.conf.sample for new installations. These may differ to keep backwards compatibility, - yet encouraging new behaviour on new installations - */ -#define DEFAULT_CONTEXT "default" -#define DEFAULT_MOHINTERPRET "default" -#define DEFAULT_MOHSUGGEST "" -#define DEFAULT_VMEXTEN "asterisk" -#define DEFAULT_CALLERID "asterisk" -#define DEFAULT_NOTIFYMIME "application/simple-message-summary" -#define DEFAULT_MWITIME 10 -#define DEFAULT_ALLOWGUEST TRUE -#define DEFAULT_SRVLOOKUP TRUE /*!< Recommended setting is ON */ -#define DEFAULT_COMPACTHEADERS FALSE -#define DEFAULT_TOS_SIP 0 /*!< Call signalling packets should be marked as DSCP CS3, but the default is 0 to be compatible with previous versions. */ -#define DEFAULT_TOS_AUDIO 0 /*!< Audio packets should be marked as DSCP EF (Expedited Forwarding), but the default is 0 to be compatible with previous versions. */ -#define DEFAULT_TOS_VIDEO 0 /*!< Video packets should be marked as DSCP AF41, but the default is 0 to be compatible with previous versions. */ -#define DEFAULT_ALLOW_EXT_DOM TRUE -#define DEFAULT_REALM "asterisk" -#define DEFAULT_NOTIFYRINGING TRUE -#define DEFAULT_PEDANTIC FALSE -#define DEFAULT_AUTOCREATEPEER FALSE -#define DEFAULT_QUALIFY FALSE -#define DEFAULT_T1MIN 100 /*!< 100 MS for minimal roundtrip time */ -#define DEFAULT_MAX_CALL_BITRATE (384) /*!< Max bitrate for video */ -#ifndef DEFAULT_USERAGENT -#define DEFAULT_USERAGENT "Asterisk PBX" /*!< Default Useragent: header unless re-defined in sip.conf */ -#endif - - -/* Default setttings are used as a channel setting and as a default when - configuring devices */ -static char default_context[AST_MAX_CONTEXT]; -static char default_subscribecontext[AST_MAX_CONTEXT]; -static char default_language[MAX_LANGUAGE]; -static char default_callerid[AST_MAX_EXTENSION]; -static char default_fromdomain[AST_MAX_EXTENSION]; -static char default_notifymime[AST_MAX_EXTENSION]; -static int default_qualify; /*!< Default Qualify= setting */ -static char default_vmexten[AST_MAX_EXTENSION]; -static char default_mohinterpret[MAX_MUSICCLASS]; /*!< Global setting for moh class to use when put on hold */ -static char default_mohsuggest[MAX_MUSICCLASS]; /*!< Global setting for moh class to suggest when putting - * a bridged channel on hold */ -static int default_maxcallbitrate; /*!< Maximum bitrate for call */ -static struct ast_codec_pref default_prefs; /*!< Default codec prefs */ - -/* Global settings only apply to the channel */ -static int global_directrtpsetup; /*!< Enable support for Direct RTP setup (no re-invites) */ -static int global_limitonpeers; /*!< Match call limit on peers only */ -static int global_rtautoclear; -static int global_notifyringing; /*!< Send notifications on ringing */ -static int global_notifyhold; /*!< Send notifications on hold */ -static int global_alwaysauthreject; /*!< Send 401 Unauthorized for all failing requests */ -static int srvlookup; /*!< SRV Lookup on or off. Default is on */ -static int pedanticsipchecking; /*!< Extra checking ? Default off */ -static int autocreatepeer; /*!< Auto creation of peers at registration? Default off. */ -static int global_relaxdtmf; /*!< Relax DTMF */ -static int global_rtptimeout; /*!< Time out call if no RTP */ -static int global_rtpholdtimeout; -static int global_rtpkeepalive; /*!< Send RTP keepalives */ -static int global_reg_timeout; -static int global_regattempts_max; /*!< Registration attempts before giving up */ -static int global_allowguest; /*!< allow unauthenticated users/peers to connect? */ -static int global_allowsubscribe; /*!< Flag for disabling ALL subscriptions, this is FALSE only if all peers are FALSE - the global setting is in globals_flags[1] */ -static int global_mwitime; /*!< Time between MWI checks for peers */ -static unsigned int global_tos_sip; /*!< IP type of service for SIP packets */ -static unsigned int global_tos_audio; /*!< IP type of service for audio RTP packets */ -static unsigned int global_tos_video; /*!< IP type of service for video RTP packets */ -static int compactheaders; /*!< send compact sip headers */ -static int recordhistory; /*!< Record SIP history. Off by default */ -static int dumphistory; /*!< Dump history to verbose before destroying SIP dialog */ -static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */ -static char global_regcontext[AST_MAX_CONTEXT]; /*!< Context for auto-extensions */ -static char global_useragent[AST_MAX_EXTENSION]; /*!< Useragent for the SIP channel */ -static int allow_external_domains; /*!< Accept calls to external SIP domains? */ -static int global_callevents; /*!< Whether we send manager events or not */ -static int global_t1min; /*!< T1 roundtrip time minimum */ -static int global_autoframing; /*!< Turn autoframing on or off. */ -static enum transfermodes global_allowtransfer; /*!< SIP Refer restriction scheme */ - -static int global_matchexterniplocally; /*!< Match externip/externhost setting against localnet setting */ - -/*! \brief Codecs that we support by default: */ -static int global_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H263; - -/*! \brief Global list of addresses dynamic peers are not allowed to use */ -static struct ast_ha *global_contact_ha = NULL; -static int global_dynamic_exclude_static = 0; - -/* Object counters */ -static int suserobjs = 0; /*!< Static users */ -static int ruserobjs = 0; /*!< Realtime users */ -static int speerobjs = 0; /*!< Statis peers */ -static int rpeerobjs = 0; /*!< Realtime peers */ -static int apeerobjs = 0; /*!< Autocreated peer objects */ -static int regobjs = 0; /*!< Registry objects */ - -static struct ast_flags global_flags[2] = {{0}}; /*!< global SIP_ flags */ - -/*! \brief Protect the SIP dialog list (of sip_pvt's) */ -AST_MUTEX_DEFINE_STATIC(iflock); - -/*! \brief Protect the monitoring thread, so only one process can kill or start it, and not - when it's doing something critical. */ -AST_MUTEX_DEFINE_STATIC(netlock); - -AST_MUTEX_DEFINE_STATIC(monlock); - -AST_MUTEX_DEFINE_STATIC(sip_reload_lock); - -/*! \brief This is the thread for the monitor which checks for input on the channels - which are not currently in use. */ -static pthread_t monitor_thread = AST_PTHREADT_NULL; - -static int sip_reloading = FALSE; /*!< Flag for avoiding multiple reloads at the same time */ -static enum channelreloadreason sip_reloadreason; /*!< Reason for last reload/load of configuration */ - -static struct sched_context *sched; /*!< The scheduling context */ -static struct io_context *io; /*!< The IO context */ -static int *sipsock_read_id; /*!< ID of IO entry for sipsock FD */ - -#define DEC_CALL_LIMIT 0 -#define INC_CALL_LIMIT 1 -#define DEC_CALL_RINGING 2 -#define INC_CALL_RINGING 3 - -/*! \brief sip_request: The data grabbed from the UDP socket */ -struct sip_request { - char *rlPart1; /*!< SIP Method Name or "SIP/2.0" protocol version */ - char *rlPart2; /*!< The Request URI or Response Status */ - int len; /*!< Length */ - int headers; /*!< # of SIP Headers */ - int method; /*!< Method of this request */ - int lines; /*!< Body Content */ - unsigned int flags; /*!< SIP_PKT Flags for this packet */ - char *header[SIP_MAX_HEADERS]; - char *line[SIP_MAX_LINES]; - char data[SIP_MAX_PACKET]; - unsigned int sdp_start; /*!< the line number where the SDP begins */ - unsigned int sdp_end; /*!< the line number where the SDP ends */ - AST_LIST_ENTRY(sip_request) next; -}; - -/* - * A sip packet is stored into the data[] buffer, with the header followed - * by an empty line and the body of the message. - * On outgoing packets, data is accumulated in data[] with len reflecting - * the next available byte, headers and lines count the number of lines - * in both parts. There are no '\0' in data[0..len-1]. - * - * On received packet, the input read from the socket is copied into data[], - * len is set and the string is NUL-terminated. Then a parser fills up - * the other fields -header[] and line[] to point to the lines of the - * message, rlPart1 and rlPart2 parse the first lnie as below: - * - * Requests have in the first line METHOD URI SIP/2.0 - * rlPart1 = method; rlPart2 = uri; - * Responses have in the first line SIP/2.0 code description - * rlPart1 = SIP/2.0; rlPart2 = code + description; - * - */ - -/*! \brief structure used in transfers */ -struct sip_dual { - struct ast_channel *chan1; /*!< First channel involved */ - struct ast_channel *chan2; /*!< Second channel involved */ - struct sip_request req; /*!< Request that caused the transfer (REFER) */ - int seqno; /*!< Sequence number */ -}; - -struct sip_pkt; - -/*! \brief Parameters to the transmit_invite function */ -struct sip_invite_param { - const char *distinctive_ring; /*!< Distinctive ring header */ - int addsipheaders; /*!< Add extra SIP headers */ - const char *uri_options; /*!< URI options to add to the URI */ - const char *vxml_url; /*!< VXML url for Cisco phones */ - char *auth; /*!< Authentication */ - char *authheader; /*!< Auth header */ - enum sip_auth_type auth_type; /*!< Authentication type */ - const char *replaces; /*!< Replaces header for call transfers */ - int transfer; /*!< Flag - is this Invite part of a SIP transfer? (invite/replaces) */ -}; - -/*! \brief Structure to save routing information for a SIP session */ -struct sip_route { - struct sip_route *next; - char hop[0]; -}; - -/*! \brief Modes for SIP domain handling in the PBX */ -enum domain_mode { - SIP_DOMAIN_AUTO, /*!< This domain is auto-configured */ - SIP_DOMAIN_CONFIG, /*!< This domain is from configuration */ -}; - -/*! \brief Domain data structure. - \note In the future, we will connect this to a configuration tree specific - for this domain -*/ -struct domain { - char domain[MAXHOSTNAMELEN]; /*!< SIP domain we are responsible for */ - char context[AST_MAX_EXTENSION]; /*!< Incoming context for this domain */ - enum domain_mode mode; /*!< How did we find this domain? */ - AST_LIST_ENTRY(domain) list; /*!< List mechanics */ -}; - -static AST_LIST_HEAD_STATIC(domain_list, domain); /*!< The SIP domain list */ - - -/*! \brief sip_history: Structure for saving transactions within a SIP dialog */ -struct sip_history { - AST_LIST_ENTRY(sip_history) list; - char event[0]; /* actually more, depending on needs */ -}; - -AST_LIST_HEAD_NOLOCK(sip_history_head, sip_history); /*!< history list, entry in sip_pvt */ - -/*! \brief sip_auth: Credentials for authentication to other SIP services */ -struct sip_auth { - char realm[AST_MAX_EXTENSION]; /*!< Realm in which these credentials are valid */ - char username[256]; /*!< Username */ - char secret[256]; /*!< Secret */ - char md5secret[256]; /*!< MD5Secret */ - struct sip_auth *next; /*!< Next auth structure in list */ -}; - -/*--- Various flags for the flags field in the pvt structure */ -#define SIP_ALREADYGONE (1 << 0) /*!< Whether or not we've already been destroyed by our peer */ -#define SIP_NEEDDESTROY (1 << 1) /*!< if we need to be destroyed by the monitor thread */ -#define SIP_NOVIDEO (1 << 2) /*!< Didn't get video in invite, don't offer */ -#define SIP_RINGING (1 << 3) /*!< Have sent 180 ringing */ -#define SIP_PROGRESS_SENT (1 << 4) /*!< Have sent 183 message progress */ -#define SIP_NEEDREINVITE (1 << 5) /*!< Do we need to send another reinvite? */ -#define SIP_PENDINGBYE (1 << 6) /*!< Need to send bye after we ack? */ -#define SIP_GOTREFER (1 << 7) /*!< Got a refer? */ -#define SIP_PROMISCREDIR (1 << 8) /*!< Promiscuous redirection */ -#define SIP_TRUSTRPID (1 << 9) /*!< Trust RPID headers? */ -#define SIP_USEREQPHONE (1 << 10) /*!< Add user=phone to numeric URI. Default off */ -#define SIP_REALTIME (1 << 11) /*!< Flag for realtime users */ -#define SIP_USECLIENTCODE (1 << 12) /*!< Trust X-ClientCode info message */ -#define SIP_OUTGOING (1 << 13) /*!< Direction of the last transaction in this dialog */ -#define SIP_FREE_BIT (1 << 14) /*!< ---- */ -#define SIP_DEFER_BYE_ON_TRANSFER (1 << 15) /*!< Do not hangup at first ast_hangup */ -#define SIP_DTMF (3 << 16) /*!< DTMF Support: four settings, uses two bits */ -#define SIP_DTMF_RFC2833 (0 << 16) /*!< DTMF Support: RTP DTMF - "rfc2833" */ -#define SIP_DTMF_INBAND (1 << 16) /*!< DTMF Support: Inband audio, only for ULAW/ALAW - "inband" */ -#define SIP_DTMF_INFO (2 << 16) /*!< DTMF Support: SIP Info messages - "info" */ -#define SIP_DTMF_AUTO (3 << 16) /*!< DTMF Support: AUTO switch between rfc2833 and in-band DTMF */ -/* NAT settings */ -#define SIP_NAT (3 << 18) /*!< four settings, uses two bits */ -#define SIP_NAT_NEVER (0 << 18) /*!< No nat support */ -#define SIP_NAT_RFC3581 (1 << 18) /*!< NAT RFC3581 */ -#define SIP_NAT_ROUTE (2 << 18) /*!< NAT Only ROUTE */ -#define SIP_NAT_ALWAYS (3 << 18) /*!< NAT Both ROUTE and RFC3581 */ -/* re-INVITE related settings */ -#define SIP_REINVITE (7 << 20) /*!< three bits used */ -#define SIP_CAN_REINVITE (1 << 20) /*!< allow peers to be reinvited to send media directly p2p */ -#define SIP_CAN_REINVITE_NAT (2 << 20) /*!< allow media reinvite when new peer is behind NAT */ -#define SIP_REINVITE_UPDATE (4 << 20) /*!< use UPDATE (RFC3311) when reinviting this peer */ -/* "insecure" settings */ -#define SIP_INSECURE_PORT (1 << 23) /*!< don't require matching port for incoming requests */ -#define SIP_INSECURE_INVITE (1 << 24) /*!< don't require authentication for incoming INVITEs */ -/* Sending PROGRESS in-band settings */ -#define SIP_PROG_INBAND (3 << 25) /*!< three settings, uses two bits */ -#define SIP_PROG_INBAND_NEVER (0 << 25) -#define SIP_PROG_INBAND_NO (1 << 25) -#define SIP_PROG_INBAND_YES (2 << 25) -#define SIP_NO_HISTORY (1 << 27) /*!< Suppress recording request/response history */ -#define SIP_CALL_LIMIT (1 << 28) /*!< Call limit enforced for this call */ -#define SIP_SENDRPID (1 << 29) /*!< Remote Party-ID Support */ -#define SIP_INC_COUNT (1 << 30) /*!< Did this connection increment the counter of in-use calls? */ -#define SIP_G726_NONSTANDARD (1 << 31) /*!< Use non-standard packing for G726-32 data */ - -#define SIP_FLAGS_TO_COPY \ - (SIP_PROMISCREDIR | SIP_TRUSTRPID | SIP_SENDRPID | SIP_DTMF | SIP_REINVITE | \ - SIP_PROG_INBAND | SIP_USECLIENTCODE | SIP_NAT | SIP_G726_NONSTANDARD | \ - SIP_USEREQPHONE | SIP_INSECURE_PORT | SIP_INSECURE_INVITE) - -/*--- a new page of flags (for flags[1] */ -/* realtime flags */ -#define SIP_PAGE2_RTCACHEFRIENDS (1 << 0) -#define SIP_PAGE2_RTUPDATE (1 << 1) -#define SIP_PAGE2_RTAUTOCLEAR (1 << 2) -#define SIP_PAGE2_RT_FROMCONTACT (1 << 4) -#define SIP_PAGE2_RTSAVE_SYSNAME (1 << 5) -/* Space for addition of other realtime flags in the future */ -#define SIP_PAGE2_STATECHANGEQUEUE (1 << 9) /*!< D: Unsent state pending change exists */ -#define SIP_PAGE2_IGNOREREGEXPIRE (1 << 10) -#define SIP_PAGE2_DEBUG (3 << 11) -#define SIP_PAGE2_DEBUG_CONFIG (1 << 11) -#define SIP_PAGE2_DEBUG_CONSOLE (1 << 12) -#define SIP_PAGE2_DYNAMIC (1 << 13) /*!< Dynamic Peers register with Asterisk */ -#define SIP_PAGE2_SELFDESTRUCT (1 << 14) /*!< Automatic peers need to destruct themselves */ -#define SIP_PAGE2_VIDEOSUPPORT (1 << 15) -#define SIP_PAGE2_ALLOWSUBSCRIBE (1 << 16) /*!< Allow subscriptions from this peer? */ -#define SIP_PAGE2_ALLOWOVERLAP (1 << 17) /*!< Allow overlap dialing ? */ -#define SIP_PAGE2_SUBSCRIBEMWIONLY (1 << 18) /*!< Only issue MWI notification if subscribed to */ -#define SIP_PAGE2_INC_RINGING (1 << 19) /*!< Did this connection increment the counter of in-use calls? */ -#define SIP_PAGE2_T38SUPPORT (7 << 20) /*!< T38 Fax Passthrough Support */ -#define SIP_PAGE2_T38SUPPORT_UDPTL (1 << 20) /*!< 20: T38 Fax Passthrough Support */ -#define SIP_PAGE2_T38SUPPORT_RTP (2 << 20) /*!< 21: T38 Fax Passthrough Support (not implemented) */ -#define SIP_PAGE2_T38SUPPORT_TCP (4 << 20) /*!< 22: T38 Fax Passthrough Support (not implemented) */ -#define SIP_PAGE2_CALL_ONHOLD (3 << 23) /*!< Call states */ -#define SIP_PAGE2_CALL_ONHOLD_ACTIVE (1 << 23) /*!< 23: Active hold */ -#define SIP_PAGE2_CALL_ONHOLD_ONEDIR (2 << 23) /*!< 23: One directional hold */ -#define SIP_PAGE2_CALL_ONHOLD_INACTIVE (3 << 23) /*!< 23: Inactive hold */ -#define SIP_PAGE2_RFC2833_COMPENSATE (1 << 25) /*!< 25: ???? */ -#define SIP_PAGE2_BUGGY_MWI (1 << 26) /*!< 26: Buggy CISCO MWI fix */ -#define SIP_PAGE2_OUTGOING_CALL (1 << 27) /*!< 27: Is this an outgoing call? */ -#define SIP_PAGE2_UDPTL_DESTINATION (1 << 28) /*!< 28: Use source IP of RTP as destination if NAT is enabled */ -#define SIP_PAGE2_DIALOG_ESTABLISHED (1 << 29) /*!< 29: Has a dialog been established? */ - -#define SIP_PAGE2_FLAGS_TO_COPY \ - (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_VIDEOSUPPORT | \ - SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | SIP_PAGE2_BUGGY_MWI | SIP_PAGE2_UDPTL_DESTINATION) - -/* SIP packet flags */ -#define SIP_PKT_DEBUG (1 << 0) /*!< Debug this packet */ -#define SIP_PKT_WITH_TOTAG (1 << 1) /*!< This packet has a to-tag */ -#define SIP_PKT_IGNORE (1 << 2) /*!< This is a re-transmit, ignore it */ -#define SIP_PKT_IGNORE_RESP (1 << 3) /*!< Resp ignore - ??? */ -#define SIP_PKT_IGNORE_REQ (1 << 4) /*!< Req ignore - ??? */ - -/* T.38 set of flags */ -#define T38FAX_FILL_BIT_REMOVAL (1 << 0) /*!< Default: 0 (unset)*/ -#define T38FAX_TRANSCODING_MMR (1 << 1) /*!< Default: 0 (unset)*/ -#define T38FAX_TRANSCODING_JBIG (1 << 2) /*!< Default: 0 (unset)*/ -/* Rate management */ -#define T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF (0 << 3) -#define T38FAX_RATE_MANAGEMENT_LOCAL_TCF (1 << 3) /*!< Unset for transferredTCF (UDPTL), set for localTCF (TPKT) */ -/* UDP Error correction */ -#define T38FAX_UDP_EC_NONE (0 << 4) /*!< two bits, if unset NO t38UDPEC field in T38 SDP*/ -#define T38FAX_UDP_EC_FEC (1 << 4) /*!< Set for t38UDPFEC */ -#define T38FAX_UDP_EC_REDUNDANCY (2 << 4) /*!< Set for t38UDPRedundancy */ -/* T38 Spec version */ -#define T38FAX_VERSION (3 << 6) /*!< two bits, 2 values so far, up to 4 values max */ -#define T38FAX_VERSION_0 (0 << 6) /*!< Version 0 */ -#define T38FAX_VERSION_1 (1 << 6) /*!< Version 1 */ -/* Maximum Fax Rate */ -#define T38FAX_RATE_2400 (1 << 8) /*!< 2400 bps t38FaxRate */ -#define T38FAX_RATE_4800 (1 << 9) /*!< 4800 bps t38FaxRate */ -#define T38FAX_RATE_7200 (1 << 10) /*!< 7200 bps t38FaxRate */ -#define T38FAX_RATE_9600 (1 << 11) /*!< 9600 bps t38FaxRate */ -#define T38FAX_RATE_12000 (1 << 12) /*!< 12000 bps t38FaxRate */ -#define T38FAX_RATE_14400 (1 << 13) /*!< 14400 bps t38FaxRate */ - -/*!< This is default: NO MMR and JBIG trancoding, NO fill bit removal, transferredTCF TCF, UDP FEC, Version 0 and 9600 max fax rate */ -static int global_t38_capability = T38FAX_VERSION_0 | T38FAX_RATE_2400 | T38FAX_RATE_4800 | T38FAX_RATE_7200 | T38FAX_RATE_9600; - -#define sipdebug ast_test_flag(&global_flags[1], SIP_PAGE2_DEBUG) -#define sipdebug_config ast_test_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONFIG) -#define sipdebug_console ast_test_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONSOLE) - -/*! \brief T38 States for a call */ -enum t38state { - T38_DISABLED = 0, /*!< Not enabled */ - T38_LOCAL_DIRECT, /*!< Offered from local */ - T38_LOCAL_REINVITE, /*!< Offered from local - REINVITE */ - T38_PEER_DIRECT, /*!< Offered from peer */ - T38_PEER_REINVITE, /*!< Offered from peer - REINVITE */ - T38_ENABLED /*!< Negotiated (enabled) */ -}; - -/*! \brief T.38 channel settings (at some point we need to make this alloc'ed */ -struct t38properties { - struct ast_flags t38support; /*!< Flag for udptl, rtp or tcp support for this session */ - int capability; /*!< Our T38 capability */ - int peercapability; /*!< Peers T38 capability */ - int jointcapability; /*!< Supported T38 capability at both ends */ - enum t38state state; /*!< T.38 state */ -}; - -/*! \brief Parameters to know status of transfer */ -enum referstatus { - REFER_IDLE, /*!< No REFER is in progress */ - REFER_SENT, /*!< Sent REFER to transferee */ - REFER_RECEIVED, /*!< Received REFER from transferer */ - REFER_CONFIRMED, /*!< Refer confirmed with a 100 TRYING */ - REFER_ACCEPTED, /*!< Accepted by transferee */ - REFER_RINGING, /*!< Target Ringing */ - REFER_200OK, /*!< Answered by transfer target */ - REFER_FAILED, /*!< REFER declined - go on */ - REFER_NOAUTH /*!< We had no auth for REFER */ -}; - -static const struct c_referstatusstring { - enum referstatus status; - char *text; -} referstatusstrings[] = { - { REFER_IDLE, "<none>" }, - { REFER_SENT, "Request sent" }, - { REFER_RECEIVED, "Request received" }, - { REFER_ACCEPTED, "Accepted" }, - { REFER_RINGING, "Target ringing" }, - { REFER_200OK, "Done" }, - { REFER_FAILED, "Failed" }, - { REFER_NOAUTH, "Failed - auth failure" } -} ; - -/*! \brief Structure to handle SIP transfers. Dynamically allocated when needed */ -/* OEJ: Should be moved to string fields */ -struct sip_refer { - char refer_to[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO extension */ - char refer_to_domain[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO domain */ - char refer_to_urioption[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO uri options */ - char refer_to_context[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO context */ - char referred_by[AST_MAX_EXTENSION]; /*!< Place to store REFERRED-BY extension */ - char referred_by_name[AST_MAX_EXTENSION]; /*!< Place to store REFERRED-BY extension */ - char refer_contact[AST_MAX_EXTENSION]; /*!< Place to store Contact info from a REFER extension */ - char replaces_callid[SIPBUFSIZE]; /*!< Replace info: callid */ - char replaces_callid_totag[SIPBUFSIZE/2]; /*!< Replace info: to-tag */ - char replaces_callid_fromtag[SIPBUFSIZE/2]; /*!< Replace info: from-tag */ - struct sip_pvt *refer_call; /*!< Call we are referring */ - int attendedtransfer; /*!< Attended or blind transfer? */ - int localtransfer; /*!< Transfer to local domain? */ - enum referstatus status; /*!< REFER status */ -}; - -/*! \brief sip_pvt: PVT structures are used for each SIP dialog, ie. a call, a registration, a subscribe */ -static struct sip_pvt { - ast_mutex_t lock; /*!< Dialog private lock */ - int method; /*!< SIP method that opened this dialog */ - enum invitestates invitestate; /*!< The state of the INVITE transaction only */ - AST_DECLARE_STRING_FIELDS( - AST_STRING_FIELD(callid); /*!< Global CallID */ - AST_STRING_FIELD(randdata); /*!< Random data */ - AST_STRING_FIELD(accountcode); /*!< Account code */ - AST_STRING_FIELD(realm); /*!< Authorization realm */ - AST_STRING_FIELD(nonce); /*!< Authorization nonce */ - AST_STRING_FIELD(opaque); /*!< Opaque nonsense */ - AST_STRING_FIELD(qop); /*!< Quality of Protection, since SIP wasn't complicated enough yet. */ - AST_STRING_FIELD(domain); /*!< Authorization domain */ - AST_STRING_FIELD(from); /*!< The From: header */ - AST_STRING_FIELD(useragent); /*!< User agent in SIP request */ - AST_STRING_FIELD(exten); /*!< Extension where to start */ - AST_STRING_FIELD(context); /*!< Context for this call */ - AST_STRING_FIELD(subscribecontext); /*!< Subscribecontext */ - AST_STRING_FIELD(subscribeuri); /*!< Subscribecontext */ - AST_STRING_FIELD(fromdomain); /*!< Domain to show in the from field */ - AST_STRING_FIELD(fromuser); /*!< User to show in the user field */ - AST_STRING_FIELD(fromname); /*!< Name to show in the user field */ - AST_STRING_FIELD(tohost); /*!< Host we should put in the "to" field */ - AST_STRING_FIELD(language); /*!< Default language for this call */ - AST_STRING_FIELD(mohinterpret); /*!< MOH class to use when put on hold */ - AST_STRING_FIELD(mohsuggest); /*!< MOH class to suggest when putting a peer on hold */ - AST_STRING_FIELD(rdnis); /*!< Referring DNIS */ - AST_STRING_FIELD(theirtag); /*!< Their tag */ - AST_STRING_FIELD(username); /*!< [user] name */ - AST_STRING_FIELD(peername); /*!< [peer] name, not set if [user] */ - AST_STRING_FIELD(authname); /*!< Who we use for authentication */ - AST_STRING_FIELD(uri); /*!< Original requested URI */ - AST_STRING_FIELD(okcontacturi); /*!< URI from the 200 OK on INVITE */ - AST_STRING_FIELD(peersecret); /*!< Password */ - AST_STRING_FIELD(peermd5secret); - AST_STRING_FIELD(cid_num); /*!< Caller*ID number */ - AST_STRING_FIELD(cid_name); /*!< Caller*ID name */ - AST_STRING_FIELD(via); /*!< Via: header */ - AST_STRING_FIELD(fullcontact); /*!< The Contact: that the UA registers with us */ - AST_STRING_FIELD(our_contact); /*!< Our contact header */ - AST_STRING_FIELD(rpid); /*!< Our RPID header */ - AST_STRING_FIELD(rpid_from); /*!< Our RPID From header */ - ); - unsigned int ocseq; /*!< Current outgoing seqno */ - unsigned int icseq; /*!< Current incoming seqno */ - ast_group_t callgroup; /*!< Call group */ - ast_group_t pickupgroup; /*!< Pickup group */ - int lastinvite; /*!< Last Cseq of invite */ - int lastnoninvite; /*!< Last Cseq of non-invite */ - struct ast_flags flags[2]; /*!< SIP_ flags */ - int timer_t1; /*!< SIP timer T1, ms rtt */ - unsigned int sipoptions; /*!< Supported SIP options on the other end */ - struct ast_codec_pref prefs; /*!< codec prefs */ - int capability; /*!< Special capability (codec) */ - int jointcapability; /*!< Supported capability at both ends (codecs) */ - int peercapability; /*!< Supported peer capability */ - int prefcodec; /*!< Preferred codec (outbound only) */ - int noncodeccapability; /*!< DTMF RFC2833 telephony-event */ - int jointnoncodeccapability; /*!< Joint Non codec capability */ - int redircodecs; /*!< Redirect codecs */ - int maxcallbitrate; /*!< Maximum Call Bitrate for Video Calls */ - struct t38properties t38; /*!< T38 settings */ - struct sockaddr_in udptlredirip; /*!< Where our T.38 UDPTL should be going if not to us */ - struct ast_udptl *udptl; /*!< T.38 UDPTL session */ - int callingpres; /*!< Calling presentation */ - int authtries; /*!< Times we've tried to authenticate */ - int expiry; /*!< How long we take to expire */ - long branch; /*!< The branch identifier of this session */ - long invite_branch; /*!< The branch used when we sent the initial INVITE */ - char tag[11]; /*!< Our tag for this session */ - int sessionid; /*!< SDP Session ID */ - int sessionversion; /*!< SDP Session Version */ - struct sockaddr_in sa; /*!< Our peer */ - struct sockaddr_in redirip; /*!< Where our RTP should be going if not to us */ - struct sockaddr_in vredirip; /*!< Where our Video RTP should be going if not to us */ - time_t lastrtprx; /*!< Last RTP received */ - time_t lastrtptx; /*!< Last RTP sent */ - int rtptimeout; /*!< RTP timeout time */ - struct sockaddr_in recv; /*!< Received as */ - struct in_addr ourip; /*!< Our IP */ - struct ast_channel *owner; /*!< Who owns us (if we have an owner) */ - struct sip_route *route; /*!< Head of linked list of routing steps (fm Record-Route) */ - int route_persistant; /*!< Is this the "real" route? */ - struct sip_auth *peerauth; /*!< Realm authentication */ - int noncecount; /*!< Nonce-count */ - char lastmsg[256]; /*!< Last Message sent/received */ - int amaflags; /*!< AMA Flags */ - int pendinginvite; /*!< Any pending INVITE or state NOTIFY (in subscribe pvt's) ? (seqno of this) */ - struct sip_request initreq; /*!< Request that opened the latest transaction - within this SIP dialog */ - - int maxtime; /*!< Max time for first response */ - int initid; /*!< Auto-congest ID if appropriate (scheduler) */ - int waitid; /*!< Wait ID for scheduler after 491 or other delays */ - int autokillid; /*!< Auto-kill ID (scheduler) */ - enum transfermodes allowtransfer; /*!< REFER: restriction scheme */ - struct sip_refer *refer; /*!< REFER: SIP transfer data structure */ - enum subscriptiontype subscribed; /*!< SUBSCRIBE: Is this dialog a subscription? */ - int stateid; /*!< SUBSCRIBE: ID for devicestate subscriptions */ - int laststate; /*!< SUBSCRIBE: Last known extension state */ - int dialogver; /*!< SUBSCRIBE: Version for subscription dialog-info */ - - struct ast_dsp *vad; /*!< Voice Activation Detection dsp */ - - struct sip_peer *relatedpeer; /*!< If this dialog is related to a peer, which one - Used in peerpoke, mwi subscriptions */ - struct sip_registry *registry; /*!< If this is a REGISTER dialog, to which registry */ - struct ast_rtp *rtp; /*!< RTP Session */ - struct ast_rtp *vrtp; /*!< Video RTP session */ - struct sip_pkt *packets; /*!< Packets scheduled for re-transmission */ - struct sip_history_head *history; /*!< History of this SIP dialog */ - size_t history_entries; /*!< Number of entires in the history */ - struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */ - AST_LIST_HEAD_NOLOCK(request_queue, sip_request) request_queue; /*!< Requests that arrived but could not be processed immediately */ - int request_queue_sched_id; /*!< Scheduler ID of any scheduled action to process queued requests */ - struct sip_pvt *next; /*!< Next dialog in chain */ - struct sip_invite_param *options; /*!< Options for INVITE */ - int autoframing; -} *iflist = NULL; - -/*! Max entires in the history list for a sip_pvt */ -#define MAX_HISTORY_ENTRIES 50 - -#define FLAG_RESPONSE (1 << 0) -#define FLAG_FATAL (1 << 1) - -/*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission */ -struct sip_pkt { - struct sip_pkt *next; /*!< Next packet in linked list */ - int retrans; /*!< Retransmission number */ - int method; /*!< SIP method for this packet */ - int seqno; /*!< Sequence number */ - unsigned int flags; /*!< non-zero if this is a response packet (e.g. 200 OK) */ - struct sip_pvt *owner; /*!< Owner AST call */ - int retransid; /*!< Retransmission ID */ - int timer_a; /*!< SIP timer A, retransmission timer */ - int timer_t1; /*!< SIP Timer T1, estimated RTT or 500 ms */ - int packetlen; /*!< Length of packet */ - char data[0]; -}; - -/*! \brief Structure for SIP user data. User's place calls to us */ -struct sip_user { - /* Users who can access various contexts */ - ASTOBJ_COMPONENTS(struct sip_user); - char secret[80]; /*!< Password */ - char md5secret[80]; /*!< Password in md5 */ - char context[AST_MAX_CONTEXT]; /*!< Default context for incoming calls */ - char subscribecontext[AST_MAX_CONTEXT]; /* Default context for subscriptions */ - char cid_num[80]; /*!< Caller ID num */ - char cid_name[80]; /*!< Caller ID name */ - char accountcode[AST_MAX_ACCOUNT_CODE]; /* Account code */ - char language[MAX_LANGUAGE]; /*!< Default language for this user */ - char mohinterpret[MAX_MUSICCLASS];/*!< Music on Hold class */ - char mohsuggest[MAX_MUSICCLASS];/*!< Music on Hold class */ - char useragent[256]; /*!< User agent in SIP request */ - struct ast_codec_pref prefs; /*!< codec prefs */ - ast_group_t callgroup; /*!< Call group */ - ast_group_t pickupgroup; /*!< Pickup Group */ - unsigned int sipoptions; /*!< Supported SIP options */ - struct ast_flags flags[2]; /*!< SIP_ flags */ - int amaflags; /*!< AMA flags for billing */ - int callingpres; /*!< Calling id presentation */ - int capability; /*!< Codec capability */ - int inUse; /*!< Number of calls in use */ - int call_limit; /*!< Limit of concurrent calls */ - enum transfermodes allowtransfer; /*! SIP Refer restriction scheme */ - struct ast_ha *ha; /*!< ACL setting */ - struct ast_variable *chanvars; /*!< Variables to set for channel created by user */ - int maxcallbitrate; /*!< Maximum Bitrate for a video call */ - int autoframing; -}; - -/*! \brief Structure for SIP peer data, we place calls to peers if registered or fixed IP address (host) */ -/* XXX field 'name' must be first otherwise sip_addrcmp() will fail */ -struct sip_peer { - ASTOBJ_COMPONENTS(struct sip_peer); /*!< name, refcount, objflags, object pointers */ - /*!< peer->name is the unique name of this object */ - char secret[80]; /*!< Password */ - char md5secret[80]; /*!< Password in MD5 */ - struct sip_auth *auth; /*!< Realm authentication list */ - char context[AST_MAX_CONTEXT]; /*!< Default context for incoming calls */ - char subscribecontext[AST_MAX_CONTEXT]; /*!< Default context for subscriptions */ - char username[80]; /*!< Temporary username until registration */ - char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */ - int amaflags; /*!< AMA Flags (for billing) */ - char tohost[MAXHOSTNAMELEN]; /*!< If not dynamic, IP address */ - char regexten[AST_MAX_EXTENSION]; /*!< Extension to register (if regcontext is used) */ - char fromuser[80]; /*!< From: user when calling this peer */ - char fromdomain[MAXHOSTNAMELEN]; /*!< From: domain when calling this peer */ - char fullcontact[256]; /*!< Contact registered with us (not in sip.conf) */ - char cid_num[80]; /*!< Caller ID num */ - char cid_name[80]; /*!< Caller ID name */ - int callingpres; /*!< Calling id presentation */ - int inUse; /*!< Number of calls in use */ - int inRinging; /*!< Number of calls ringing */ - int onHold; /*!< Peer has someone on hold */ - int call_limit; /*!< Limit of concurrent calls */ - enum transfermodes allowtransfer; /*! SIP Refer restriction scheme */ - char vmexten[AST_MAX_EXTENSION]; /*!< Dialplan extension for MWI notify message*/ - char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox setting for MWI checks */ - char language[MAX_LANGUAGE]; /*!< Default language for prompts */ - char mohinterpret[MAX_MUSICCLASS];/*!< Music on Hold class */ - char mohsuggest[MAX_MUSICCLASS];/*!< Music on Hold class */ - char useragent[256]; /*!< User agent in SIP request (saved from registration) */ - struct ast_codec_pref prefs; /*!< codec prefs */ - int lastmsgssent; - time_t lastmsgcheck; /*!< Last time we checked for MWI */ - unsigned int sipoptions; /*!< Supported SIP options */ - struct ast_flags flags[2]; /*!< SIP_ flags */ - int expire; /*!< When to expire this peer registration */ - int capability; /*!< Codec capability */ - int rtptimeout; /*!< RTP timeout */ - int rtpholdtimeout; /*!< RTP Hold Timeout */ - int rtpkeepalive; /*!< Send RTP packets for keepalive */ - ast_group_t callgroup; /*!< Call group */ - ast_group_t pickupgroup; /*!< Pickup group */ - struct sockaddr_in addr; /*!< IP address of peer */ - int maxcallbitrate; /*!< Maximum Bitrate for a video call */ - - /* Qualification */ - struct sip_pvt *call; /*!< Call pointer */ - int pokeexpire; /*!< When to expire poke (qualify= checking) */ - int lastms; /*!< How long last response took (in ms), or -1 for no response */ - int maxms; /*!< Max ms we will accept for the host to be up, 0 to not monitor */ - struct timeval ps; /*!< Ping send time */ - - struct sockaddr_in defaddr; /*!< Default IP address, used until registration */ - struct ast_ha *ha; /*!< Access control list */ - struct ast_ha *contactha; /*!< Restrict what IPs are allowed in the Contact header (for registration) */ - struct ast_variable *chanvars; /*!< Variables to set for channel created by user */ - struct sip_pvt *mwipvt; /*!< Subscription for MWI */ - int lastmsg; - int autoframing; -}; - - - -/*! \brief Registrations with other SIP proxies */ -struct sip_registry { - ASTOBJ_COMPONENTS_FULL(struct sip_registry,1,1); - AST_DECLARE_STRING_FIELDS( - AST_STRING_FIELD(callid); /*!< Global Call-ID */ - AST_STRING_FIELD(realm); /*!< Authorization realm */ - AST_STRING_FIELD(nonce); /*!< Authorization nonce */ - AST_STRING_FIELD(opaque); /*!< Opaque nonsense */ - AST_STRING_FIELD(qop); /*!< Quality of Protection, since SIP wasn't complicated enough yet. */ - AST_STRING_FIELD(domain); /*!< Authorization domain */ - AST_STRING_FIELD(username); /*!< Who we are registering as */ - AST_STRING_FIELD(authuser); /*!< Who we *authenticate* as */ - AST_STRING_FIELD(hostname); /*!< Domain or host we register to */ - AST_STRING_FIELD(secret); /*!< Password in clear text */ - AST_STRING_FIELD(md5secret); /*!< Password in md5 */ - AST_STRING_FIELD(contact); /*!< Contact extension */ - AST_STRING_FIELD(random); - ); - int portno; /*!< Optional port override */ - int expire; /*!< Sched ID of expiration */ - int regattempts; /*!< Number of attempts (since the last success) */ - int timeout; /*!< sched id of sip_reg_timeout */ - int refresh; /*!< How often to refresh */ - struct sip_pvt *call; /*!< create a sip_pvt structure for each outbound "registration dialog" in progress */ - enum sipregistrystate regstate; /*!< Registration state (see above) */ - time_t regtime; /*!< Last succesful registration time */ - int callid_valid; /*!< 0 means we haven't chosen callid for this registry yet. */ - unsigned int ocseq; /*!< Sequence number we got to for REGISTERs for this registry */ - struct sockaddr_in us; /*!< Who the server thinks we are */ - int noncecount; /*!< Nonce-count */ - char lastmsg[256]; /*!< Last Message sent/received */ -}; - -/* --- Linked lists of various objects --------*/ - -/*! \brief The user list: Users and friends */ -static struct ast_user_list { - ASTOBJ_CONTAINER_COMPONENTS(struct sip_user); -} userl; - -/*! \brief The peer list: Peers and Friends */ -static struct ast_peer_list { - ASTOBJ_CONTAINER_COMPONENTS(struct sip_peer); -} peerl; - -/*! \brief The register list: Other SIP proxys we register with and place calls to */ -static struct ast_register_list { - ASTOBJ_CONTAINER_COMPONENTS(struct sip_registry); - int recheck; -} regl; - -static void temp_pvt_cleanup(void *); - -/*! \brief A per-thread temporary pvt structure */ -AST_THREADSTORAGE_CUSTOM(ts_temp_pvt, temp_pvt_init, temp_pvt_cleanup); - -#ifdef LOW_MEMORY -static void ts_ast_rtp_destroy(void *); - -AST_THREADSTORAGE_CUSTOM(ts_audio_rtp, ts_audio_rtp_init, ts_ast_rtp_destroy); -AST_THREADSTORAGE_CUSTOM(ts_video_rtp, ts_video_rtp_init, ts_ast_rtp_destroy); -#endif - -/*! \todo Move the sip_auth list to AST_LIST */ -static struct sip_auth *authl = NULL; /*!< Authentication list for realm authentication */ - - -/* --- Sockets and networking --------------*/ -static int sipsock = -1; /*!< Main socket for SIP network communication */ -static struct sockaddr_in bindaddr = { 0, }; /*!< The address we bind to */ -static struct sockaddr_in externip; /*!< External IP address if we are behind NAT */ -static char externhost[MAXHOSTNAMELEN]; /*!< External host name (possibly with dynamic DNS and DHCP */ -static time_t externexpire = 0; /*!< Expiration counter for re-resolving external host name in dynamic DNS */ -static int externrefresh = 10; -static struct ast_ha *localaddr; /*!< List of local networks, on the same side of NAT as this Asterisk */ -static struct in_addr __ourip; -static struct sockaddr_in outboundproxyip; -static int ourport; -static struct sockaddr_in debugaddr; - -static struct ast_config *notify_types; /*!< The list of manual NOTIFY types we know how to send */ - -/*---------------------------- Forward declarations of functions in chan_sip.c */ -/*! \note This is added to help splitting up chan_sip.c into several files - in coming releases */ - -/*--- PBX interface functions */ -static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause); -static int sip_devicestate(void *data); -static int sip_sendtext(struct ast_channel *ast, const char *text); -static int sip_call(struct ast_channel *ast, char *dest, int timeout); -static int sip_hangup(struct ast_channel *ast); -static int sip_answer(struct ast_channel *ast); -static struct ast_frame *sip_read(struct ast_channel *ast); -static int sip_write(struct ast_channel *ast, struct ast_frame *frame); -static int sip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); -static int sip_transfer(struct ast_channel *ast, const char *dest); -static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -static int sip_senddigit_begin(struct ast_channel *ast, char digit); -static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration); - -/*--- Transmitting responses and requests */ -static int sipsock_read(int *id, int fd, short events, void *ignore); -static int __sip_xmit(struct sip_pvt *p, char *data, int len); -static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, char *data, int len, int fatal, int sipmethod); -static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable); -static int retrans_pkt(const void *data); -static int transmit_sip_request(struct sip_pvt *p, struct sip_request *req); -static int transmit_response_using_temp(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg); -static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req); -static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req); -static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req); -static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable); -static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported); -static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale); -static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable); -static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, int reliable); -static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmittype reliable, int newbranch); -static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch); -static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init); -static int transmit_reinvite_with_sdp(struct sip_pvt *p); -static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration); -static int transmit_info_with_vidupdate(struct sip_pvt *p); -static int transmit_message_with_text(struct sip_pvt *p, const char *text); -static int transmit_refer(struct sip_pvt *p, const char *dest); -static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, char *vmexten); -static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate); -static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader); -static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno); -static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno); -static void copy_request(struct sip_request *dst, const struct sip_request *src); -static void receive_message(struct sip_pvt *p, struct sip_request *req); -static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req); -static int sip_send_mwi_to_peer(struct sip_peer *peer); -static int does_peer_need_mwi(struct sip_peer *peer); - -/*--- Dialog management */ -static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin, - int useglobal_nat, const int intended_method); -static int __sip_autodestruct(const void *data); -static void sip_scheddestroy(struct sip_pvt *p, int ms); -static int sip_cancel_destroy(struct sip_pvt *p); -static void sip_destroy(struct sip_pvt *p); -static int __sip_destroy(struct sip_pvt *p, int lockowner); -static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod); -static void __sip_pretend_ack(struct sip_pvt *p); -static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod); -static int auto_congest(const void *nothing); -static int update_call_counter(struct sip_pvt *fup, int event); -static int hangup_sip2cause(int cause); -static const char *hangup_cause2sip(int cause); -static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method); -static void free_old_route(struct sip_route *route); -static void list_route(struct sip_route *route); -static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards); -static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr_in *sin, - struct sip_request *req, char *uri); -static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag); -static void check_pendings(struct sip_pvt *p); -static void *sip_park_thread(void *stuff); -static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno); -static int sip_sipredirect(struct sip_pvt *p, const char *dest); - -/*--- Codec handling / SDP */ -static void try_suggested_sip_codec(struct sip_pvt *p); -static const char* get_sdp_iterate(int* start, struct sip_request *req, const char *name); -static const char *get_sdp(struct sip_request *req, const char *name); -static int find_sdp(struct sip_request *req); -static int process_sdp(struct sip_pvt *p, struct sip_request *req); -static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate, - char **m_buf, size_t *m_size, char **a_buf, size_t *a_size, - int debug, int *min_packet_size); -static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate, - char **m_buf, size_t *m_size, char **a_buf, size_t *a_size, - int debug); -static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p); -static void stop_media_flows(struct sip_pvt *p); - -/*--- Authentication stuff */ -static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len); -static int build_reply_digest(struct sip_pvt *p, int method, char *digest, int digest_len); -static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username, - const char *secret, const char *md5secret, int sipmethod, - char *uri, enum xmittype reliable, int ignore); -static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req, - int sipmethod, char *uri, enum xmittype reliable, - struct sockaddr_in *sin, struct sip_peer **authpeer); -static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, char *uri, enum xmittype reliable, struct sockaddr_in *sin); - -/*--- Domain handling */ -static int check_sip_domain(const char *domain, char *context, size_t len); /* Check if domain is one of our local domains */ -static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context); -static void clear_sip_domains(void); - -/*--- SIP realm authentication */ -static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno); -static int clear_realm_authentication(struct sip_auth *authlist); /* Clear realm authentication list (at reload) */ -static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm); - -/*--- Misc functions */ -static int sip_do_reload(enum channelreloadreason reason); -static int reload_config(enum channelreloadreason reason); -static int expire_register(const void *data); -static void *do_monitor(void *data); -static int restart_monitor(void); -static int sip_send_mwi_to_peer(struct sip_peer *peer); -static int sip_addrcmp(char *name, struct sockaddr_in *sin); /* Support for peer matching */ -static int sip_refer_allocate(struct sip_pvt *p); -static void ast_quiet_chan(struct ast_channel *chan); -static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target); - -/*--- Device monitoring and Device/extension state handling */ -static int cb_extensionstate(char *context, char* exten, int state, void *data); -static int sip_devicestate(void *data); -static int sip_poke_noanswer(const void *data); -static int sip_poke_peer(struct sip_peer *peer); -static void sip_poke_all_peers(void); -static void sip_peer_hold(struct sip_pvt *p, int hold); - -/*--- Applications, functions, CLI and manager command helpers */ -static const char *sip_nat_mode(const struct sip_pvt *p); -static int sip_show_inuse(int fd, int argc, char *argv[]); -static char *transfermode2str(enum transfermodes mode) attribute_const; -static char *nat2str(int nat) attribute_const; -static int peer_status(struct sip_peer *peer, char *status, int statuslen); -static int sip_show_users(int fd, int argc, char *argv[]); -static int _sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]); -static int sip_show_peers(int fd, int argc, char *argv[]); -static int sip_show_objects(int fd, int argc, char *argv[]); -static void print_group(int fd, ast_group_t group, int crlf); -static const char *dtmfmode2str(int mode) attribute_const; -static const char *insecure2str(int port, int invite) attribute_const; -static void cleanup_stale_contexts(char *new, char *old); -static void print_codec_to_cli(int fd, struct ast_codec_pref *pref); -static const char *domain_mode_to_text(const enum domain_mode mode); -static int sip_show_domains(int fd, int argc, char *argv[]); -static int _sip_show_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[]); -static int sip_show_peer(int fd, int argc, char *argv[]); -static int sip_show_user(int fd, int argc, char *argv[]); -static int sip_show_registry(int fd, int argc, char *argv[]); -static int sip_show_settings(int fd, int argc, char *argv[]); -static const char *subscription_type2str(enum subscriptiontype subtype) attribute_pure; -static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype); -static int __sip_show_channels(int fd, int argc, char *argv[], int subscriptions); -static int sip_show_channels(int fd, int argc, char *argv[]); -static int sip_show_subscriptions(int fd, int argc, char *argv[]); -static int __sip_show_channels(int fd, int argc, char *argv[], int subscriptions); -static char *complete_sipch(const char *line, const char *word, int pos, int state); -static char *complete_sip_peer(const char *word, int state, int flags2); -static char *complete_sip_show_peer(const char *line, const char *word, int pos, int state); -static char *complete_sip_debug_peer(const char *line, const char *word, int pos, int state); -static char *complete_sip_user(const char *word, int state, int flags2); -static char *complete_sip_show_user(const char *line, const char *word, int pos, int state); -static char *complete_sipnotify(const char *line, const char *word, int pos, int state); -static char *complete_sip_prune_realtime_peer(const char *line, const char *word, int pos, int state); -static char *complete_sip_prune_realtime_user(const char *line, const char *word, int pos, int state); -static int sip_show_channel(int fd, int argc, char *argv[]); -static int sip_show_history(int fd, int argc, char *argv[]); -static int sip_do_debug_ip(int fd, int argc, char *argv[]); -static int sip_do_debug_peer(int fd, int argc, char *argv[]); -static int sip_do_debug(int fd, int argc, char *argv[]); -static int sip_no_debug(int fd, int argc, char *argv[]); -static int sip_notify(int fd, int argc, char *argv[]); -static int sip_do_history(int fd, int argc, char *argv[]); -static int sip_no_history(int fd, int argc, char *argv[]); -static int func_header_read(struct ast_channel *chan, char *function, char *data, char *buf, size_t len); -static int func_check_sipdomain(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len); -static int function_sippeer(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len); -static int function_sipchaninfo_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len); -static int sip_dtmfmode(struct ast_channel *chan, void *data); -static int sip_addheader(struct ast_channel *chan, void *data); -static int sip_do_reload(enum channelreloadreason reason); -static int sip_reload(int fd, int argc, char *argv[]); -static int acf_channel_read(struct ast_channel *chan, char *funcname, char *preparse, char *buf, size_t buflen); - -/*--- Debugging - Functions for enabling debug per IP or fully, or enabling history logging for - a SIP dialog -*/ -static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to LOG_DEBUG at end of dialog, before destroying data */ -static inline int sip_debug_test_addr(const struct sockaddr_in *addr); -static inline int sip_debug_test_pvt(struct sip_pvt *p); -static void append_history_full(struct sip_pvt *p, const char *fmt, ...); -static void sip_dump_history(struct sip_pvt *dialog); - -/*--- Device object handling */ -static struct sip_peer *temp_peer(const char *name); -static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime); -static struct sip_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime); -static int update_call_counter(struct sip_pvt *fup, int event); -static void sip_destroy_peer(struct sip_peer *peer); -static void sip_destroy_user(struct sip_user *user); -static int sip_poke_peer(struct sip_peer *peer); -static int sip_poke_peer_s(const void *data); -static void set_peer_defaults(struct sip_peer *peer); -static struct sip_peer *temp_peer(const char *name); -static void register_peer_exten(struct sip_peer *peer, int onoff); -static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime, int devstate_only); -static struct sip_user *find_user(const char *name, int realtime); -static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req); -static int expire_register(const void *data); -static void reg_source_db(struct sip_peer *peer); -static void destroy_association(struct sip_peer *peer); -static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v); - -/* Realtime device support */ -static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *username, const char *fullcontact, int expirey); -static struct sip_user *realtime_user(const char *username); -static void update_peer(struct sip_peer *p, int expiry); -static struct sip_peer *realtime_peer(const char *peername, struct sockaddr_in *sin, int devstate_only); -static int sip_prune_realtime(int fd, int argc, char *argv[]); - -/*--- Internal UA client handling (outbound registrations) */ -static int ast_sip_ouraddrfor(struct in_addr *them, struct in_addr *us); -static void sip_registry_destroy(struct sip_registry *reg); -static int sip_register(char *value, int lineno); -static char *regstate2str(enum sipregistrystate regstate) attribute_const; -static int sip_reregister(const void *data); -static int __sip_do_register(struct sip_registry *r); -static int sip_reg_timeout(const void *data); -static void sip_send_all_registers(void); - -/*--- Parsing SIP requests and responses */ -static void append_date(struct sip_request *req); /* Append date to SIP packet */ -static int determine_firstline_parts(struct sip_request *req); -static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype); -static const char *gettag(const struct sip_request *req, const char *header, char *tagbuf, int tagbufsize); -static void set_insecure_flags(struct ast_flags *flags, const char *value, int lineno); -static int find_sip_method(const char *msg); -static unsigned int parse_sip_options(struct sip_pvt *pvt, const char *supported); -static int parse_request(struct sip_request *req); -static const char *get_header(const struct sip_request *req, const char *name); -static char *referstatus2str(enum referstatus rstatus) attribute_pure; -static int method_match(enum sipmethod id, const char *name); -static void parse_copy(struct sip_request *dst, const struct sip_request *src); -static char *get_in_brackets(char *tmp); -static const char *find_alias(const char *name, const char *_default); -static const char *__get_header(const struct sip_request *req, const char *name, int *start); -static int lws2sws(char *msgbuf, int len); -static void extract_uri(struct sip_pvt *p, struct sip_request *req); -static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req); -static int get_also_info(struct sip_pvt *p, struct sip_request *oreq); -static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req); -static int set_address_from_contact(struct sip_pvt *pvt); -static void check_via(struct sip_pvt *p, const struct sip_request *req); -static char *get_calleridname(const char *input, char *output, size_t outputsize); -static int get_rpid_num(const char *input, char *output, int maxlen); -static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq); -static int get_destination(struct sip_pvt *p, struct sip_request *oreq); -static int get_msg_text(char *buf, int len, struct sip_request *req); -static void free_old_route(struct sip_route *route); -static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout); - -/*--- Constructing requests and responses */ -static void initialize_initreq(struct sip_pvt *p, struct sip_request *req); -static int init_req(struct sip_request *req, int sipmethod, const char *recip); -static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch); -static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod); -static int init_resp(struct sip_request *resp, const char *msg); -static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req); -static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p); -static void build_via(struct sip_pvt *p); -static int create_addr_from_peer(struct sip_pvt *r, struct sip_peer *peer); -static int create_addr(struct sip_pvt *dialog, const char *opeer); -static char *generate_random_string(char *buf, size_t size); -static void build_callid_pvt(struct sip_pvt *pvt); -static void build_callid_registry(struct sip_registry *reg, struct in_addr ourip, const char *fromdomain); -static void make_our_tag(char *tagbuf, size_t len); -static int add_header(struct sip_request *req, const char *var, const char *value); -static int add_header_contentLength(struct sip_request *req, int len); -static int add_line(struct sip_request *req, const char *line); -static int add_text(struct sip_request *req, const char *text); -static int add_digit(struct sip_request *req, char digit, unsigned int duration); -static int add_vidupdate(struct sip_request *req); -static void add_route(struct sip_request *req, struct sip_route *route); -static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field); -static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field); -static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field); -static void set_destination(struct sip_pvt *p, char *uri); -static void append_date(struct sip_request *req); -static void build_contact(struct sip_pvt *p); -static void build_rpid(struct sip_pvt *p); - -/*------Request handling functions */ -static int handle_request(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock); -static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, char *e, int *nounlock); -static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int ignore, int seqno, int *nounlock); -static int handle_request_bye(struct sip_pvt *p, struct sip_request *req); -static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, char *e); -static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req); -static int handle_request_message(struct sip_pvt *p, struct sip_request *req); -static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e); -static void handle_request_info(struct sip_pvt *p, struct sip_request *req); -static int handle_request_options(struct sip_pvt *p, struct sip_request *req); -static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int ignore, int seqno, struct sockaddr_in *sin); -static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e); -static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno); - -/*------Response handling functions */ -static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); -static void handle_response_refer(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno); -static int handle_response_register(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int ignore, int seqno); -static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int ignore, int seqno); - -/*----- RTP interface functions */ -static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active); -static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp); -static int sip_get_codec(struct ast_channel *chan); -static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect); - -/*------ T38 Support --------- */ -static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt, int reinvite); /*!< T38 negotiation helper function */ -static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans); -static int transmit_reinvite_with_t38_sdp(struct sip_pvt *p); -static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan); -static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl); - -/*! \brief Definition of this channel for PBX channel registration */ -static const struct ast_channel_tech sip_tech = { - .type = "SIP", - .description = "Session Initiation Protocol (SIP)", - .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1), - .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER, - .requester = sip_request_call, - .devicestate = sip_devicestate, - .call = sip_call, - .hangup = sip_hangup, - .answer = sip_answer, - .read = sip_read, - .write = sip_write, - .write_video = sip_write, - .indicate = sip_indicate, - .transfer = sip_transfer, - .fixup = sip_fixup, - .send_digit_begin = sip_senddigit_begin, - .send_digit_end = sip_senddigit_end, - .bridge = ast_rtp_bridge, - .send_text = sip_sendtext, - .func_channel_read = acf_channel_read, -}; - -/*! \brief This version of the sip channel tech has no send_digit_begin - * callback. This is for use with channels using SIP INFO DTMF so that - * the core knows that the channel doesn't want DTMF BEGIN frames. */ -static const struct ast_channel_tech sip_tech_info = { - .type = "SIP", - .description = "Session Initiation Protocol (SIP)", - .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1), - .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER, - .requester = sip_request_call, - .devicestate = sip_devicestate, - .call = sip_call, - .hangup = sip_hangup, - .answer = sip_answer, - .read = sip_read, - .write = sip_write, - .write_video = sip_write, - .indicate = sip_indicate, - .transfer = sip_transfer, - .fixup = sip_fixup, - .send_digit_end = sip_senddigit_end, - .bridge = ast_rtp_bridge, - .send_text = sip_sendtext, - .func_channel_read = acf_channel_read, -}; - -/**--- some list management macros. **/ - -#define UNLINK(element, head, prev) do { \ - if (prev) \ - (prev)->next = (element)->next; \ - else \ - (head) = (element)->next; \ - } while (0) - -/*! \brief Interface structure with callbacks used to connect to RTP module */ -static struct ast_rtp_protocol sip_rtp = { - type: "SIP", - get_rtp_info: sip_get_rtp_peer, - get_vrtp_info: sip_get_vrtp_peer, - set_rtp_peer: sip_set_rtp_peer, - get_codec: sip_get_codec, -}; - -/*! \brief Interface structure with callbacks used to connect to UDPTL module*/ -static struct ast_udptl_protocol sip_udptl = { - type: "SIP", - get_udptl_info: sip_get_udptl_peer, - set_udptl_peer: sip_set_udptl_peer, -}; - -/*! \brief Convert transfer status to string */ -static char *referstatus2str(enum referstatus rstatus) -{ - int i = (sizeof(referstatusstrings) / sizeof(referstatusstrings[0])); - int x; - - for (x = 0; x < i; x++) { - if (referstatusstrings[x].status == rstatus) - return (char *) referstatusstrings[x].text; - } - return ""; -} - -/*! \brief Initialize the initital request packet in the pvt structure. - This packet is used for creating replies and future requests in - a dialog */ -static void initialize_initreq(struct sip_pvt *p, struct sip_request *req) -{ - if (p->initreq.headers && option_debug) { - ast_log(LOG_DEBUG, "Initializing already initialized SIP dialog %s (presumably reinvite)\n", p->callid); - } - /* Use this as the basis */ - copy_request(&p->initreq, req); - parse_request(&p->initreq); - if (ast_test_flag(req, SIP_PKT_DEBUG)) - ast_verbose("%d headers, %d lines\n", p->initreq.headers, p->initreq.lines); -} - -static void sip_alreadygone(struct sip_pvt *dialog) -{ - if (option_debug > 2) - ast_log(LOG_DEBUG, "Setting SIP_ALREADYGONE on dialog %s\n", dialog->callid); - ast_set_flag(&dialog->flags[0], SIP_ALREADYGONE); -} - - -/*! \brief returns true if 'name' (with optional trailing whitespace) - * matches the sip method 'id'. - * Strictly speaking, SIP methods are case SENSITIVE, but we do - * a case-insensitive comparison to be more tolerant. - * following Jon Postel's rule: Be gentle in what you accept, strict with what you send - */ -static int method_match(enum sipmethod id, const char *name) -{ - int len = strlen(sip_methods[id].text); - int l_name = name ? strlen(name) : 0; - /* true if the string is long enough, and ends with whitespace, and matches */ - return (l_name >= len && name[len] < 33 && - !strncasecmp(sip_methods[id].text, name, len)); -} - -/*! \brief find_sip_method: Find SIP method from header */ -static int find_sip_method(const char *msg) -{ - int i, res = 0; - - if (ast_strlen_zero(msg)) - return 0; - for (i = 1; i < (sizeof(sip_methods) / sizeof(sip_methods[0])) && !res; i++) { - if (method_match(i, msg)) - res = sip_methods[i].id; - } - return res; -} - -/*! \brief Parse supported header in incoming packet */ -static unsigned int parse_sip_options(struct sip_pvt *pvt, const char *supported) -{ - char *next, *sep; - char *temp; - unsigned int profile = 0; - int i, found; - - if (ast_strlen_zero(supported) ) - return 0; - temp = ast_strdupa(supported); - - if (option_debug > 2 && sipdebug) - ast_log(LOG_DEBUG, "Begin: parsing SIP \"Supported: %s\"\n", supported); - - for (next = temp; next; next = sep) { - found = FALSE; - if ( (sep = strchr(next, ',')) != NULL) - *sep++ = '\0'; - next = ast_skip_blanks(next); - if (option_debug > 2 && sipdebug) - ast_log(LOG_DEBUG, "Found SIP option: -%s-\n", next); - for (i=0; i < (sizeof(sip_options) / sizeof(sip_options[0])); i++) { - if (!strcasecmp(next, sip_options[i].text)) { - profile |= sip_options[i].id; - found = TRUE; - if (option_debug > 2 && sipdebug) - ast_log(LOG_DEBUG, "Matched SIP option: %s\n", next); - break; - } - } - if (!found && option_debug > 2 && sipdebug) { - if (!strncasecmp(next, "x-", 2)) - ast_log(LOG_DEBUG, "Found private SIP option, not supported: %s\n", next); - else - ast_log(LOG_DEBUG, "Found no match for SIP option: %s (Please file bug report!)\n", next); - } - } - - if (pvt) - pvt->sipoptions = profile; - return profile; -} - -/*! \brief See if we pass debug IP filter */ -static inline int sip_debug_test_addr(const struct sockaddr_in *addr) -{ - if (!sipdebug) - return 0; - if (debugaddr.sin_addr.s_addr) { - if (((ntohs(debugaddr.sin_port) != 0) - && (debugaddr.sin_port != addr->sin_port)) - || (debugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) - return 0; - } - return 1; -} - -/*! \brief The real destination address for a write */ -static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p) -{ - return ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE ? &p->recv : &p->sa; -} - -/*! \brief Display SIP nat mode */ -static const char *sip_nat_mode(const struct sip_pvt *p) -{ - return ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE ? "NAT" : "no NAT"; -} - -/*! \brief Test PVT for debugging output */ -static inline int sip_debug_test_pvt(struct sip_pvt *p) -{ - if (!sipdebug) - return 0; - return sip_debug_test_addr(sip_real_dst(p)); -} - -/*! \brief Transmit SIP message */ -static int __sip_xmit(struct sip_pvt *p, char *data, int len) -{ - int res; - const struct sockaddr_in *dst = sip_real_dst(p); - res = sendto(sipsock, data, len, 0, (const struct sockaddr *)dst, sizeof(struct sockaddr_in)); - - if (res == -1) { - switch (errno) { - case EBADF: /* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */ - case EHOSTUNREACH: /* Host can't be reached */ - case ENETDOWN: /* Inteface down */ - case ENETUNREACH: /* Network failure */ - case ECONNREFUSED: /* ICMP port unreachable */ - res = XMIT_ERROR; /* Don't bother with trying to transmit again */ - } - } - if (res != len) - ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s:%d returned %d: %s\n", data, len, ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), res, strerror(errno)); - return res; -} - - -/*! \brief Build a Via header for a request */ -static void build_via(struct sip_pvt *p) -{ - /* Work around buggy UNIDEN UIP200 firmware */ - const char *rport = ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_RFC3581 ? ";rport" : ""; - - /* z9hG4bK is a magic cookie. See RFC 3261 section 8.1.1.7 */ - ast_string_field_build(p, via, "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x%s", - ast_inet_ntoa(p->ourip), ourport, (int) p->branch, rport); -} - -/*! \brief NAT fix - decide which IP address to use for ASterisk server? - * - * Using the localaddr structure built up with localnet statements in sip.conf - * apply it to their address to see if we need to substitute our - * externip or can get away with our internal bindaddr - */ -static enum sip_result ast_sip_ouraddrfor(struct in_addr *them, struct in_addr *us) -{ - struct sockaddr_in theirs, ours; - - /* Get our local information */ - ast_ouraddrfor(them, us); - theirs.sin_addr = *them; - ours.sin_addr = *us; - - if (localaddr && externip.sin_addr.s_addr && - (ast_apply_ha(localaddr, &theirs)) && - (!global_matchexterniplocally || !ast_apply_ha(localaddr, &ours))) { - if (externexpire && time(NULL) >= externexpire) { - struct ast_hostent ahp; - struct hostent *hp; - - externexpire = time(NULL) + externrefresh; - if ((hp = ast_gethostbyname(externhost, &ahp))) { - memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr)); - } else - ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost); - } - *us = externip.sin_addr; - if (option_debug) { - ast_log(LOG_DEBUG, "Target address %s is not local, substituting externip\n", - ast_inet_ntoa(*(struct in_addr *)&them->s_addr)); - } - } else if (bindaddr.sin_addr.s_addr) - *us = bindaddr.sin_addr; - return AST_SUCCESS; -} - -/*! \brief Append to SIP dialog history - \return Always returns 0 */ -#define append_history(p, event, fmt , args... ) append_history_full(p, "%-15s " fmt, event, ## args) - -static void append_history_full(struct sip_pvt *p, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); - -/*! \brief Append to SIP dialog history with arg list */ -static void __attribute__((format(printf, 2, 0))) append_history_va(struct sip_pvt *p, const char *fmt, va_list ap) -{ - char buf[80], *c = buf; /* max history length */ - struct sip_history *hist; - int l; - - vsnprintf(buf, sizeof(buf), fmt, ap); - strsep(&c, "\r\n"); /* Trim up everything after \r or \n */ - l = strlen(buf) + 1; - if (!(hist = ast_calloc(1, sizeof(*hist) + l))) - return; - if (!p->history && !(p->history = ast_calloc(1, sizeof(*p->history)))) { - free(hist); - return; - } - memcpy(hist->event, buf, l); - if (p->history_entries == MAX_HISTORY_ENTRIES) { - struct sip_history *oldest; - oldest = AST_LIST_REMOVE_HEAD(p->history, list); - p->history_entries--; - free(oldest); - } - AST_LIST_INSERT_TAIL(p->history, hist, list); - p->history_entries++; -} - -/*! \brief Append to SIP dialog history with arg list */ -static void append_history_full(struct sip_pvt *p, const char *fmt, ...) -{ - va_list ap; - - if (!p) - return; - - if (ast_test_flag(&p->flags[0], SIP_NO_HISTORY) - && !recordhistory && !dumphistory) { - return; - } - - va_start(ap, fmt); - append_history_va(p, fmt, ap); - va_end(ap); - - return; -} - -/*! \brief Retransmit SIP message if no answer (Called from scheduler) */ -static int retrans_pkt(const void *data) -{ - struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL; - int reschedule = DEFAULT_RETRANS; - int xmitres = 0; - - /* Lock channel PVT */ - ast_mutex_lock(&pkt->owner->lock); - - if (pkt->retrans < MAX_RETRANS) { - pkt->retrans++; - if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */ - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method); - } else { - int siptimer_a; - - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method); - if (!pkt->timer_a) - pkt->timer_a = 2 ; - else - pkt->timer_a = 2 * pkt->timer_a; - - /* For non-invites, a maximum of 4 secs */ - siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */ - if (pkt->method != SIP_INVITE && siptimer_a > 4000) - siptimer_a = 4000; - - /* Reschedule re-transmit */ - reschedule = siptimer_a; - if (option_debug > 3) - ast_log(LOG_DEBUG, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid); - } - - if (sip_debug_test_pvt(pkt->owner)) { - const struct sockaddr_in *dst = sip_real_dst(pkt->owner); - ast_verbose("Retransmitting #%d (%s) to %s:%d:\n%s\n---\n", - pkt->retrans, sip_nat_mode(pkt->owner), - ast_inet_ntoa(dst->sin_addr), - ntohs(dst->sin_port), pkt->data); - } - - append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data); - xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); - ast_mutex_unlock(&pkt->owner->lock); - if (xmitres == XMIT_ERROR) - ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner->callid); - else - return reschedule; - } - /* Too many retries */ - if (pkt->owner && pkt->method != SIP_OPTIONS && xmitres == 0) { - if (ast_test_flag(pkt, FLAG_FATAL) || sipdebug) /* Tell us if it's critical or if we're debugging */ - ast_log(LOG_WARNING, "Maximum retries exceeded on transmission %s for seqno %d (%s %s) -- See doc/sip-retransmit.txt.\n", pkt->owner->callid, pkt->seqno, (ast_test_flag(pkt, FLAG_FATAL)) ? "Critical" : "Non-critical", (ast_test_flag(pkt, FLAG_RESPONSE)) ? "Response" : "Request"); - } else if ((pkt->method == SIP_OPTIONS) && sipdebug) { - ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) -- See doc/sip-retransmit.txt.\n", pkt->owner->callid); - } - if (xmitres == XMIT_ERROR) { - ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission of transaction in call id %s \n", pkt->owner->callid); - append_history(pkt->owner, "XmitErr", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); - } else - append_history(pkt->owner, "MaxRetries", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); - - pkt->retransid = -1; - - if (ast_test_flag(pkt, FLAG_FATAL)) { - while(pkt->owner->owner && ast_channel_trylock(pkt->owner->owner)) { - DEADLOCK_AVOIDANCE(&pkt->owner->lock); /* SIP_PVT, not channel */ - } - - if (pkt->owner->owner && !pkt->owner->owner->hangupcause) - pkt->owner->owner->hangupcause = AST_CAUSE_NO_USER_RESPONSE; - - if (pkt->owner->owner) { - sip_alreadygone(pkt->owner); - ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet (see doc/sip-retransmit.txt).\n", pkt->owner->callid); - ast_queue_hangup(pkt->owner->owner); - ast_channel_unlock(pkt->owner->owner); - } else { - /* If no channel owner, destroy now */ - - /* Let the peerpoke system expire packets when the timer expires for poke_noanswer */ - if (pkt->method != SIP_OPTIONS) { - ast_set_flag(&pkt->owner->flags[0], SIP_NEEDDESTROY); - sip_alreadygone(pkt->owner); - if (option_debug) - append_history(pkt->owner, "DialogKill", "Killing this failed dialog immediately"); - } - } - } - - if (pkt->method == SIP_BYE) { - /* We're not getting answers on SIP BYE's. Tear down the call anyway. */ - if (pkt->owner->owner) - ast_channel_unlock(pkt->owner->owner); - append_history(pkt->owner, "ByeFailure", "Remote peer doesn't respond to bye. Destroying call anyway."); - ast_set_flag(&pkt->owner->flags[0], SIP_NEEDDESTROY); - } - - /* In any case, go ahead and remove the packet */ - for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) { - if (cur == pkt) - break; - } - if (cur) { - if (prev) - prev->next = cur->next; - else - pkt->owner->packets = cur->next; - ast_mutex_unlock(&pkt->owner->lock); - free(cur); - pkt = NULL; - } else - ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n"); - if (pkt) - ast_mutex_unlock(&pkt->owner->lock); - return 0; -} - -/*! \brief Transmit packet with retransmits - \return 0 on success, -1 on failure to allocate packet -*/ -static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, char *data, int len, int fatal, int sipmethod) -{ - struct sip_pkt *pkt; - int siptimer_a = DEFAULT_RETRANS; - int xmitres = 0; - - if (!(pkt = ast_calloc(1, sizeof(*pkt) + len + 1))) - return AST_FAILURE; - memcpy(pkt->data, data, len); - pkt->method = sipmethod; - pkt->packetlen = len; - pkt->next = p->packets; - pkt->owner = p; - pkt->seqno = seqno; - if (resp) - ast_set_flag(pkt, FLAG_RESPONSE); - pkt->data[len] = '\0'; - pkt->timer_t1 = p->timer_t1; /* Set SIP timer T1 */ - pkt->retransid = -1; - if (fatal) - ast_set_flag(pkt, FLAG_FATAL); - if (pkt->timer_t1) - siptimer_a = pkt->timer_t1 * 2; - - if (option_debug > 3 && sipdebug) - ast_log(LOG_DEBUG, "*** SIP TIMER: Initializing retransmit timer on packet: Id #%d\n", pkt->retransid); - pkt->retransid = -1; - pkt->next = p->packets; - p->packets = pkt; - if (sipmethod == SIP_INVITE) { - /* Note this is a pending invite */ - p->pendinginvite = seqno; - } - - xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); /* Send packet */ - - if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */ - append_history(pkt->owner, "XmitErr", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); - return AST_FAILURE; - } else { - /* Schedule retransmission */ - pkt->retransid = ast_sched_add_variable(sched, siptimer_a, retrans_pkt, pkt, 1); - return AST_SUCCESS; - } -} - -/*! \brief Kill a SIP dialog (called by scheduler) */ -static int __sip_autodestruct(const void *data) -{ - struct sip_pvt *p = (struct sip_pvt *)data; - - /* If this is a subscription, tell the phone that we got a timeout */ - if (p->subscribed) { - transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1, TRUE); /* Send last notification */ - p->subscribed = NONE; - append_history(p, "Subscribestatus", "timeout"); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Re-scheduled destruction of SIP subsription %s\n", p->callid ? p->callid : "<unknown>"); - return 10000; /* Reschedule this destruction so that we know that it's gone */ - } - - /* If there are packets still waiting for delivery, delay the destruction */ - /* via bug 12101, the two usages of SIP_NEEDDESTROY in the following block - * of code make a sort of "safety relief valve", that allows sip channels - * that were created via INVITE, then thru some sequence were CANCELED, - * to die, rather than infinitely be rescheduled */ - if (p->packets && !ast_test_flag(&p->flags[0], SIP_NEEDDESTROY)) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : "<unknown>"); - append_history(p, "ReliableXmit", "timeout"); - if (p->method == SIP_CANCEL || p->method == SIP_BYE) { - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - return 10000; - } - - /* If we're destroying a subscription, dereference peer object too */ - if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer) - ASTOBJ_UNREF(p->relatedpeer,sip_destroy_peer); - - /* Reset schedule ID */ - p->autokillid = -1; - - if (option_debug) - ast_log(LOG_DEBUG, "Auto destroying SIP dialog '%s'\n", p->callid); - append_history(p, "AutoDestroy", "%s", p->callid); - if (p->owner) { - ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner in place (Method: %s)\n", p->callid, sip_methods[p->method].text); - ast_queue_hangup(p->owner); - } else if (p->refer && !ast_test_flag(&p->flags[0], SIP_ALREADYGONE)) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Finally hanging up channel after transfer: %s\n", p->callid); - transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } else - sip_destroy(p); - return 0; -} - -/*! \brief Schedule destruction of SIP dialog */ -static void sip_scheddestroy(struct sip_pvt *p, int ms) -{ - if (ms < 0) { - if (p->timer_t1 == 0) - p->timer_t1 = 500; /* Set timer T1 if not set (RFC 3261) */ - ms = p->timer_t1 * 64; - } - if (sip_debug_test_pvt(p)) - ast_verbose("Scheduling destruction of SIP dialog '%s' in %d ms (Method: %s)\n", p->callid, ms, sip_methods[p->method].text); - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) - append_history(p, "SchedDestroy", "%d ms", ms); - - AST_SCHED_DEL(sched, p->autokillid); - p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, p); -} - -/*! \brief Cancel destruction of SIP dialog */ -static int sip_cancel_destroy(struct sip_pvt *p) -{ - int res = 0; - if (p->autokillid > -1) { - if (!(res = ast_sched_del(sched, p->autokillid))) { - append_history(p, "CancelDestroy", ""); - p->autokillid = -1; - } - } - return res; -} - -/*! \brief Acknowledges receipt of a packet and stops retransmission - * called with p locked*/ -static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod) -{ - struct sip_pkt *cur, *prev = NULL; - - /* Just in case... */ - char *msg; - int res = FALSE; - - msg = sip_methods[sipmethod].text; - - for (cur = p->packets; cur; prev = cur, cur = cur->next) { - if ((cur->seqno == seqno) && ((ast_test_flag(cur, FLAG_RESPONSE)) == resp) && - ((ast_test_flag(cur, FLAG_RESPONSE)) || - (!strncasecmp(msg, cur->data, strlen(msg)) && (cur->data[strlen(msg)] < 33)))) { - if (!resp && (seqno == p->pendinginvite)) { - if (option_debug) - ast_log(LOG_DEBUG, "Acked pending invite %d\n", p->pendinginvite); - p->pendinginvite = 0; - } - /* this is our baby */ - res = TRUE; - UNLINK(cur, p->packets, prev); - if (cur->retransid > -1) { - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid); - } - /* This odd section is designed to thwart a - * race condition in the packet scheduler. There are - * two conditions under which deleting the packet from the - * scheduler can fail. - * - * 1. The packet has been removed from the scheduler because retransmission - * is being attempted. The problem is that if the packet is currently attempting - * retransmission and we are at this point in the code, then that MUST mean - * that retrans_pkt is waiting on p's lock. Therefore we will relinquish the - * lock temporarily to allow retransmission. - * - * 2. The packet has reached its maximum number of retransmissions and has - * been permanently removed from the packet scheduler. If this is the case, then - * the packet's retransid will be set to -1. The atomicity of the setting and checking - * of the retransid to -1 is ensured since in both cases p's lock is held. - */ - while (cur->retransid > -1 && ast_sched_del(sched, cur->retransid)) { - DEADLOCK_AVOIDANCE(&p->lock); - } - free(cur); - break; - } - } - if (option_debug) - ast_log(LOG_DEBUG, "Stopping retransmission on '%s' of %s %d: Match %s\n", p->callid, resp ? "Response" : "Request", seqno, res == FALSE ? "Not Found" : "Found"); -} - -/*! \brief Pretend to ack all packets - * called with p locked */ -static void __sip_pretend_ack(struct sip_pvt *p) -{ - struct sip_pkt *cur = NULL; - - while (p->packets) { - int method; - if (cur == p->packets) { - ast_log(LOG_WARNING, "Have a packet that doesn't want to give up! %s\n", sip_methods[cur->method].text); - return; - } - cur = p->packets; - method = (cur->method) ? cur->method : find_sip_method(cur->data); - __sip_ack(p, cur->seqno, ast_test_flag(cur, FLAG_RESPONSE), method); - } -} - -/*! \brief Acks receipt of packet, keep it around (used for provisional responses) */ -static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod) -{ - struct sip_pkt *cur; - int res = -1; - - for (cur = p->packets; cur; cur = cur->next) { - if (cur->seqno == seqno && ast_test_flag(cur, FLAG_RESPONSE) == resp && - (ast_test_flag(cur, FLAG_RESPONSE) || method_match(sipmethod, cur->data))) { - /* this is our baby */ - if (cur->retransid > -1) { - if (option_debug > 3 && sipdebug) - ast_log(LOG_DEBUG, "*** SIP TIMER: Cancelling retransmission #%d - %s (got response)\n", cur->retransid, sip_methods[sipmethod].text); - } - AST_SCHED_DEL(sched, cur->retransid); - res = 0; - break; - } - } - if (option_debug) - ast_log(LOG_DEBUG, "(Provisional) Stopping retransmission (but retaining packet) on '%s' %s %d: %s\n", p->callid, resp ? "Response" : "Request", seqno, res == -1 ? "Not Found" : "Found"); - return res; -} - - -/*! \brief Copy SIP request, parse it */ -static void parse_copy(struct sip_request *dst, const struct sip_request *src) -{ - memset(dst, 0, sizeof(*dst)); - memcpy(dst->data, src->data, sizeof(dst->data)); - dst->len = src->len; - parse_request(dst); -} - -/*! \brief add a blank line if no body */ -static void add_blank(struct sip_request *req) -{ - if (!req->lines) { - /* Add extra empty return. add_header() reserves 4 bytes so cannot be truncated */ - snprintf(req->data + req->len, sizeof(req->data) - req->len, "\r\n"); - req->len += strlen(req->data + req->len); - } -} - -/*! \brief Transmit response on SIP request*/ -static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno) -{ - int res; - - add_blank(req); - if (sip_debug_test_pvt(p)) { - const struct sockaddr_in *dst = sip_real_dst(p); - - ast_verbose("\n<--- %sTransmitting (%s) to %s:%d --->\n%s\n<------------>\n", - reliable ? "Reliably " : "", sip_nat_mode(p), - ast_inet_ntoa(dst->sin_addr), - ntohs(dst->sin_port), req->data); - } - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) { - struct sip_request tmp; - parse_copy(&tmp, req); - append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", tmp.data, get_header(&tmp, "CSeq"), - (tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? tmp.rlPart2 : sip_methods[tmp.method].text); - } - res = (reliable) ? - __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) : - __sip_xmit(p, req->data, req->len); - if (res > 0) - return 0; - return res; -} - -/*! \brief Send SIP Request to the other part of the dialogue */ -static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno) -{ - int res; - - add_blank(req); - if (sip_debug_test_pvt(p)) { - if (ast_test_flag(&p->flags[0], SIP_NAT_ROUTE)) - ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port), req->data); - else - ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), req->data); - } - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) { - struct sip_request tmp; - parse_copy(&tmp, req); - append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", tmp.data, get_header(&tmp, "CSeq"), sip_methods[tmp.method].text); - } - res = (reliable) ? - __sip_reliable_xmit(p, seqno, 0, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) : - __sip_xmit(p, req->data, req->len); - return res; -} - -/*! \brief Locate closing quote in a string, skipping escaped quotes. - * optionally with a limit on the search. - * start must be past the first quote. - */ -static const char *find_closing_quote(const char *start, const char *lim) -{ - char last_char = '\0'; - const char *s; - for (s = start; *s && s != lim; last_char = *s++) { - if (*s == '"' && last_char != '\\') - break; - } - return s; -} - -/*! \brief Pick out text in brackets from character string - \return pointer to terminated stripped string - \param tmp input string that will be modified - Examples: - - "foo" <bar> valid input, returns bar - foo returns the whole string - < "foo ... > returns the string between brackets - < "foo... bogus (missing closing bracket), returns the whole string - XXX maybe should still skip the opening bracket - */ -static char *get_in_brackets(char *tmp) -{ - const char *parse = tmp; - char *first_bracket; - - /* - * Skip any quoted text until we find the part in brackets. - * On any error give up and return the full string. - */ - while ( (first_bracket = strchr(parse, '<')) ) { - char *first_quote = strchr(parse, '"'); - - if (!first_quote || first_quote > first_bracket) - break; /* no need to look at quoted part */ - /* the bracket is within quotes, so ignore it */ - parse = find_closing_quote(first_quote + 1, NULL); - if (!*parse) { /* not found, return full string ? */ - /* XXX or be robust and return in-bracket part ? */ - ast_log(LOG_WARNING, "No closing quote found in '%s'\n", tmp); - break; - } - parse++; - } - if (first_bracket) { - char *second_bracket = strchr(first_bracket + 1, '>'); - if (second_bracket) { - *second_bracket = '\0'; - tmp = first_bracket + 1; - } else { - ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp); - } - } - return tmp; -} - -/*! \brief Send SIP MESSAGE text within a call - Called from PBX core sendtext() application */ -static int sip_sendtext(struct ast_channel *ast, const char *text) -{ - struct sip_pvt *p = ast->tech_pvt; - int debug = sip_debug_test_pvt(p); - - if (debug) - ast_verbose("Sending text %s on %s\n", text, ast->name); - if (!p) - return -1; - if (ast_strlen_zero(text)) - return 0; - if (debug) - ast_verbose("Really sending text %s on %s\n", text, ast->name); - transmit_message_with_text(p, text); - return 0; -} - -/*! \brief Update peer object in realtime storage - If the Asterisk system name is set in asterisk.conf, we will use - that name and store that in the "regserver" field in the sippeers - table to facilitate multi-server setups. -*/ -static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *username, const char *fullcontact, int expirey) -{ - char port[10]; - char ipaddr[INET_ADDRSTRLEN]; - char regseconds[20]; - - char *sysname = ast_config_AST_SYSTEM_NAME; - char *syslabel = NULL; - - time_t nowtime = time(NULL) + expirey; - const char *fc = fullcontact ? "fullcontact" : NULL; - - snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime); /* Expiration time */ - ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr)); - snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port)); - - if (ast_strlen_zero(sysname)) /* No system name, disable this */ - sysname = NULL; - else if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTSAVE_SYSNAME)) - syslabel = "regserver"; - - if (fc) - ast_update_realtime("sippeers", "name", peername, "ipaddr", ipaddr, - "port", port, "regseconds", regseconds, - "username", username, fc, fullcontact, syslabel, sysname, NULL); /* note fc and syslabel _can_ be NULL */ - else - ast_update_realtime("sippeers", "name", peername, "ipaddr", ipaddr, - "port", port, "regseconds", regseconds, - "username", username, syslabel, sysname, NULL); /* note syslabel _can_ be NULL */ -} - -/*! \brief Automatically add peer extension to dial plan */ -static void register_peer_exten(struct sip_peer *peer, int onoff) -{ - char multi[256]; - char *stringp, *ext, *context; - - /* XXX note that global_regcontext is both a global 'enable' flag and - * the name of the global regexten context, if not specified - * individually. - */ - if (ast_strlen_zero(global_regcontext)) - return; - - ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi)); - stringp = multi; - while ((ext = strsep(&stringp, "&"))) { - if ((context = strchr(ext, '@'))) { - *context++ = '\0'; /* split ext@context */ - if (!ast_context_find(context)) { - ast_log(LOG_WARNING, "Context %s must exist in regcontext= in sip.conf!\n", context); - continue; - } - } else { - context = global_regcontext; - } - if (onoff) { - if (!ast_exists_extension(NULL, context, ext, 1, NULL)) { - ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop", - ast_strdup(peer->name), ast_free, "SIP"); - } - } else { - ast_context_remove_extension(context, ext, 1, NULL); - } - } -} - -/*! \brief Destroy peer object from memory */ -static void sip_destroy_peer(struct sip_peer *peer) -{ - if (option_debug > 2) - ast_log(LOG_DEBUG, "Destroying SIP peer %s\n", peer->name); - - /* Delete it, it needs to disappear */ - if (peer->call) - sip_destroy(peer->call); - - if (peer->mwipvt) /* We have an active subscription, delete it */ - sip_destroy(peer->mwipvt); - - if (peer->chanvars) { - ast_variables_destroy(peer->chanvars); - peer->chanvars = NULL; - } - - register_peer_exten(peer, FALSE); - ast_free_ha(peer->ha); - if (ast_test_flag(&peer->flags[1], SIP_PAGE2_SELFDESTRUCT)) - apeerobjs--; - else if (ast_test_flag(&peer->flags[0], SIP_REALTIME)) - rpeerobjs--; - else - speerobjs--; - clear_realm_authentication(peer->auth); - peer->auth = NULL; - free(peer); -} - -/*! \brief Update peer data in database (if used) */ -static void update_peer(struct sip_peer *p, int expiry) -{ - int rtcachefriends = ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS); - if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTUPDATE) && - (ast_test_flag(&p->flags[0], SIP_REALTIME) || rtcachefriends)) { - realtime_update_peer(p->name, &p->addr, p->username, rtcachefriends ? p->fullcontact : NULL, expiry); - } -} - - -/*! \brief realtime_peer: Get peer from realtime storage - * Checks the "sippeers" realtime family from extconfig.conf - * \todo Consider adding check of port address when matching here to follow the same - * algorithm as for static peers. Will we break anything by adding that? -*/ -static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_in *sin, int devstate_only) -{ - struct sip_peer *peer=NULL; - struct ast_variable *var = NULL; - struct ast_config *peerlist = NULL; - struct ast_variable *tmp; - struct ast_flags flags = {0}; - const char *iabuf = NULL; - char portstring[6]; /*up to five digits plus null terminator*/ - const char *insecure; - char *cat = NULL; - unsigned short portnum; - - /* First check on peer name */ - if (newpeername) { - var = ast_load_realtime("sippeers", "name", newpeername, "host", "dynamic", NULL); - if (!var && sin) - var = ast_load_realtime("sippeers", "name", newpeername, "host", ast_inet_ntoa(sin->sin_addr), NULL); - if (!var) { - var = ast_load_realtime("sippeers", "name", newpeername, NULL); - /*!\note - * If this one loaded something, then we need to ensure that the host - * field matched. The only reason why we can't have this as a criteria - * is because we only have the IP address and the host field might be - * set as a name (and the reverse PTR might not match). - */ - if (var && sin) { - for (tmp = var; tmp; tmp = tmp->next) { - if (!strcasecmp(tmp->name, "host")) { - struct hostent *hp; - struct ast_hostent ahp; - if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(&hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) { - /* No match */ - ast_variables_destroy(var); - var = NULL; - } - break; - } - } - } - } - } - - if (!var && sin) { /* Then check on IP address */ - iabuf = ast_inet_ntoa(sin->sin_addr); - portnum = ntohs(sin->sin_port); - sprintf(portstring, "%d", portnum); - var = ast_load_realtime("sippeers", "host", iabuf, "port", portstring, NULL); /* First check for fixed IP hosts */ - if (!var) - var = ast_load_realtime("sippeers", "ipaddr", iabuf, "port", portstring, NULL); /* Then check for registered hosts */ - if (!var) { - peerlist = ast_load_realtime_multientry("sippeers", "host", iabuf, NULL); /*No exact match, see if port is insecure, try host match first*/ - if(peerlist){ - while((cat = ast_category_browse(peerlist, cat))) - { - insecure = ast_variable_retrieve(peerlist, cat, "insecure"); - set_insecure_flags(&flags, insecure, -1); - if(ast_test_flag(&flags, SIP_INSECURE_PORT)) { - var = ast_category_root(peerlist, cat); - break; - } - } - } - if(!var) { - ast_config_destroy(peerlist); - peerlist = NULL; /*for safety's sake*/ - cat = NULL; - peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", iabuf, NULL); /*No exact match, see if port is insecure, now try ip address match*/ - if(peerlist) { - while((cat = ast_category_browse(peerlist, cat))) - { - insecure = ast_variable_retrieve(peerlist, cat, "insecure"); - set_insecure_flags(&flags, insecure, -1); - if(ast_test_flag(&flags, SIP_INSECURE_PORT)) { - var = ast_category_root(peerlist, cat); - break; - } - } - } - } - } - } - - if (!var) { - if(peerlist) - ast_config_destroy(peerlist); - return NULL; - } - - for (tmp = var; tmp; tmp = tmp->next) { - /* If this is type=user, then skip this object. */ - if (!strcasecmp(tmp->name, "type") && - !strcasecmp(tmp->value, "user")) { - ast_variables_destroy(var); - return NULL; - } else if (!newpeername && !strcasecmp(tmp->name, "name")) { - newpeername = tmp->value; - } - } - - if (!newpeername) { /* Did not find peer in realtime */ - ast_log(LOG_WARNING, "Cannot Determine peer name ip=%s\n", iabuf); - if(peerlist) - ast_config_destroy(peerlist); - else - ast_variables_destroy(var); - return NULL; - } - - /* Peer found in realtime, now build it in memory */ - peer = build_peer(newpeername, var, NULL, 1); - if (!peer) { - if(peerlist) - ast_config_destroy(peerlist); - else - ast_variables_destroy(var); - return NULL; - } - - if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) && !devstate_only) { - /* Cache peer */ - ast_copy_flags(&peer->flags[1],&global_flags[1], SIP_PAGE2_RTAUTOCLEAR|SIP_PAGE2_RTCACHEFRIENDS); - if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR)) { - if (!AST_SCHED_DEL(sched, peer->expire)) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - peer->expire = ast_sched_add(sched, (global_rtautoclear) * 1000, expire_register, ASTOBJ_REF(peer)); - if (peer->expire == -1) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - } - ASTOBJ_CONTAINER_LINK(&peerl,peer); - } - ast_set_flag(&peer->flags[0], SIP_REALTIME); - if(peerlist) - ast_config_destroy(peerlist); - else - ast_variables_destroy(var); - return peer; -} - -/*! \brief Support routine for find_peer */ -static int sip_addrcmp(char *name, struct sockaddr_in *sin) -{ - /* We know name is the first field, so we can cast */ - struct sip_peer *p = (struct sip_peer *) name; - return !(!inaddrcmp(&p->addr, sin) || - (ast_test_flag(&p->flags[0], SIP_INSECURE_PORT) && - (p->addr.sin_addr.s_addr == sin->sin_addr.s_addr))); -} - -/*! \brief Locate peer by name or ip address - * This is used on incoming SIP message to find matching peer on ip - or outgoing message to find matching peer on name */ -static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime, int devstate_only) -{ - struct sip_peer *p = NULL; - - if (peer) - p = ASTOBJ_CONTAINER_FIND(&peerl, peer); - else - p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); - - if (!p && (realtime || devstate_only)) - p = realtime_peer(peer, sin, devstate_only); - - return p; -} - -/*! \brief Remove user object from in-memory storage */ -static void sip_destroy_user(struct sip_user *user) -{ - if (option_debug > 2) - ast_log(LOG_DEBUG, "Destroying user object from memory: %s\n", user->name); - ast_free_ha(user->ha); - if (user->chanvars) { - ast_variables_destroy(user->chanvars); - user->chanvars = NULL; - } - if (ast_test_flag(&user->flags[0], SIP_REALTIME)) - ruserobjs--; - else - suserobjs--; - free(user); -} - -/*! \brief Load user from realtime storage - * Loads user from "sipusers" category in realtime (extconfig.conf) - * Users are matched on From: user name (the domain in skipped) */ -static struct sip_user *realtime_user(const char *username) -{ - struct ast_variable *var; - struct ast_variable *tmp; - struct sip_user *user = NULL; - - var = ast_load_realtime("sipusers", "name", username, NULL); - - if (!var) - return NULL; - - for (tmp = var; tmp; tmp = tmp->next) { - if (!strcasecmp(tmp->name, "type") && - !strcasecmp(tmp->value, "peer")) { - ast_variables_destroy(var); - return NULL; - } - } - - user = build_user(username, var, NULL, !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)); - - if (!user) { /* No user found */ - ast_variables_destroy(var); - return NULL; - } - - if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) { - ast_set_flag(&user->flags[1], SIP_PAGE2_RTCACHEFRIENDS); - suserobjs++; - ASTOBJ_CONTAINER_LINK(&userl,user); - } else { - /* Move counter from s to r... */ - suserobjs--; - ruserobjs++; - } - ast_set_flag(&user->flags[0], SIP_REALTIME); - ast_variables_destroy(var); - return user; -} - -/*! \brief Locate user by name - * Locates user by name (From: sip uri user name part) first - * from in-memory list (static configuration) then from - * realtime storage (defined in extconfig.conf) */ -static struct sip_user *find_user(const char *name, int realtime) -{ - struct sip_user *u = ASTOBJ_CONTAINER_FIND(&userl, name); - if (!u && realtime) - u = realtime_user(name); - return u; -} - -/*! \brief Set nat mode on the various data sockets */ -static void do_setnat(struct sip_pvt *p, int natflags) -{ - const char *mode = natflags ? "On" : "Off"; - - if (p->rtp) { - if (option_debug) - ast_log(LOG_DEBUG, "Setting NAT on RTP to %s\n", mode); - ast_rtp_setnat(p->rtp, natflags); - } - if (p->vrtp) { - if (option_debug) - ast_log(LOG_DEBUG, "Setting NAT on VRTP to %s\n", mode); - ast_rtp_setnat(p->vrtp, natflags); - } - if (p->udptl) { - if (option_debug) - ast_log(LOG_DEBUG, "Setting NAT on UDPTL to %s\n", mode); - ast_udptl_setnat(p->udptl, natflags); - } -} - -/*! \brief Create address structure from peer reference. - * return -1 on error, 0 on success. - */ -static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) -{ - if ((peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr) && - (!peer->maxms || ((peer->lastms >= 0) && (peer->lastms <= peer->maxms)))) { - dialog->sa = (peer->addr.sin_addr.s_addr) ? peer->addr : peer->defaddr; - dialog->recv = dialog->sa; - } else - return -1; - - ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY); - ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); - dialog->capability = peer->capability; - if ((!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) || !(dialog->capability & AST_FORMAT_VIDEO_MASK)) && dialog->vrtp) { - ast_rtp_destroy(dialog->vrtp); - dialog->vrtp = NULL; - } - dialog->prefs = peer->prefs; - if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_T38SUPPORT)) { - dialog->t38.capability = global_t38_capability; - if (dialog->udptl) { - if (ast_udptl_get_error_correction_scheme(dialog->udptl) == UDPTL_ERROR_CORRECTION_FEC ) - dialog->t38.capability |= T38FAX_UDP_EC_FEC; - else if (ast_udptl_get_error_correction_scheme(dialog->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY ) - dialog->t38.capability |= T38FAX_UDP_EC_REDUNDANCY; - else if (ast_udptl_get_error_correction_scheme(dialog->udptl) == UDPTL_ERROR_CORRECTION_NONE ) - dialog->t38.capability |= T38FAX_UDP_EC_NONE; - dialog->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF; - if (option_debug > 1) - ast_log(LOG_DEBUG,"Our T38 capability (%d)\n", dialog->t38.capability); - } - dialog->t38.jointcapability = dialog->t38.capability; - } else if (dialog->udptl) { - ast_udptl_destroy(dialog->udptl); - dialog->udptl = NULL; - } - do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE ); - - if (dialog->rtp) { - ast_rtp_setdtmf(dialog->rtp, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); - ast_rtp_setdtmfcompensate(dialog->rtp, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); - ast_rtp_set_rtptimeout(dialog->rtp, peer->rtptimeout); - ast_rtp_set_rtpholdtimeout(dialog->rtp, peer->rtpholdtimeout); - ast_rtp_set_rtpkeepalive(dialog->rtp, peer->rtpkeepalive); - /* Set Frame packetization */ - ast_rtp_codec_setpref(dialog->rtp, &dialog->prefs); - dialog->autoframing = peer->autoframing; - } - if (dialog->vrtp) { - ast_rtp_setdtmf(dialog->vrtp, 0); - ast_rtp_setdtmfcompensate(dialog->vrtp, 0); - ast_rtp_set_rtptimeout(dialog->vrtp, peer->rtptimeout); - ast_rtp_set_rtpholdtimeout(dialog->vrtp, peer->rtpholdtimeout); - ast_rtp_set_rtpkeepalive(dialog->vrtp, peer->rtpkeepalive); - } - - ast_string_field_set(dialog, peername, peer->name); - ast_string_field_set(dialog, authname, peer->username); - ast_string_field_set(dialog, username, peer->username); - ast_string_field_set(dialog, peersecret, peer->secret); - ast_string_field_set(dialog, peermd5secret, peer->md5secret); - ast_string_field_set(dialog, mohsuggest, peer->mohsuggest); - ast_string_field_set(dialog, mohinterpret, peer->mohinterpret); - ast_string_field_set(dialog, tohost, peer->tohost); - ast_string_field_set(dialog, fullcontact, peer->fullcontact); - if (!dialog->initreq.headers && !ast_strlen_zero(peer->fromdomain)) { - char *tmpcall; - char *c; - tmpcall = ast_strdupa(dialog->callid); - c = strchr(tmpcall, '@'); - if (c) { - *c = '\0'; - ast_string_field_build(dialog, callid, "%s@%s", tmpcall, peer->fromdomain); - } - } - if (ast_strlen_zero(dialog->tohost)) - ast_string_field_set(dialog, tohost, ast_inet_ntoa(dialog->sa.sin_addr)); - if (!ast_strlen_zero(peer->fromdomain)) - ast_string_field_set(dialog, fromdomain, peer->fromdomain); - if (!ast_strlen_zero(peer->fromuser)) - ast_string_field_set(dialog, fromuser, peer->fromuser); - if (!ast_strlen_zero(peer->language)) - ast_string_field_set(dialog, language, peer->language); - dialog->maxtime = peer->maxms; - dialog->callgroup = peer->callgroup; - dialog->pickupgroup = peer->pickupgroup; - dialog->peerauth = peer->auth; - dialog->allowtransfer = peer->allowtransfer; - /* Set timer T1 to RTT for this peer (if known by qualify=) */ - /* Minimum is settable or default to 100 ms */ - if (peer->maxms && peer->lastms) - dialog->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms; - if ((ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || - (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) - dialog->noncodeccapability |= AST_RTP_DTMF; - else - dialog->noncodeccapability &= ~AST_RTP_DTMF; - dialog->jointnoncodeccapability = dialog->noncodeccapability; - ast_string_field_set(dialog, context, peer->context); - dialog->rtptimeout = peer->rtptimeout; - if (peer->call_limit) - ast_set_flag(&dialog->flags[0], SIP_CALL_LIMIT); - dialog->maxcallbitrate = peer->maxcallbitrate; - - return 0; -} - -/*! \brief create address structure from peer name - * Or, if peer not found, find it in the global DNS - * returns TRUE (-1) on failure, FALSE on success */ -static int create_addr(struct sip_pvt *dialog, const char *opeer) -{ - struct hostent *hp; - struct ast_hostent ahp; - struct sip_peer *p; - char *port; - int portno; - char host[MAXHOSTNAMELEN], *hostn; - char peer[256]; - - ast_copy_string(peer, opeer, sizeof(peer)); - port = strchr(peer, ':'); - if (port) - *port++ = '\0'; - dialog->sa.sin_family = AF_INET; - dialog->timer_t1 = 500; /* Default SIP retransmission timer T1 (RFC 3261) */ - p = find_peer(peer, NULL, 1, 0); - - if (p) { - int res = create_addr_from_peer(dialog, p); - if (port) { - portno = atoi(port); - dialog->sa.sin_port = dialog->recv.sin_port = htons(portno); - } - ASTOBJ_UNREF(p, sip_destroy_peer); - return res; - } - hostn = peer; - portno = port ? atoi(port) : STANDARD_SIP_PORT; - if (srvlookup) { - char service[MAXHOSTNAMELEN]; - int tportno; - int ret; - - snprintf(service, sizeof(service), "_sip._udp.%s", peer); - ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service); - if (ret > 0) { - hostn = host; - portno = tportno; - } - } - hp = ast_gethostbyname(hostn, &ahp); - if (!hp) { - ast_log(LOG_WARNING, "No such host: %s\n", peer); - return -1; - } - ast_string_field_set(dialog, tohost, peer); - memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr)); - dialog->sa.sin_port = htons(portno); - dialog->recv = dialog->sa; - return 0; -} - -/*! \brief Scheduled congestion on a call */ -static int auto_congest(const void *nothing) -{ - struct sip_pvt *p = (struct sip_pvt *)nothing; - - ast_mutex_lock(&p->lock); - p->initid = -1; - if (p->owner) { - /* XXX fails on possible deadlock */ - if (!ast_channel_trylock(p->owner)) { - ast_log(LOG_NOTICE, "Auto-congesting %s\n", p->owner->name); - append_history(p, "Cong", "Auto-congesting (timer)"); - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - ast_channel_unlock(p->owner); - } - } - ast_mutex_unlock(&p->lock); - return 0; -} - - -/*! \brief Initiate SIP call from PBX - * used from the dial() application */ -static int sip_call(struct ast_channel *ast, char *dest, int timeout) -{ - int res, xmitres = 0; - struct sip_pvt *p; - struct varshead *headp; - struct ast_var_t *current; - const char *referer = NULL; /* SIP refererer */ - - p = ast->tech_pvt; - if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { - ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name); - return -1; - } - - /* Check whether there is vxml_url, distinctive ring variables */ - headp=&ast->varshead; - AST_LIST_TRAVERSE(headp,current,entries) { - /* Check whether there is a VXML_URL variable */ - if (!p->options->vxml_url && !strcasecmp(ast_var_name(current), "VXML_URL")) { - p->options->vxml_url = ast_var_value(current); - } else if (!p->options->uri_options && !strcasecmp(ast_var_name(current), "SIP_URI_OPTIONS")) { - p->options->uri_options = ast_var_value(current); - } else if (!p->options->distinctive_ring && !strcasecmp(ast_var_name(current), "ALERT_INFO")) { - /* Check whether there is a ALERT_INFO variable */ - p->options->distinctive_ring = ast_var_value(current); - } else if (!p->options->addsipheaders && !strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) { - /* Check whether there is a variable with a name starting with SIPADDHEADER */ - p->options->addsipheaders = 1; - } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER")) { - /* This is a transfered call */ - p->options->transfer = 1; - } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REFERER")) { - /* This is the referer */ - referer = ast_var_value(current); - } else if (!strcasecmp(ast_var_name(current), "SIPTRANSFER_REPLACES")) { - /* We're replacing a call. */ - p->options->replaces = ast_var_value(current); - } else if (!strcasecmp(ast_var_name(current), "T38CALL")) { - p->t38.state = T38_LOCAL_DIRECT; - if (option_debug) - ast_log(LOG_DEBUG,"T38State change to %d on channel %s\n", p->t38.state, ast->name); - } - - } - - res = 0; - ast_set_flag(&p->flags[0], SIP_OUTGOING); - - if (p->options->transfer) { - char buf[SIPBUFSIZE/2]; - - if (referer) { - if (sipdebug && option_debug > 2) - ast_log(LOG_DEBUG, "Call for %s transfered by %s\n", p->username, referer); - snprintf(buf, sizeof(buf)-1, "-> %s (via %s)", p->cid_name, referer); - } else - snprintf(buf, sizeof(buf)-1, "-> %s", p->cid_name); - ast_string_field_set(p, cid_name, buf); - } - if (option_debug) - ast_log(LOG_DEBUG, "Outgoing Call for %s\n", p->username); - - res = update_call_counter(p, INC_CALL_RINGING); - if ( res != -1 ) { - p->callingpres = ast->cid.cid_pres; - p->jointcapability = ast_translate_available_formats(p->capability, p->prefcodec); - p->jointnoncodeccapability = p->noncodeccapability; - - /* If there are no audio formats left to offer, punt */ - if (!(p->jointcapability & AST_FORMAT_AUDIO_MASK)) { - ast_log(LOG_WARNING, "No audio format found to offer. Cancelling call to %s\n", p->username); - res = -1; - } else { - p->t38.jointcapability = p->t38.capability; - if (option_debug > 1) - ast_log(LOG_DEBUG,"Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability); - xmitres = transmit_invite(p, SIP_INVITE, 1, 2); - if (xmitres == XMIT_ERROR) - return -1; /* Transmission error */ - - p->invitestate = INV_CALLING; - - /* Initialize auto-congest time */ - AST_SCHED_DEL(sched, p->initid); - p->initid = ast_sched_add(sched, p->maxtime ? (p->maxtime * 4) : SIP_TRANS_TIMEOUT, auto_congest, p); - } - } else { - ast->hangupcause = AST_CAUSE_USER_BUSY; - } - return res; -} - -/*! \brief Destroy registry object - Objects created with the register= statement in static configuration */ -static void sip_registry_destroy(struct sip_registry *reg) -{ - /* Really delete */ - if (option_debug > 2) - ast_log(LOG_DEBUG, "Destroying registry entry for %s@%s\n", reg->username, reg->hostname); - - if (reg->call) { - /* Clear registry before destroying to ensure - we don't get reentered trying to grab the registry lock */ - reg->call->registry = NULL; - if (option_debug > 2) - ast_log(LOG_DEBUG, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname); - sip_destroy(reg->call); - } - AST_SCHED_DEL(sched, reg->expire); - AST_SCHED_DEL(sched, reg->timeout); - ast_string_field_free_memory(reg); - regobjs--; - free(reg); - -} - -/*! \brief Execute destruction of SIP dialog structure, release memory */ -static int __sip_destroy(struct sip_pvt *p, int lockowner) -{ - struct sip_pvt *cur, *prev = NULL; - struct sip_pkt *cp; - struct sip_request *req; - - /* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */ - if (p->rtp && ast_rtp_get_bridged(p->rtp)) { - ast_verbose("Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text); - return -1; - } - - if (p->vrtp && ast_rtp_get_bridged(p->vrtp)) { - ast_verbose("Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text); - return -1; - } - - if (sip_debug_test_pvt(p) || option_debug > 2) - ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text); - - if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) { - update_call_counter(p, DEC_CALL_LIMIT); - if (option_debug > 1) - ast_log(LOG_DEBUG, "This call did not properly clean up call limits. Call ID %s\n", p->callid); - } - - /* Unlink us from the owner if we have one */ - if (p->owner) { - if (lockowner) - ast_channel_lock(p->owner); - if (option_debug) - ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name); - p->owner->tech_pvt = NULL; - /* Make sure that the channel knows its backend is going away */ - p->owner->_softhangup |= AST_SOFTHANGUP_DEV; - if (lockowner) - ast_channel_unlock(p->owner); - /* Give the channel a chance to react before deallocation */ - usleep(1); - } - - /* Remove link from peer to subscription of MWI */ - if (p->relatedpeer) { - if (p->relatedpeer->mwipvt == p) { - p->relatedpeer->mwipvt = NULL; - } - ASTOBJ_UNREF(p->relatedpeer, sip_destroy_peer); - } - - if (dumphistory) - sip_dump_history(p); - - if (p->options) - free(p->options); - - if (p->stateid > -1) - ast_extension_state_del(p->stateid, NULL); - AST_SCHED_DEL(sched, p->initid); - AST_SCHED_DEL(sched, p->waitid); - AST_SCHED_DEL(sched, p->autokillid); - AST_SCHED_DEL(sched, p->request_queue_sched_id); - - if (p->rtp) { - ast_rtp_destroy(p->rtp); - } - if (p->vrtp) { - ast_rtp_destroy(p->vrtp); - } - if (p->udptl) - ast_udptl_destroy(p->udptl); - if (p->refer) - free(p->refer); - if (p->route) { - free_old_route(p->route); - p->route = NULL; - } - if (p->registry) { - if (p->registry->call == p) - p->registry->call = NULL; - ASTOBJ_UNREF(p->registry, sip_registry_destroy); - } - - /* Clear history */ - if (p->history) { - struct sip_history *hist; - while ( (hist = AST_LIST_REMOVE_HEAD(p->history, list)) ) { - free(hist); - p->history_entries--; - } - free(p->history); - p->history = NULL; - } - - while ((req = AST_LIST_REMOVE_HEAD(&p->request_queue, next))) { - ast_free(req); - } - - for (prev = NULL, cur = iflist; cur; prev = cur, cur = cur->next) { - if (cur == p) { - UNLINK(cur, iflist, prev); - break; - } - } - if (!cur) { - ast_log(LOG_WARNING, "Trying to destroy \"%s\", not found in dialog list?!?! \n", p->callid); - return 0; - } - - /* remove all current packets in this dialog */ - while((cp = p->packets)) { - p->packets = p->packets->next; - AST_SCHED_DEL(sched, cp->retransid); - free(cp); - } - if (p->chanvars) { - ast_variables_destroy(p->chanvars); - p->chanvars = NULL; - } - ast_mutex_destroy(&p->lock); - - ast_string_field_free_memory(p); - - free(p); - return 0; -} - -/*! \brief update_call_counter: Handle call_limit for SIP users - * Setting a call-limit will cause calls above the limit not to be accepted. - * - * Remember that for a type=friend, there's one limit for the user and - * another for the peer, not a combined call limit. - * This will cause unexpected behaviour in subscriptions, since a "friend" - * is *two* devices in Asterisk, not one. - * - * Thought: For realtime, we should propably update storage with inuse counter... - * - * \return 0 if call is ok (no call limit, below treshold) - * -1 on rejection of call - * - */ -static int update_call_counter(struct sip_pvt *fup, int event) -{ - char name[256]; - int *inuse = NULL, *call_limit = NULL, *inringing = NULL; - int outgoing = ast_test_flag(&fup->flags[1], SIP_PAGE2_OUTGOING_CALL); - struct sip_user *u = NULL; - struct sip_peer *p = NULL; - - if (option_debug > 2) - ast_log(LOG_DEBUG, "Updating call counter for %s call\n", outgoing ? "outgoing" : "incoming"); - - /* Test if we need to check call limits, in order to avoid - realtime lookups if we do not need it */ - if (!ast_test_flag(&fup->flags[0], SIP_CALL_LIMIT) && !ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD)) - return 0; - - ast_copy_string(name, fup->username, sizeof(name)); - - /* Check the list of users only for incoming calls */ - if (global_limitonpeers == FALSE && !outgoing && (u = find_user(name, 1))) { - inuse = &u->inUse; - call_limit = &u->call_limit; - inringing = NULL; - } else if ( (p = find_peer(ast_strlen_zero(fup->peername) ? name : fup->peername, NULL, 1, 0) ) ) { /* Try to find peer */ - inuse = &p->inUse; - call_limit = &p->call_limit; - inringing = &p->inRinging; - ast_copy_string(name, fup->peername, sizeof(name)); - } - if (!p && !u) { - if (option_debug > 1) - ast_log(LOG_DEBUG, "%s is not a local device, no call limit\n", name); - return 0; - } - - switch(event) { - /* incoming and outgoing affects the inUse counter */ - case DEC_CALL_LIMIT: - if ( *inuse > 0 ) { - if (ast_test_flag(&fup->flags[0], SIP_INC_COUNT)) { - (*inuse)--; - ast_clear_flag(&fup->flags[0], SIP_INC_COUNT); - } - } else { - *inuse = 0; - } - if (inringing) { - if (ast_test_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING)) { - if (*inringing > 0) - (*inringing)--; - else if (!ast_test_flag(&p->flags[0], SIP_REALTIME) || ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) - ast_log(LOG_WARNING, "Inringing for peer '%s' < 0?\n", fup->peername); - ast_clear_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING); - } - } - if (ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD) && global_notifyhold) { - ast_clear_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD); - sip_peer_hold(fup, 0); - } - if (option_debug > 1 || sipdebug) { - ast_log(LOG_DEBUG, "Call %s %s '%s' removed from call limit %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit); - } - break; - - case INC_CALL_RINGING: - case INC_CALL_LIMIT: - if (*call_limit > 0 ) { - if (*inuse >= *call_limit) { - ast_log(LOG_ERROR, "Call %s %s '%s' rejected due to usage limit of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit); - if (u) - ASTOBJ_UNREF(u, sip_destroy_user); - else - ASTOBJ_UNREF(p, sip_destroy_peer); - return -1; - } - } - if (inringing && (event == INC_CALL_RINGING)) { - if (!ast_test_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING)) { - (*inringing)++; - ast_set_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING); - } - } - /* Continue */ - (*inuse)++; - ast_set_flag(&fup->flags[0], SIP_INC_COUNT); - if (option_debug > 1 || sipdebug) { - ast_log(LOG_DEBUG, "Call %s %s '%s' is %d out of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *inuse, *call_limit); - } - break; - - case DEC_CALL_RINGING: - if (inringing) { - if (ast_test_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING)) { - if (*inringing > 0) - (*inringing)--; - else if (!ast_test_flag(&p->flags[0], SIP_REALTIME) || ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) - ast_log(LOG_WARNING, "Inringing for peer '%s' < 0?\n", p->name); - ast_clear_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING); - } - } - break; - - default: - ast_log(LOG_ERROR, "update_call_counter(%s, %d) called with no event!\n", name, event); - } - if (p) { - ast_device_state_changed("SIP/%s", p->name); - ASTOBJ_UNREF(p, sip_destroy_peer); - } else /* u must be set */ - ASTOBJ_UNREF(u, sip_destroy_user); - return 0; -} - -/*! \brief Destroy SIP call structure */ -static void sip_destroy(struct sip_pvt *p) -{ - ast_mutex_lock(&iflock); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Destroying SIP dialog %s\n", p->callid); - __sip_destroy(p, 1); - ast_mutex_unlock(&iflock); -} - -/*! \brief Convert SIP hangup causes to Asterisk hangup causes */ -static int hangup_sip2cause(int cause) -{ - /* Possible values taken from causes.h */ - - switch(cause) { - case 401: /* Unauthorized */ - return AST_CAUSE_CALL_REJECTED; - case 403: /* Not found */ - return AST_CAUSE_CALL_REJECTED; - case 404: /* Not found */ - return AST_CAUSE_UNALLOCATED; - case 405: /* Method not allowed */ - return AST_CAUSE_INTERWORKING; - case 407: /* Proxy authentication required */ - return AST_CAUSE_CALL_REJECTED; - case 408: /* No reaction */ - return AST_CAUSE_NO_USER_RESPONSE; - case 409: /* Conflict */ - return AST_CAUSE_NORMAL_TEMPORARY_FAILURE; - case 410: /* Gone */ - return AST_CAUSE_UNALLOCATED; - case 411: /* Length required */ - return AST_CAUSE_INTERWORKING; - case 413: /* Request entity too large */ - return AST_CAUSE_INTERWORKING; - case 414: /* Request URI too large */ - return AST_CAUSE_INTERWORKING; - case 415: /* Unsupported media type */ - return AST_CAUSE_INTERWORKING; - case 420: /* Bad extension */ - return AST_CAUSE_NO_ROUTE_DESTINATION; - case 480: /* No answer */ - return AST_CAUSE_NO_ANSWER; - case 481: /* No answer */ - return AST_CAUSE_INTERWORKING; - case 482: /* Loop detected */ - return AST_CAUSE_INTERWORKING; - case 483: /* Too many hops */ - return AST_CAUSE_NO_ANSWER; - case 484: /* Address incomplete */ - return AST_CAUSE_INVALID_NUMBER_FORMAT; - case 485: /* Ambigous */ - return AST_CAUSE_UNALLOCATED; - case 486: /* Busy everywhere */ - return AST_CAUSE_BUSY; - case 487: /* Request terminated */ - return AST_CAUSE_INTERWORKING; - case 488: /* No codecs approved */ - return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; - case 491: /* Request pending */ - return AST_CAUSE_INTERWORKING; - case 493: /* Undecipherable */ - return AST_CAUSE_INTERWORKING; - case 500: /* Server internal failure */ - return AST_CAUSE_FAILURE; - case 501: /* Call rejected */ - return AST_CAUSE_FACILITY_REJECTED; - case 502: - return AST_CAUSE_DESTINATION_OUT_OF_ORDER; - case 503: /* Service unavailable */ - return AST_CAUSE_CONGESTION; - case 504: /* Gateway timeout */ - return AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE; - case 505: /* SIP version not supported */ - return AST_CAUSE_INTERWORKING; - case 600: /* Busy everywhere */ - return AST_CAUSE_USER_BUSY; - case 603: /* Decline */ - return AST_CAUSE_CALL_REJECTED; - case 604: /* Does not exist anywhere */ - return AST_CAUSE_UNALLOCATED; - case 606: /* Not acceptable */ - return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; - default: - return AST_CAUSE_NORMAL; - } - /* Never reached */ - return 0; -} - -/*! \brief Convert Asterisk hangup causes to SIP codes -\verbatim - Possible values from causes.h - AST_CAUSE_NOTDEFINED AST_CAUSE_NORMAL AST_CAUSE_BUSY - AST_CAUSE_FAILURE AST_CAUSE_CONGESTION AST_CAUSE_UNALLOCATED - - In addition to these, a lot of PRI codes is defined in causes.h - ...should we take care of them too ? - - Quote RFC 3398 - - ISUP Cause value SIP response - ---------------- ------------ - 1 unallocated number 404 Not Found - 2 no route to network 404 Not found - 3 no route to destination 404 Not found - 16 normal call clearing --- (*) - 17 user busy 486 Busy here - 18 no user responding 408 Request Timeout - 19 no answer from the user 480 Temporarily unavailable - 20 subscriber absent 480 Temporarily unavailable - 21 call rejected 403 Forbidden (+) - 22 number changed (w/o diagnostic) 410 Gone - 22 number changed (w/ diagnostic) 301 Moved Permanently - 23 redirection to new destination 410 Gone - 26 non-selected user clearing 404 Not Found (=) - 27 destination out of order 502 Bad Gateway - 28 address incomplete 484 Address incomplete - 29 facility rejected 501 Not implemented - 31 normal unspecified 480 Temporarily unavailable -\endverbatim -*/ -static const char *hangup_cause2sip(int cause) -{ - switch (cause) { - case AST_CAUSE_UNALLOCATED: /* 1 */ - case AST_CAUSE_NO_ROUTE_DESTINATION: /* 3 IAX2: Can't find extension in context */ - case AST_CAUSE_NO_ROUTE_TRANSIT_NET: /* 2 */ - return "404 Not Found"; - case AST_CAUSE_CONGESTION: /* 34 */ - case AST_CAUSE_SWITCH_CONGESTION: /* 42 */ - return "503 Service Unavailable"; - case AST_CAUSE_NO_USER_RESPONSE: /* 18 */ - return "408 Request Timeout"; - case AST_CAUSE_NO_ANSWER: /* 19 */ - case AST_CAUSE_UNREGISTERED: /* 20 */ - return "480 Temporarily unavailable"; - case AST_CAUSE_CALL_REJECTED: /* 21 */ - return "403 Forbidden"; - case AST_CAUSE_NUMBER_CHANGED: /* 22 */ - return "410 Gone"; - case AST_CAUSE_NORMAL_UNSPECIFIED: /* 31 */ - return "480 Temporarily unavailable"; - case AST_CAUSE_INVALID_NUMBER_FORMAT: - return "484 Address incomplete"; - case AST_CAUSE_USER_BUSY: - return "486 Busy here"; - case AST_CAUSE_FAILURE: - return "500 Server internal failure"; - case AST_CAUSE_FACILITY_REJECTED: /* 29 */ - return "501 Not Implemented"; - case AST_CAUSE_CHAN_NOT_IMPLEMENTED: - return "503 Service Unavailable"; - /* Used in chan_iax2 */ - case AST_CAUSE_DESTINATION_OUT_OF_ORDER: - return "502 Bad Gateway"; - case AST_CAUSE_BEARERCAPABILITY_NOTAVAIL: /* Can't find codec to connect to host */ - return "488 Not Acceptable Here"; - - case AST_CAUSE_NOTDEFINED: - default: - if (option_debug) - ast_log(LOG_DEBUG, "AST hangup cause %d (no match found in SIP)\n", cause); - return NULL; - } - - /* Never reached */ - return 0; -} - - -/*! \brief sip_hangup: Hangup SIP call - * Part of PBX interface, called from ast_hangup */ -static int sip_hangup(struct ast_channel *ast) -{ - struct sip_pvt *p = ast->tech_pvt; - int needcancel = FALSE; - int needdestroy = 0; - struct ast_channel *oldowner = ast; - - if (!p) { - if (option_debug) - ast_log(LOG_DEBUG, "Asked to hangup channel that was not connected\n"); - return 0; - } - - if (ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) { - if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) { - if (option_debug && sipdebug) - ast_log(LOG_DEBUG, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username); - update_call_counter(p, DEC_CALL_LIMIT); - } - if (option_debug >3) - ast_log(LOG_DEBUG, "SIP Transfer: Not hanging up right now... Rescheduling hangup for %s.\n", p->callid); - if (p->autokillid > -1 && sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Really hang up next time */ - ast_clear_flag(&p->flags[0], SIP_NEEDDESTROY); - p->owner->tech_pvt = NULL; - p->owner = NULL; /* Owner will be gone after we return, so take it away */ - return 0; - } - if (option_debug) { - if (ast_test_flag(ast, AST_FLAG_ZOMBIE) && p->refer && option_debug) - ast_log(LOG_DEBUG, "SIP Transfer: Hanging up Zombie channel %s after transfer ... Call-ID: %s\n", ast->name, p->callid); - else { - if (option_debug) - ast_log(LOG_DEBUG, "Hangup call %s, SIP callid %s)\n", ast->name, p->callid); - } - } - if (option_debug && ast_test_flag(ast, AST_FLAG_ZOMBIE)) - ast_log(LOG_DEBUG, "Hanging up zombie call. Be scared.\n"); - - ast_mutex_lock(&p->lock); - if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) { - if (option_debug && sipdebug) - ast_log(LOG_DEBUG, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username); - update_call_counter(p, DEC_CALL_LIMIT); - } - - /* Determine how to disconnect */ - if (p->owner != ast) { - ast_log(LOG_WARNING, "Huh? We aren't the owner? Can't hangup call.\n"); - ast_mutex_unlock(&p->lock); - return 0; - } - /* If the call is not UP, we need to send CANCEL instead of BYE */ - if (ast->_state == AST_STATE_RING || ast->_state == AST_STATE_RINGING || (p->invitestate < INV_COMPLETED && ast->_state != AST_STATE_UP)) { - needcancel = TRUE; - if (option_debug > 3) - ast_log(LOG_DEBUG, "Hanging up channel in state %s (not UP)\n", ast_state2str(ast->_state)); - } - - stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */ - - append_history(p, needcancel ? "Cancel" : "Hangup", "Cause %s", p->owner ? ast_cause2str(p->owner->hangupcause) : "Unknown"); - - /* Disconnect */ - if (p->vad) - ast_dsp_free(p->vad); - - p->owner = NULL; - ast->tech_pvt = NULL; - - ast_module_unref(ast_module_info->self); - - /* Do not destroy this pvt until we have timeout or - get an answer to the BYE or INVITE/CANCEL - If we get no answer during retransmit period, drop the call anyway. - (Sorry, mother-in-law, you can't deny a hangup by sending - 603 declined to BYE...) - */ - if (ast_test_flag(&p->flags[0], SIP_ALREADYGONE)) - needdestroy = 1; /* Set destroy flag at end of this function */ - else if (p->invitestate != INV_CALLING) - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - - /* Start the process if it's not already started */ - if (!ast_test_flag(&p->flags[0], SIP_ALREADYGONE) && !ast_strlen_zero(p->initreq.data)) { - if (needcancel) { /* Outgoing call, not up */ - if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - /* stop retransmitting an INVITE that has not received a response */ - __sip_pretend_ack(p); - p->invitestate = INV_CANCELLED; - - /* if we can't send right now, mark it pending */ - if (p->invitestate == INV_CALLING) { - /* We can't send anything in CALLING state */ - ast_set_flag(&p->flags[0], SIP_PENDINGBYE); - /* Do we need a timer here if we don't hear from them at all? Yes we do or else we will get hung dialogs and those are no fun. */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - append_history(p, "DELAY", "Not sending cancel, waiting for timeout"); - } else { - /* Send a new request: CANCEL */ - transmit_request(p, SIP_CANCEL, p->lastinvite, XMIT_RELIABLE, FALSE); - /* Actually don't destroy us yet, wait for the 487 on our original - INVITE, but do set an autodestruct just in case we never get it. */ - needdestroy = 0; - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - if ( p->initid != -1 ) { - /* channel still up - reverse dec of inUse counter - only if the channel is not auto-congested */ - update_call_counter(p, INC_CALL_LIMIT); - } - } else { /* Incoming call, not up */ - const char *res; - if (ast->hangupcause && (res = hangup_cause2sip(ast->hangupcause))) - transmit_response_reliable(p, res, &p->initreq); - else - transmit_response_reliable(p, "603 Declined", &p->initreq); - p->invitestate = INV_TERMINATED; - } - } else { /* Call is in UP state, send BYE */ - if (!p->pendinginvite) { - char *audioqos = ""; - char *videoqos = ""; - if (p->rtp) - audioqos = ast_rtp_get_quality(p->rtp, NULL); - if (p->vrtp) - videoqos = ast_rtp_get_quality(p->vrtp, NULL); - /* Send a hangup */ - transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1); - - /* Get RTCP quality before end of call */ - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) { - if (p->rtp) - append_history(p, "RTCPaudio", "Quality:%s", audioqos); - if (p->vrtp) - append_history(p, "RTCPvideo", "Quality:%s", videoqos); - } - if (p->rtp && oldowner) - pbx_builtin_setvar_helper(oldowner, "RTPAUDIOQOS", audioqos); - if (p->vrtp && oldowner) - pbx_builtin_setvar_helper(oldowner, "RTPVIDEOQOS", videoqos); - } else { - /* Note we will need a BYE when this all settles out - but we can't send one while we have "INVITE" outstanding. */ - ast_set_flag(&p->flags[0], SIP_PENDINGBYE); - ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE); - AST_SCHED_DEL(sched, p->waitid); - if (sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - } - } - } - if (needdestroy) - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - ast_mutex_unlock(&p->lock); - return 0; -} - -/*! \brief Try setting codec suggested by the SIP_CODEC channel variable */ -static void try_suggested_sip_codec(struct sip_pvt *p) -{ - int fmt; - const char *codec; - - codec = pbx_builtin_getvar_helper(p->owner, "SIP_CODEC"); - if (!codec) - return; - - fmt = ast_getformatbyname(codec); - if (fmt) { - ast_log(LOG_NOTICE, "Changing codec to '%s' for this call because of ${SIP_CODEC} variable\n", codec); - if (p->jointcapability & fmt) { - p->jointcapability &= fmt; - p->capability &= fmt; - } else - ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because it is not shared by both ends.\n"); - } else - ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because of unrecognized/not configured codec (check allow/disallow in sip.conf): %s\n", codec); - return; -} - -/*! \brief sip_answer: Answer SIP call , send 200 OK on Invite - * Part of PBX interface */ -static int sip_answer(struct ast_channel *ast) -{ - int res = 0; - struct sip_pvt *p = ast->tech_pvt; - - ast_mutex_lock(&p->lock); - if (ast->_state != AST_STATE_UP) { - try_suggested_sip_codec(p); - - ast_setstate(ast, AST_STATE_UP); - if (option_debug) - ast_log(LOG_DEBUG, "SIP answering channel: %s\n", ast->name); - if (p->t38.state == T38_PEER_DIRECT) { - p->t38.state = T38_ENABLED; - if (option_debug > 1) - ast_log(LOG_DEBUG,"T38State change to %d on channel %s\n", p->t38.state, ast->name); - res = transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL); - ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - } else { - res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL); - ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - } - } - ast_mutex_unlock(&p->lock); - return res; -} - -/*! \brief Send frame to media channel (rtp) */ -static int sip_write(struct ast_channel *ast, struct ast_frame *frame) -{ - struct sip_pvt *p = ast->tech_pvt; - int res = 0; - - switch (frame->frametype) { - case AST_FRAME_VOICE: - if (!(frame->subclass & ast->nativeformats)) { - char s1[512], s2[512], s3[512]; - ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %s(%d) read/write = %s(%d)/%s(%d)\n", - frame->subclass, - ast_getformatname_multiple(s1, sizeof(s1) - 1, ast->nativeformats & AST_FORMAT_AUDIO_MASK), - ast->nativeformats & AST_FORMAT_AUDIO_MASK, - ast_getformatname_multiple(s2, sizeof(s2) - 1, ast->readformat), - ast->readformat, - ast_getformatname_multiple(s3, sizeof(s3) - 1, ast->writeformat), - ast->writeformat); - return 0; - } - if (p) { - ast_mutex_lock(&p->lock); - if (p->rtp) { - /* If channel is not up, activate early media session */ - if ((ast->_state != AST_STATE_UP) && - !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && - !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - ast_rtp_new_source(p->rtp); - p->invitestate = INV_EARLY_MEDIA; - transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE); - ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); - } - p->lastrtptx = time(NULL); - res = ast_rtp_write(p->rtp, frame); - } - ast_mutex_unlock(&p->lock); - } - break; - case AST_FRAME_VIDEO: - if (p) { - ast_mutex_lock(&p->lock); - if (p->vrtp) { - /* Activate video early media */ - if ((ast->_state != AST_STATE_UP) && - !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && - !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - p->invitestate = INV_EARLY_MEDIA; - transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE); - ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); - } - p->lastrtptx = time(NULL); - res = ast_rtp_write(p->vrtp, frame); - } - ast_mutex_unlock(&p->lock); - } - break; - case AST_FRAME_IMAGE: - return 0; - break; - case AST_FRAME_MODEM: - if (p) { - ast_mutex_lock(&p->lock); - /* UDPTL requires two-way communication, so early media is not needed here. - we simply forget the frames if we get modem frames before the bridge is up. - Fax will re-transmit. - */ - if (p->udptl && ast->_state == AST_STATE_UP) - res = ast_udptl_write(p->udptl, frame); - ast_mutex_unlock(&p->lock); - } - break; - default: - ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n", frame->frametype); - return 0; - } - - return res; -} - -/*! \brief sip_fixup: Fix up a channel: If a channel is consumed, this is called. - Basically update any ->owner links */ -static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - int ret = -1; - struct sip_pvt *p; - - if (newchan && ast_test_flag(newchan, AST_FLAG_ZOMBIE) && option_debug) - ast_log(LOG_DEBUG, "New channel is zombie\n"); - if (oldchan && ast_test_flag(oldchan, AST_FLAG_ZOMBIE) && option_debug) - ast_log(LOG_DEBUG, "Old channel is zombie\n"); - - if (!newchan || !newchan->tech_pvt) { - if (!newchan) - ast_log(LOG_WARNING, "No new channel! Fixup of %s failed.\n", oldchan->name); - else - ast_log(LOG_WARNING, "No SIP tech_pvt! Fixup of %s failed.\n", oldchan->name); - return -1; - } - p = newchan->tech_pvt; - - if (!p) { - ast_log(LOG_WARNING, "No pvt after masquerade. Strange things may happen\n"); - return -1; - } - - ast_mutex_lock(&p->lock); - append_history(p, "Masq", "Old channel: %s\n", oldchan->name); - append_history(p, "Masq (cont)", "...new owner: %s\n", newchan->name); - if (p->owner != oldchan) - ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner); - else { - p->owner = newchan; - /* Re-invite RTP back to Asterisk. Needed if channel is masqueraded out of a native - RTP bridge (i.e., RTP not going through Asterisk): RTP bridge code might not be - able to do this if the masquerade happens before the bridge breaks (e.g., AMI - redirect of both channels). Note that a channel can not be masqueraded *into* - a native bridge. So there is no danger that this breaks a native bridge that - should stay up. */ - sip_set_rtp_peer(newchan, NULL, NULL, 0, 0); - ret = 0; - } - if (option_debug > 2) - ast_log(LOG_DEBUG, "SIP Fixup: New owner for dialogue %s: %s (Old parent: %s)\n", p->callid, p->owner->name, oldchan->name); - - ast_mutex_unlock(&p->lock); - return ret; -} - -static int sip_senddigit_begin(struct ast_channel *ast, char digit) -{ - struct sip_pvt *p = ast->tech_pvt; - int res = 0; - - ast_mutex_lock(&p->lock); - switch (ast_test_flag(&p->flags[0], SIP_DTMF)) { - case SIP_DTMF_INBAND: - res = -1; /* Tell Asterisk to generate inband indications */ - break; - case SIP_DTMF_RFC2833: - if (p->rtp) - ast_rtp_senddigit_begin(p->rtp, digit); - break; - default: - break; - } - ast_mutex_unlock(&p->lock); - - return res; -} - -/*! \brief Send DTMF character on SIP channel - within one call, we're able to transmit in many methods simultaneously */ -static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ - struct sip_pvt *p = ast->tech_pvt; - int res = 0; - - ast_mutex_lock(&p->lock); - switch (ast_test_flag(&p->flags[0], SIP_DTMF)) { - case SIP_DTMF_INFO: - transmit_info_with_digit(p, digit, duration); - break; - case SIP_DTMF_RFC2833: - if (p->rtp) - ast_rtp_senddigit_end(p->rtp, digit); - break; - case SIP_DTMF_INBAND: - res = -1; /* Tell Asterisk to stop inband indications */ - break; - } - ast_mutex_unlock(&p->lock); - - return res; -} - -/*! \brief Transfer SIP call */ -static int sip_transfer(struct ast_channel *ast, const char *dest) -{ - struct sip_pvt *p = ast->tech_pvt; - int res; - - if (dest == NULL) /* functions below do not take a NULL */ - dest = ""; - ast_mutex_lock(&p->lock); - if (ast->_state == AST_STATE_RING) - res = sip_sipredirect(p, dest); - else - res = transmit_refer(p, dest); - ast_mutex_unlock(&p->lock); - return res; -} - -/*! \brief Play indication to user - * With SIP a lot of indications is sent as messages, letting the device play - the indication - busy signal, congestion etc - \return -1 to force ast_indicate to send indication in audio, 0 if SIP can handle the indication by sending a message -*/ -static int sip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen) -{ - struct sip_pvt *p = ast->tech_pvt; - int res = 0; - - ast_mutex_lock(&p->lock); - switch(condition) { - case AST_CONTROL_RINGING: - if (ast->_state == AST_STATE_RING) { - p->invitestate = INV_EARLY_MEDIA; - if (!ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) || - (ast_test_flag(&p->flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER)) { - /* Send 180 ringing if out-of-band seems reasonable */ - transmit_response(p, "180 Ringing", &p->initreq); - ast_set_flag(&p->flags[0], SIP_RINGING); - if (ast_test_flag(&p->flags[0], SIP_PROG_INBAND) != SIP_PROG_INBAND_YES) - break; - } else { - /* Well, if it's not reasonable, just send in-band */ - } - } - res = -1; - break; - case AST_CONTROL_BUSY: - if (ast->_state != AST_STATE_UP) { - transmit_response_reliable(p, "486 Busy Here", &p->initreq); - p->invitestate = INV_COMPLETED; - sip_alreadygone(p); - ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV); - break; - } - res = -1; - break; - case AST_CONTROL_CONGESTION: - if (ast->_state != AST_STATE_UP) { - transmit_response_reliable(p, "503 Service Unavailable", &p->initreq); - p->invitestate = INV_COMPLETED; - sip_alreadygone(p); - ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV); - break; - } - res = -1; - break; - case AST_CONTROL_PROCEEDING: - if ((ast->_state != AST_STATE_UP) && - !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && - !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - transmit_response(p, "100 Trying", &p->initreq); - p->invitestate = INV_PROCEEDING; - break; - } - res = -1; - break; - case AST_CONTROL_PROGRESS: - if ((ast->_state != AST_STATE_UP) && - !ast_test_flag(&p->flags[0], SIP_PROGRESS_SENT) && - !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - p->invitestate = INV_EARLY_MEDIA; - transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, XMIT_UNRELIABLE); - ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); - break; - } - res = -1; - break; - case AST_CONTROL_HOLD: - ast_rtp_new_source(p->rtp); - ast_moh_start(ast, data, p->mohinterpret); - break; - case AST_CONTROL_UNHOLD: - ast_rtp_new_source(p->rtp); - ast_moh_stop(ast); - break; - case AST_CONTROL_VIDUPDATE: /* Request a video frame update */ - if (p->vrtp && !ast_test_flag(&p->flags[0], SIP_NOVIDEO)) { - transmit_info_with_vidupdate(p); - /* ast_rtcp_send_h261fur(p->vrtp); */ - } else - res = -1; - break; - case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(p->rtp); - break; - case -1: - res = -1; - break; - default: - ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", condition); - res = -1; - break; - } - ast_mutex_unlock(&p->lock); - return res; -} - - -/*! \brief Initiate a call in the SIP channel - called from sip_request_call (calls from the pbx ) for outbound channels - and from handle_request_invite for inbound channels - -*/ -static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *title) -{ - struct ast_channel *tmp; - struct ast_variable *v = NULL; - int fmt; - int what; - int needvideo = 0, video = 0; - char *decoded_exten; - { - const char *my_name; /* pick a good name */ - - if (title) - my_name = title; - else if ( (my_name = strchr(i->fromdomain,':')) ) - my_name++; /* skip ':' */ - else - my_name = i->fromdomain; - - ast_mutex_unlock(&i->lock); - /* Don't hold a sip pvt lock while we allocate a channel */ - tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, i->accountcode, i->exten, i->context, i->amaflags, "SIP/%s-%08x", my_name, (int)(long) i); - - } - if (!tmp) { - ast_log(LOG_WARNING, "Unable to allocate AST channel structure for SIP channel\n"); - ast_mutex_lock(&i->lock); - return NULL; - } - ast_mutex_lock(&i->lock); - - if (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INFO) - tmp->tech = &sip_tech_info; - else - tmp->tech = &sip_tech; - - /* Select our native format based on codec preference until we receive - something from another device to the contrary. */ - if (i->jointcapability) { /* The joint capabilities of us and peer */ - what = i->jointcapability; - video = i->jointcapability & AST_FORMAT_VIDEO_MASK; - } else if (i->capability) { /* Our configured capability for this peer */ - what = i->capability; - video = i->capability & AST_FORMAT_VIDEO_MASK; - } else { - what = global_capability; /* Global codec support */ - video = global_capability & AST_FORMAT_VIDEO_MASK; - } - - /* Set the native formats for audio and merge in video */ - tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | video; - if (option_debug > 2) { - char buf[SIPBUFSIZE]; - ast_log(LOG_DEBUG, "*** Our native formats are %s \n", ast_getformatname_multiple(buf, SIPBUFSIZE, tmp->nativeformats)); - ast_log(LOG_DEBUG, "*** Joint capabilities are %s \n", ast_getformatname_multiple(buf, SIPBUFSIZE, i->jointcapability)); - ast_log(LOG_DEBUG, "*** Our capabilities are %s \n", ast_getformatname_multiple(buf, SIPBUFSIZE, i->capability)); - ast_log(LOG_DEBUG, "*** AST_CODEC_CHOOSE formats are %s \n", ast_getformatname_multiple(buf, SIPBUFSIZE, ast_codec_choose(&i->prefs, what, 1))); - if (i->prefcodec) - ast_log(LOG_DEBUG, "*** Our preferred formats from the incoming channel are %s \n", ast_getformatname_multiple(buf, SIPBUFSIZE, i->prefcodec)); - } - - /* XXX Why are we choosing a codec from the native formats?? */ - fmt = ast_best_codec(tmp->nativeformats); - - /* If we have a prefcodec setting, we have an inbound channel that set a - preferred format for this call. Otherwise, we check the jointcapability - We also check for vrtp. If it's not there, we are not allowed do any video anyway. - */ - if (i->vrtp) { - if (i->prefcodec) - needvideo = i->prefcodec & AST_FORMAT_VIDEO_MASK; /* Outbound call */ - else - needvideo = i->jointcapability & AST_FORMAT_VIDEO_MASK; /* Inbound call */ - } - - if (option_debug > 2) { - if (needvideo) - ast_log(LOG_DEBUG, "This channel can handle video! HOLLYWOOD next!\n"); - else - ast_log(LOG_DEBUG, "This channel will not be able to handle video.\n"); - } - - - - if (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) { - i->vad = ast_dsp_new(); - ast_dsp_set_features(i->vad, DSP_FEATURE_DTMF_DETECT); - if (global_relaxdtmf) - ast_dsp_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); - } - if (i->rtp) { - tmp->fds[0] = ast_rtp_fd(i->rtp); - tmp->fds[1] = ast_rtcp_fd(i->rtp); - } - if (needvideo && i->vrtp) { - tmp->fds[2] = ast_rtp_fd(i->vrtp); - tmp->fds[3] = ast_rtcp_fd(i->vrtp); - } - if (i->udptl) { - tmp->fds[5] = ast_udptl_fd(i->udptl); - } - if (state == AST_STATE_RING) - tmp->rings = 1; - tmp->adsicpe = AST_ADSI_UNAVAILABLE; - tmp->writeformat = fmt; - tmp->rawwriteformat = fmt; - tmp->readformat = fmt; - tmp->rawreadformat = fmt; - tmp->tech_pvt = i; - - tmp->callgroup = i->callgroup; - tmp->pickupgroup = i->pickupgroup; - tmp->cid.cid_pres = i->callingpres; - if (!ast_strlen_zero(i->accountcode)) - ast_string_field_set(tmp, accountcode, i->accountcode); - if (i->amaflags) - tmp->amaflags = i->amaflags; - if (!ast_strlen_zero(i->language)) - ast_string_field_set(tmp, language, i->language); - i->owner = tmp; - ast_module_ref(ast_module_info->self); - ast_copy_string(tmp->context, i->context, sizeof(tmp->context)); - /*Since it is valid to have extensions in the dialplan that have unescaped characters in them - * we should decode the uri before storing it in the channel, but leave it encoded in the sip_pvt - * structure so that there aren't issues when forming URI's - */ - decoded_exten = ast_strdupa(i->exten); - ast_uri_decode(decoded_exten); - ast_copy_string(tmp->exten, decoded_exten, sizeof(tmp->exten)); - - /* Don't use ast_set_callerid() here because it will - * generate an unnecessary NewCallerID event */ - tmp->cid.cid_ani = ast_strdup(i->cid_num); - if (!ast_strlen_zero(i->rdnis)) - tmp->cid.cid_rdnis = ast_strdup(i->rdnis); - - if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s")) - tmp->cid.cid_dnid = ast_strdup(i->exten); - - tmp->priority = 1; - if (!ast_strlen_zero(i->uri)) - pbx_builtin_setvar_helper(tmp, "SIPURI", i->uri); - if (!ast_strlen_zero(i->domain)) - pbx_builtin_setvar_helper(tmp, "SIPDOMAIN", i->domain); - if (!ast_strlen_zero(i->useragent)) - pbx_builtin_setvar_helper(tmp, "SIPUSERAGENT", i->useragent); - if (!ast_strlen_zero(i->callid)) - pbx_builtin_setvar_helper(tmp, "SIPCALLID", i->callid); - if (i->rtp) - ast_jb_configure(tmp, &global_jbconf); - - /* If the INVITE contains T.38 SDP information set the proper channel variable so a created outgoing call will also have T.38 */ - if (i->udptl && i->t38.state == T38_PEER_DIRECT) - pbx_builtin_setvar_helper(tmp, "_T38CALL", "1"); - - /* Set channel variables for this call from configuration */ - for (v = i->chanvars ; v ; v = v->next) - pbx_builtin_setvar_helper(tmp, v->name, v->value); - - if (state != AST_STATE_DOWN && ast_pbx_start(tmp)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); - tmp->hangupcause = AST_CAUSE_SWITCH_CONGESTION; - ast_hangup(tmp); - tmp = NULL; - } - - if (!ast_test_flag(&i->flags[0], SIP_NO_HISTORY)) - append_history(i, "NewChan", "Channel %s - from %s", tmp->name, i->callid); - - return tmp; -} - -/*! \brief Reads one line of SIP message body */ -static char *get_body_by_line(const char *line, const char *name, int nameLen) -{ - if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=') - return ast_skip_blanks(line + nameLen + 1); - - return ""; -} - -/*! \brief Lookup 'name' in the SDP starting - * at the 'start' line. Returns the matching line, and 'start' - * is updated with the next line number. - */ -static const char *get_sdp_iterate(int *start, struct sip_request *req, const char *name) -{ - int len = strlen(name); - - while (*start < req->sdp_end) { - const char *r = get_body_by_line(req->line[(*start)++], name, len); - if (r[0] != '\0') - return r; - } - - return ""; -} - -/*! \brief Get a line from an SDP message body */ -static const char *get_sdp(struct sip_request *req, const char *name) -{ - int dummy = 0; - - return get_sdp_iterate(&dummy, req, name); -} - -/*! \brief Get a specific line from the message body */ -static char *get_body(struct sip_request *req, char *name) -{ - int x; - int len = strlen(name); - char *r; - - for (x = 0; x < req->lines; x++) { - r = get_body_by_line(req->line[x], name, len); - if (r[0] != '\0') - return r; - } - - return ""; -} - -/*! \brief Find compressed SIP alias */ -static const char *find_alias(const char *name, const char *_default) -{ - /*! \brief Structure for conversion between compressed SIP and "normal" SIP */ - static const struct cfalias { - char * const fullname; - char * const shortname; - } aliases[] = { - { "Content-Type", "c" }, - { "Content-Encoding", "e" }, - { "From", "f" }, - { "Call-ID", "i" }, - { "Contact", "m" }, - { "Content-Length", "l" }, - { "Subject", "s" }, - { "To", "t" }, - { "Supported", "k" }, - { "Refer-To", "r" }, - { "Referred-By", "b" }, - { "Allow-Events", "u" }, - { "Event", "o" }, - { "Via", "v" }, - { "Accept-Contact", "a" }, - { "Reject-Contact", "j" }, - { "Request-Disposition", "d" }, - { "Session-Expires", "x" }, - { "Identity", "y" }, - { "Identity-Info", "n" }, - }; - int x; - - for (x=0; x<sizeof(aliases) / sizeof(aliases[0]); x++) - if (!strcasecmp(aliases[x].fullname, name)) - return aliases[x].shortname; - - return _default; -} - -static const char *__get_header(const struct sip_request *req, const char *name, int *start) -{ - int pass; - - /* - * Technically you can place arbitrary whitespace both before and after the ':' in - * a header, although RFC3261 clearly says you shouldn't before, and place just - * one afterwards. If you shouldn't do it, what absolute idiot decided it was - * a good idea to say you can do it, and if you can do it, why in the hell would. - * you say you shouldn't. - * Anyways, pedanticsipchecking controls whether we allow spaces before ':', - * and we always allow spaces after that for compatibility. - */ - for (pass = 0; name && pass < 2;pass++) { - int x, len = strlen(name); - for (x=*start; x<req->headers; x++) { - if (!strncasecmp(req->header[x], name, len)) { - char *r = req->header[x] + len; /* skip name */ - if (pedanticsipchecking) - r = ast_skip_blanks(r); - - if (*r == ':') { - *start = x+1; - return ast_skip_blanks(r+1); - } - } - } - if (pass == 0) /* Try aliases */ - name = find_alias(name, NULL); - } - - /* Don't return NULL, so get_header is always a valid pointer */ - return ""; -} - -/*! \brief Get header from SIP request */ -static const char *get_header(const struct sip_request *req, const char *name) -{ - int start = 0; - return __get_header(req, name, &start); -} - -/*! \brief Read RTP from network */ -static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect) -{ - /* Retrieve audio/etc from channel. Assumes p->lock is already held. */ - struct ast_frame *f; - - if (!p->rtp) { - /* We have no RTP allocated for this channel */ - return &ast_null_frame; - } - - switch(ast->fdno) { - case 0: - f = ast_rtp_read(p->rtp); /* RTP Audio */ - break; - case 1: - f = ast_rtcp_read(p->rtp); /* RTCP Control Channel */ - break; - case 2: - f = ast_rtp_read(p->vrtp); /* RTP Video */ - break; - case 3: - f = ast_rtcp_read(p->vrtp); /* RTCP Control Channel for video */ - break; - case 5: - f = ast_udptl_read(p->udptl); /* UDPTL for T.38 */ - break; - default: - f = &ast_null_frame; - } - /* Don't forward RFC2833 if we're not supposed to */ - if (f && (f->frametype == AST_FRAME_DTMF) && - (ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_RFC2833)) - return &ast_null_frame; - - /* We already hold the channel lock */ - if (!p->owner || (f && f->frametype != AST_FRAME_VOICE)) - return f; - - if (f && f->subclass != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) { - if (!(f->subclass & p->jointcapability)) { - if (option_debug) { - ast_log(LOG_DEBUG, "Bogus frame of format '%s' received from '%s'!\n", - ast_getformatname(f->subclass), p->owner->name); - } - return &ast_null_frame; - } - if (option_debug) - ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass); - p->owner->nativeformats = (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass; - ast_set_read_format(p->owner, p->owner->readformat); - ast_set_write_format(p->owner, p->owner->writeformat); - } - - if (f && (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) { - f = ast_dsp_process(p->owner, p->vad, f); - if (f && f->frametype == AST_FRAME_DTMF) { - if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && f->subclass == 'f') { - if (option_debug) - ast_log(LOG_DEBUG, "Fax CNG detected on %s\n", ast->name); - *faxdetect = 1; - } else if (option_debug) { - ast_log(LOG_DEBUG, "* Detected inband DTMF '%c'\n", f->subclass); - } - } - } - - return f; -} - -/*! \brief Read SIP RTP from channel */ -static struct ast_frame *sip_read(struct ast_channel *ast) -{ - struct ast_frame *fr; - struct sip_pvt *p = ast->tech_pvt; - int faxdetected = FALSE; - - ast_mutex_lock(&p->lock); - fr = sip_rtp_read(ast, p, &faxdetected); - p->lastrtprx = time(NULL); - - /* If we are NOT bridged to another channel, and we have detected fax tone we issue T38 re-invite to a peer */ - /* If we are bridged then it is the responsibility of the SIP device to issue T38 re-invite if it detects CNG or fax preamble */ - if (faxdetected && ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) { - if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) { - if (!p->pendinginvite) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Sending reinvite on SIP (%s) for T.38 negotiation.\n",ast->name); - p->t38.state = T38_LOCAL_REINVITE; - transmit_reinvite_with_t38_sdp(p); - if (option_debug > 1) - ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, ast->name); - } - } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Deferring reinvite on SIP (%s) - it will be re-negotiated for T.38\n", ast->name); - ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); - } - } - - /* Only allow audio through if they sent progress with SDP, or if the channel is actually answered */ - if (fr && fr->frametype == AST_FRAME_VOICE && p->invitestate != INV_EARLY_MEDIA && ast->_state != AST_STATE_UP) { - fr = &ast_null_frame; - } - - ast_mutex_unlock(&p->lock); - return fr; -} - - -/*! \brief Generate 32 byte random string for callid's etc */ -static char *generate_random_string(char *buf, size_t size) -{ - long val[4]; - int x; - - for (x=0; x<4; x++) - val[x] = ast_random(); - snprintf(buf, size, "%08lx%08lx%08lx%08lx", val[0], val[1], val[2], val[3]); - - return buf; -} - -/*! \brief Build SIP Call-ID value for a non-REGISTER transaction */ -static void build_callid_pvt(struct sip_pvt *pvt) -{ - char buf[33]; - - const char *host = S_OR(pvt->fromdomain, ast_inet_ntoa(pvt->ourip)); - - ast_string_field_build(pvt, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host); - -} - -/*! \brief Build SIP Call-ID value for a REGISTER transaction */ -static void build_callid_registry(struct sip_registry *reg, struct in_addr ourip, const char *fromdomain) -{ - char buf[33]; - - const char *host = S_OR(fromdomain, ast_inet_ntoa(ourip)); - - ast_string_field_build(reg, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host); -} - -/*! \brief Make our SIP dialog tag */ -static void make_our_tag(char *tagbuf, size_t len) -{ - snprintf(tagbuf, len, "as%08lx", ast_random()); -} - -/*! \brief Allocate SIP_PVT structure and set defaults */ -static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin, - int useglobal_nat, const int intended_method) -{ - struct sip_pvt *p; - - if (!(p = ast_calloc(1, sizeof(*p)))) - return NULL; - - if (ast_string_field_init(p, 512)) { - free(p); - return NULL; - } - - ast_mutex_init(&p->lock); - - p->method = intended_method; - p->initid = -1; - p->waitid = -1; - p->autokillid = -1; - p->request_queue_sched_id = -1; - p->subscribed = NONE; - p->stateid = -1; - p->prefs = default_prefs; /* Set default codecs for this call */ - - if (intended_method != SIP_OPTIONS) /* Peerpoke has it's own system */ - p->timer_t1 = 500; /* Default SIP retransmission timer T1 (RFC 3261) */ - - if (sin) { - p->sa = *sin; - if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip)) - p->ourip = __ourip; - } else - p->ourip = __ourip; - - /* Copy global flags to this PVT at setup. */ - ast_copy_flags(&p->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY); - ast_copy_flags(&p->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY); - - ast_set2_flag(&p->flags[0], !recordhistory, SIP_NO_HISTORY); - - p->branch = ast_random(); - make_our_tag(p->tag, sizeof(p->tag)); - p->ocseq = INITIAL_CSEQ; - - if (sip_methods[intended_method].need_rtp) { - p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - /* If the global videosupport flag is on, we always create a RTP interface for video */ - if (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT)) - p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) - p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr); - if (!p->rtp || (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp)) { - ast_log(LOG_WARNING, "Unable to create RTP audio %s session: %s\n", - ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video" : "", strerror(errno)); - ast_mutex_destroy(&p->lock); - if (p->chanvars) { - ast_variables_destroy(p->chanvars); - p->chanvars = NULL; - } - free(p); - return NULL; - } - ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); - ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); - ast_rtp_settos(p->rtp, global_tos_audio); - ast_rtp_set_rtptimeout(p->rtp, global_rtptimeout); - ast_rtp_set_rtpholdtimeout(p->rtp, global_rtpholdtimeout); - ast_rtp_set_rtpkeepalive(p->rtp, global_rtpkeepalive); - if (p->vrtp) { - ast_rtp_settos(p->vrtp, global_tos_video); - ast_rtp_setdtmf(p->vrtp, 0); - ast_rtp_setdtmfcompensate(p->vrtp, 0); - ast_rtp_set_rtptimeout(p->vrtp, global_rtptimeout); - ast_rtp_set_rtpholdtimeout(p->vrtp, global_rtpholdtimeout); - ast_rtp_set_rtpkeepalive(p->vrtp, global_rtpkeepalive); - } - if (p->udptl) - ast_udptl_settos(p->udptl, global_tos_audio); - p->maxcallbitrate = default_maxcallbitrate; - p->autoframing = global_autoframing; - ast_rtp_codec_setpref(p->rtp, &p->prefs); - } - - if (useglobal_nat && sin) { - /* Setup NAT structure according to global settings if we have an address */ - ast_copy_flags(&p->flags[0], &global_flags[0], SIP_NAT); - p->recv = *sin; - do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE); - } - - if (p->method != SIP_REGISTER) - ast_string_field_set(p, fromdomain, default_fromdomain); - build_via(p); - if (!callid) - build_callid_pvt(p); - else - ast_string_field_set(p, callid, callid); - /* Assign default music on hold class */ - ast_string_field_set(p, mohinterpret, default_mohinterpret); - ast_string_field_set(p, mohsuggest, default_mohsuggest); - p->capability = global_capability; - p->allowtransfer = global_allowtransfer; - if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || - (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) - p->noncodeccapability |= AST_RTP_DTMF; - if (p->udptl) { - p->t38.capability = global_t38_capability; - if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY) - p->t38.capability |= T38FAX_UDP_EC_REDUNDANCY; - else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_FEC) - p->t38.capability |= T38FAX_UDP_EC_FEC; - else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_NONE) - p->t38.capability |= T38FAX_UDP_EC_NONE; - p->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF; - p->t38.jointcapability = p->t38.capability; - } - ast_string_field_set(p, context, default_context); - - AST_LIST_HEAD_INIT_NOLOCK(&p->request_queue); - - /* Add to active dialog list */ - ast_mutex_lock(&iflock); - p->next = iflist; - iflist = p; - ast_mutex_unlock(&iflock); - if (option_debug) - ast_log(LOG_DEBUG, "Allocating new SIP dialog for %s - %s (%s)\n", callid ? callid : "(No Call-ID)", sip_methods[intended_method].text, p->rtp ? "With RTP" : "No RTP"); - return p; -} - -/*! \brief Connect incoming SIP message to current dialog or create new dialog structure - Called by handle_request, sipsock_read */ -static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method) -{ - struct sip_pvt *p = NULL; - char *tag = ""; /* note, tag is never NULL */ - char totag[128]; - char fromtag[128]; - const char *callid = get_header(req, "Call-ID"); - const char *from = get_header(req, "From"); - const char *to = get_header(req, "To"); - const char *cseq = get_header(req, "Cseq"); - - /* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */ - /* get_header always returns non-NULL so we must use ast_strlen_zero() */ - if (ast_strlen_zero(callid) || ast_strlen_zero(to) || - ast_strlen_zero(from) || ast_strlen_zero(cseq)) - return NULL; /* Invalid packet */ - - if (pedanticsipchecking) { - /* In principle Call-ID's uniquely identify a call, but with a forking SIP proxy - we need more to identify a branch - so we have to check branch, from - and to tags to identify a call leg. - For Asterisk to behave correctly, you need to turn on pedanticsipchecking - in sip.conf - */ - if (gettag(req, "To", totag, sizeof(totag))) - ast_set_flag(req, SIP_PKT_WITH_TOTAG); /* Used in handle_request/response */ - gettag(req, "From", fromtag, sizeof(fromtag)); - - tag = (req->method == SIP_RESPONSE) ? totag : fromtag; - - if (option_debug > 4 ) - ast_log(LOG_DEBUG, "= Looking for Call ID: %s (Checking %s) --From tag %s --To-tag %s \n", callid, req->method==SIP_RESPONSE ? "To" : "From", fromtag, totag); - } - - ast_mutex_lock(&iflock); - for (p = iflist; p; p = p->next) { - /* In pedantic, we do not want packets with bad syntax to be connected to a PVT */ - int found = FALSE; - if (ast_strlen_zero(p->callid)) - continue; - if (req->method == SIP_REGISTER) - found = (!strcmp(p->callid, callid)); - else { - found = !strcmp(p->callid, callid); - if (pedanticsipchecking && found) { - found = ast_strlen_zero(tag) || ast_strlen_zero(p->theirtag) || !ast_test_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED) || !strcmp(p->theirtag, tag); - } - } - - if (option_debug > 4) - ast_log(LOG_DEBUG, "= %s Their Call ID: %s Their Tag %s Our tag: %s\n", found ? "Found" : "No match", p->callid, p->theirtag, p->tag); - - /* If we get a new request within an existing to-tag - check the to tag as well */ - if (pedanticsipchecking && found && req->method != SIP_RESPONSE) { /* SIP Request */ - if (p->tag[0] == '\0' && totag[0]) { - /* We have no to tag, but they have. Wrong dialog */ - found = FALSE; - } else if (totag[0]) { /* Both have tags, compare them */ - if (strcmp(totag, p->tag)) { - found = FALSE; /* This is not our packet */ - } - } - if (!found && option_debug > 4) - ast_log(LOG_DEBUG, "= Being pedantic: This is not our match on request: Call ID: %s Ourtag <null> Totag %s Method %s\n", p->callid, totag, sip_methods[req->method].text); - } - if (found) { - /* Found the call */ - ast_mutex_lock(&p->lock); - ast_mutex_unlock(&iflock); - return p; - } - } - ast_mutex_unlock(&iflock); - - /* See if the method is capable of creating a dialog */ - if (sip_methods[intended_method].can_create == CAN_CREATE_DIALOG) { - if (intended_method == SIP_REFER) { - /* We do support REFER, but not outside of a dialog yet */ - transmit_response_using_temp(callid, sin, 1, intended_method, req, "603 Declined (no dialog)"); - } else if (intended_method == SIP_NOTIFY) { - /* We do not support out-of-dialog NOTIFY either, - like voicemail notification, so cancel that early */ - transmit_response_using_temp(callid, sin, 1, intended_method, req, "489 Bad event"); - } else { - /* Ok, time to create a new SIP dialog object, a pvt */ - if ((p = sip_alloc(callid, sin, 1, intended_method))) { - /* Ok, we've created a dialog, let's go and process it */ - ast_mutex_lock(&p->lock); - } else { - /* We have a memory or file/socket error (can't allocate RTP sockets or something) so we're not - getting a dialog from sip_alloc. - - Without a dialog we can't retransmit and handle ACKs and all that, but at least - send an error message. - - Sorry, we apologize for the inconvienience - */ - transmit_response_using_temp(callid, sin, 1, intended_method, req, "500 Server internal error"); - if (option_debug > 3) - ast_log(LOG_DEBUG, "Failed allocating SIP dialog, sending 500 Server internal error and giving up\n"); - } - } - return p; - } else if( sip_methods[intended_method].can_create == CAN_CREATE_DIALOG_UNSUPPORTED_METHOD) { - /* A method we do not support, let's take it on the volley */ - transmit_response_using_temp(callid, sin, 1, intended_method, req, "501 Method Not Implemented"); - } else if (intended_method != SIP_RESPONSE && intended_method != SIP_ACK) { - /* This is a request outside of a dialog that we don't know about - ...never reply to an ACK! - */ - transmit_response_using_temp(callid, sin, 1, intended_method, req, "481 Call leg/transaction does not exist"); - } - /* We do not respond to responses for dialogs that we don't know about, we just drop - the session quickly */ - - return p; -} - -/*! \brief Parse register=> line in sip.conf and add to registry */ -static int sip_register(char *value, int lineno) -{ - struct sip_registry *reg; - int portnum = 0; - char username[256] = ""; - char *hostname=NULL, *secret=NULL, *authuser=NULL; - char *porta=NULL; - char *contact=NULL; - - if (!value) - return -1; - ast_copy_string(username, value, sizeof(username)); - /* First split around the last '@' then parse the two components. */ - hostname = strrchr(username, '@'); /* allow @ in the first part */ - if (hostname) - *hostname++ = '\0'; - if (ast_strlen_zero(username) || ast_strlen_zero(hostname)) { - ast_log(LOG_WARNING, "Format for registration is user[:secret[:authuser]]@host[:port][/contact] at line %d\n", lineno); - return -1; - } - /* split user[:secret[:authuser]] */ - secret = strchr(username, ':'); - if (secret) { - *secret++ = '\0'; - authuser = strchr(secret, ':'); - if (authuser) - *authuser++ = '\0'; - } - /* split host[:port][/contact] */ - contact = strchr(hostname, '/'); - if (contact) - *contact++ = '\0'; - if (ast_strlen_zero(contact)) - contact = "s"; - porta = strchr(hostname, ':'); - if (porta) { - *porta++ = '\0'; - portnum = atoi(porta); - if (portnum == 0) { - ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno); - return -1; - } - } - if (!(reg = ast_calloc(1, sizeof(*reg)))) { - ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry entry\n"); - return -1; - } - - if (ast_string_field_init(reg, 256)) { - ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry strings\n"); - free(reg); - return -1; - } - - regobjs++; - ASTOBJ_INIT(reg); - ast_string_field_set(reg, contact, contact); - if (!ast_strlen_zero(username)) - ast_string_field_set(reg, username, username); - if (hostname) - ast_string_field_set(reg, hostname, hostname); - if (authuser) - ast_string_field_set(reg, authuser, authuser); - if (secret) - ast_string_field_set(reg, secret, secret); - reg->expire = -1; - reg->timeout = -1; - reg->refresh = default_expiry; - reg->portno = portnum; - reg->callid_valid = FALSE; - reg->ocseq = INITIAL_CSEQ; - ASTOBJ_CONTAINER_LINK(®l, reg); /* Add the new registry entry to the list */ - ASTOBJ_UNREF(reg,sip_registry_destroy); - return 0; -} - -/*! \brief Parse multiline SIP headers into one header - This is enabled if pedanticsipchecking is enabled */ -static int lws2sws(char *msgbuf, int len) -{ - int h = 0, t = 0; - int lws = 0; - - for (; h < len;) { - /* Eliminate all CRs */ - if (msgbuf[h] == '\r') { - h++; - continue; - } - /* Check for end-of-line */ - if (msgbuf[h] == '\n') { - /* Check for end-of-message */ - if (h + 1 == len) - break; - /* Check for a continuation line */ - if (msgbuf[h + 1] == ' ' || msgbuf[h + 1] == '\t') { - /* Merge continuation line */ - h++; - continue; - } - /* Propagate LF and start new line */ - msgbuf[t++] = msgbuf[h++]; - lws = 0; - continue; - } - if (msgbuf[h] == ' ' || msgbuf[h] == '\t') { - if (lws) { - h++; - continue; - } - msgbuf[t++] = msgbuf[h++]; - lws = 1; - continue; - } - msgbuf[t++] = msgbuf[h++]; - if (lws) - lws = 0; - } - msgbuf[t] = '\0'; - return t; -} - -/*! \brief Parse a SIP message - \note this function is used both on incoming and outgoing packets -*/ -static int parse_request(struct sip_request *req) -{ - /* Divide fields by NULL's */ - char *c; - int f = 0; - - c = req->data; - - /* First header starts immediately */ - req->header[f] = c; - while(*c) { - if (*c == '\n') { - /* We've got a new header */ - *c = 0; - - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "Header %d: %s (%d)\n", f, req->header[f], (int) strlen(req->header[f])); - if (ast_strlen_zero(req->header[f])) { - /* Line by itself means we're now in content */ - c++; - break; - } - if (f >= SIP_MAX_HEADERS - 1) { - ast_log(LOG_WARNING, "Too many SIP headers. Ignoring.\n"); - } else { - f++; - req->header[f] = c + 1; - } - } else if (*c == '\r') { - /* Ignore but eliminate \r's */ - *c = 0; - } - c++; - } - - req->headers = f; - - /* Check a non-newline-terminated last header */ - if (!ast_strlen_zero(req->header[f])) { - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "Header %d: %s (%d)\n", f, req->header[f], (int) strlen(req->header[f])); - req->headers++; - } - - /* Now we process any body content */ - f = 0; - req->line[f] = c; - while (*c) { - if (*c == '\n') { - /* We've got a new line */ - *c = 0; - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "Line: %s (%d)\n", req->line[f], (int) strlen(req->line[f])); - if (f == SIP_MAX_LINES - 1) { - ast_log(LOG_WARNING, "Too many SDP lines. Ignoring.\n"); - break; - } else { - f++; - req->line[f] = c + 1; - } - } else if (*c == '\r') { - /* Ignore and eliminate \r's */ - *c = 0; - } - c++; - } - - req->lines = f; - - /* Check a non-newline-terminated last line */ - if (!ast_strlen_zero(req->line[f])) { - req->lines++; - } - - if (*c) - ast_log(LOG_WARNING, "Odd content, extra stuff left over ('%s')\n", c); - - /* Split up the first line parts */ - return determine_firstline_parts(req); -} - -/*! - \brief Determine whether a SIP message contains an SDP in its body - \param req the SIP request to process - \return 1 if SDP found, 0 if not found - - Also updates req->sdp_start and req->sdp_end to indicate where the SDP - lives in the message body. -*/ -static int find_sdp(struct sip_request *req) -{ - const char *content_type; - const char *content_length; - const char *search; - char *boundary; - unsigned int x; - int boundaryisquoted = FALSE; - int found_application_sdp = FALSE; - int found_end_of_headers = FALSE; - - content_length = get_header(req, "Content-Length"); - - if (!ast_strlen_zero(content_length)) { - if (sscanf(content_length, "%ud", &x) != 1) { - ast_log(LOG_WARNING, "Invalid Content-Length: %s\n", content_length); - return 0; - } - - /* Content-Length of zero means there can't possibly be an - SDP here, even if the Content-Type says there is */ - if (x == 0) - return 0; - } - - content_type = get_header(req, "Content-Type"); - - /* if the body contains only SDP, this is easy */ - if (!strncasecmp(content_type, "application/sdp", 15)) { - req->sdp_start = 0; - req->sdp_end = req->lines; - return req->lines ? 1 : 0; - } - - /* if it's not multipart/mixed, there cannot be an SDP */ - if (strncasecmp(content_type, "multipart/mixed", 15)) - return 0; - - /* if there is no boundary marker, it's invalid */ - if ((search = strcasestr(content_type, ";boundary="))) - search += 10; - else if ((search = strcasestr(content_type, "; boundary="))) - search += 11; - else - return 0; - - if (ast_strlen_zero(search)) - return 0; - - /* If the boundary is quoted with ", remove quote */ - if (*search == '\"') { - search++; - boundaryisquoted = TRUE; - } - - /* make a duplicate of the string, with two extra characters - at the beginning */ - boundary = ast_strdupa(search - 2); - boundary[0] = boundary[1] = '-'; - /* Remove final quote */ - if (boundaryisquoted) - boundary[strlen(boundary) - 1] = '\0'; - - /* search for the boundary marker, the empty line delimiting headers from - sdp part and the end boundry if it exists */ - - for (x = 0; x < (req->lines ); x++) { - if(!strncasecmp(req->line[x], boundary, strlen(boundary))){ - if(found_application_sdp && found_end_of_headers){ - req->sdp_end = x-1; - return 1; - } - found_application_sdp = FALSE; - } - if(!strcasecmp(req->line[x], "Content-Type: application/sdp")) - found_application_sdp = TRUE; - - if(strlen(req->line[x]) == 0 ){ - if(found_application_sdp && !found_end_of_headers){ - req->sdp_start = x; - found_end_of_headers = TRUE; - } - } - } - if(found_application_sdp && found_end_of_headers) { - req->sdp_end = x; - return TRUE; - } - return FALSE; -} - -/*! \brief Change hold state for a call */ -static void change_hold_state(struct sip_pvt *dialog, struct sip_request *req, int holdstate, int sendonly) -{ - if (global_notifyhold && (!holdstate || !ast_test_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD))) - sip_peer_hold(dialog, holdstate); - if (global_callevents) - manager_event(EVENT_FLAG_CALL, holdstate ? "Hold" : "Unhold", - "Channel: %s\r\n" - "Uniqueid: %s\r\n", - dialog->owner->name, - dialog->owner->uniqueid); - append_history(dialog, holdstate ? "Hold" : "Unhold", "%s", req->data); - if (!holdstate) { /* Put off remote hold */ - ast_clear_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */ - return; - } - /* No address for RTP, we're on hold */ - - if (sendonly == 1) /* One directional hold (sendonly/recvonly) */ - ast_set_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD_ONEDIR); - else if (sendonly == 2) /* Inactive stream */ - ast_set_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD_INACTIVE); - else - ast_set_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD_ACTIVE); - return; -} - -/*! \brief Process SIP SDP offer, select formats and activate RTP channels - If offer is rejected, we will not change any properties of the call - Return 0 on success, a negative value on errors. - Must be called after find_sdp(). -*/ -static int process_sdp(struct sip_pvt *p, struct sip_request *req) -{ - const char *m; /* SDP media offer */ - const char *c; - const char *a; - char host[258]; - int len = -1; - int portno = -1; /*!< RTP Audio port number */ - int vportno = -1; /*!< RTP Video port number */ - int udptlportno = -1; - int peert38capability = 0; - char s[256]; - int old = 0; - - /* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */ - int peercapability = 0, peernoncodeccapability = 0; - int vpeercapability = 0, vpeernoncodeccapability = 0; - struct sockaddr_in sin; /*!< media socket address */ - struct sockaddr_in vsin; /*!< Video socket address */ - - const char *codecs; - struct hostent *hp; /*!< RTP Audio host IP */ - struct hostent *vhp = NULL; /*!< RTP video host IP */ - struct ast_hostent audiohp; - struct ast_hostent videohp; - int codec; - int destiterator = 0; - int iterator; - int sendonly = -1; - int numberofports; - struct ast_rtp *newaudiortp, *newvideortp; /* Buffers for codec handling */ - int newjointcapability; /* Negotiated capability */ - int newpeercapability; - int newnoncodeccapability; - int numberofmediastreams = 0; - int debug = sip_debug_test_pvt(p); - - int found_rtpmap_codecs[SDP_MAX_RTPMAP_CODECS]; - int last_rtpmap_codec=0; - - if (!p->rtp) { - ast_log(LOG_ERROR, "Got SDP but have no RTP session allocated.\n"); - return -1; - } - - /* Initialize the temporary RTP structures we use to evaluate the offer from the peer */ -#ifdef LOW_MEMORY - newaudiortp = ast_threadstorage_get(&ts_audio_rtp, ast_rtp_alloc_size()); -#else - newaudiortp = alloca(ast_rtp_alloc_size()); -#endif - memset(newaudiortp, 0, ast_rtp_alloc_size()); - ast_rtp_new_init(newaudiortp); - ast_rtp_pt_clear(newaudiortp); - -#ifdef LOW_MEMORY - newvideortp = ast_threadstorage_get(&ts_video_rtp, ast_rtp_alloc_size()); -#else - newvideortp = alloca(ast_rtp_alloc_size()); -#endif - memset(newvideortp, 0, ast_rtp_alloc_size()); - ast_rtp_new_init(newvideortp); - ast_rtp_pt_clear(newvideortp); - - /* Update our last rtprx when we receive an SDP, too */ - p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ - - - /* Try to find first media stream */ - m = get_sdp(req, "m"); - destiterator = req->sdp_start; - c = get_sdp_iterate(&destiterator, req, "c"); - if (ast_strlen_zero(m) || ast_strlen_zero(c)) { - ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c); - return -1; - } - - /* Check for IPv4 address (not IPv6 yet) */ - if (sscanf(c, "IN IP4 %256s", host) != 1) { - ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c); - return -1; - } - - /* XXX This could block for a long time, and block the main thread! XXX */ - hp = ast_gethostbyname(host, &audiohp); - if (!hp) { - ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c); - return -1; - } - vhp = hp; /* Copy to video address as default too */ - - iterator = req->sdp_start; - ast_set_flag(&p->flags[0], SIP_NOVIDEO); - - - /* Find media streams in this SDP offer */ - while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') { - int x; - int audio = FALSE; - - numberofports = 1; - len = -1; - if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || - (sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1 && len > 0)) { - audio = TRUE; - numberofmediastreams++; - /* Found audio stream in this media definition */ - portno = x; - /* Scan through the RTP payload types specified in a "m=" line: */ - for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) { - if (sscanf(codecs, "%d%n", &codec, &len) != 1) { - ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs); - return -1; - } - if (debug) - ast_verbose("Found RTP audio format %d\n", codec); - ast_rtp_set_m_type(newaudiortp, codec); - } - } else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || - (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1 && len >= 0)) { - /* If it is not audio - is it video ? */ - ast_clear_flag(&p->flags[0], SIP_NOVIDEO); - numberofmediastreams++; - vportno = x; - /* Scan through the RTP payload types specified in a "m=" line: */ - for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) { - if (sscanf(codecs, "%d%n", &codec, &len) != 1) { - ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs); - return -1; - } - if (debug) - ast_verbose("Found RTP video format %d\n", codec); - ast_rtp_set_m_type(newvideortp, codec); - } - } else if (p->udptl && ( (sscanf(m, "image %d udptl t38%n", &x, &len) == 1 && len > 0) || - (sscanf(m, "image %d UDPTL t38%n", &x, &len) == 1 && len >= 0) )) { - if (debug) - ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid); - udptlportno = x; - numberofmediastreams++; - - if (p->owner && p->lastinvite) { - p->t38.state = T38_PEER_REINVITE; /* T38 Offered in re-invite from remote party */ - if (option_debug > 1) - ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>" ); - } else { - p->t38.state = T38_PEER_DIRECT; /* T38 Offered directly from peer in first invite */ - if (option_debug > 1) - ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); - } - } else - ast_log(LOG_WARNING, "Unsupported SDP media type in offer: %s\n", m); - if (numberofports > 1) - ast_log(LOG_WARNING, "SDP offered %d ports for media, not supported by Asterisk. Will try anyway...\n", numberofports); - - - /* Check for Media-description-level-address for audio */ - c = get_sdp_iterate(&destiterator, req, "c"); - if (!ast_strlen_zero(c)) { - if (sscanf(c, "IN IP4 %256s", host) != 1) { - ast_log(LOG_WARNING, "Invalid secondary host in c= line, '%s'\n", c); - } else { - /* XXX This could block for a long time, and block the main thread! XXX */ - if (audio) { - if ( !(hp = ast_gethostbyname(host, &audiohp))) { - ast_log(LOG_WARNING, "Unable to lookup RTP Audio host in secondary c= line, '%s'\n", c); - return -2; - } - } else if (!(vhp = ast_gethostbyname(host, &videohp))) { - ast_log(LOG_WARNING, "Unable to lookup RTP video host in secondary c= line, '%s'\n", c); - return -2; - } - } - - } - } - if (portno == -1 && vportno == -1 && udptlportno == -1) - /* No acceptable offer found in SDP - we have no ports */ - /* Do not change RTP or VRTP if this is a re-invite */ - return -2; - - if (numberofmediastreams > 2) - /* We have too many fax, audio and/or video media streams, fail this offer */ - return -3; - - /* RTP addresses and ports for audio and video */ - sin.sin_family = AF_INET; - vsin.sin_family = AF_INET; - memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); - if (vhp) - memcpy(&vsin.sin_addr, vhp->h_addr, sizeof(vsin.sin_addr)); - - /* Setup UDPTL port number */ - if (p->udptl) { - if (udptlportno > 0) { - sin.sin_port = htons(udptlportno); - if (ast_test_flag(&p->flags[0], SIP_NAT) && ast_test_flag(&p->flags[1], SIP_PAGE2_UDPTL_DESTINATION)) { - struct sockaddr_in peer; - ast_rtp_get_peer(p->rtp, &peer); - if (peer.sin_addr.s_addr) { - memcpy(&sin.sin_addr, &peer.sin_addr, sizeof(sin.sin_addr)); - if (debug) { - ast_log(LOG_DEBUG, "Peer T.38 UDPTL is set behind NAT and with destination, destination address now %s\n", ast_inet_ntoa(sin.sin_addr)); - } - } - } - ast_udptl_set_peer(p->udptl, &sin); - if (debug) - ast_log(LOG_DEBUG,"Peer T.38 UDPTL is at port %s:%d\n",ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - } else { - ast_udptl_stop(p->udptl); - if (debug) - ast_log(LOG_DEBUG, "Peer doesn't provide T.38 UDPTL\n"); - } - } - - - if (p->rtp) { - if (portno > 0) { - sin.sin_port = htons(portno); - ast_rtp_set_peer(p->rtp, &sin); - if (debug) - ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - } else { - if (udptlportno > 0) { - if (debug) - ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session. Callid %s\n", p->callid); - } else { - ast_rtp_stop(p->rtp); - if (debug) - ast_verbose("Peer doesn't provide audio. Callid %s\n", p->callid); - } - } - } - /* Setup video port number */ - if (vportno != -1) - vsin.sin_port = htons(vportno); - - /* Next, scan through each "a=rtpmap:" line, noting each - * specified RTP payload type (with corresponding MIME subtype): - */ - /* XXX This needs to be done per media stream, since it's media stream specific */ - iterator = req->sdp_start; - while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') { - char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */ - if (option_debug > 1) { - int breakout = FALSE; - - /* If we're debugging, check for unsupported sdp options */ - if (!strncasecmp(a, "rtcp:", (size_t) 5)) { - if (debug) - ast_verbose("Got unsupported a:rtcp in SDP offer \n"); - breakout = TRUE; - } else if (!strncasecmp(a, "fmtp:", (size_t) 5)) { - /* Format parameters: Not supported */ - /* Note: This is used for codec parameters, like bitrate for - G722 and video formats for H263 and H264 - See RFC2327 for an example */ - if (debug) - ast_verbose("Got unsupported a:fmtp in SDP offer \n"); - breakout = TRUE; - } else if (!strncasecmp(a, "framerate:", (size_t) 10)) { - /* Video stuff: Not supported */ - if (debug) - ast_verbose("Got unsupported a:framerate in SDP offer \n"); - breakout = TRUE; - } else if (!strncasecmp(a, "maxprate:", (size_t) 9)) { - /* Video stuff: Not supported */ - if (debug) - ast_verbose("Got unsupported a:maxprate in SDP offer \n"); - breakout = TRUE; - } else if (!strncasecmp(a, "crypto:", (size_t) 7)) { - /* SRTP stuff, not yet supported */ - if (debug) - ast_verbose("Got unsupported a:crypto in SDP offer \n"); - breakout = TRUE; - } - if (breakout) /* We have a match, skip to next header */ - continue; - } - if (!strcasecmp(a, "sendonly")) { - if (sendonly == -1) - sendonly = 1; - continue; - } else if (!strcasecmp(a, "inactive")) { - if (sendonly == -1) - sendonly = 2; - continue; - } else if (!strcasecmp(a, "sendrecv")) { - if (sendonly == -1) - sendonly = 0; - continue; - } else if (strlen(a) > 5 && !strncasecmp(a, "ptime", 5)) { - char *tmp = strrchr(a, ':'); - long int framing = 0; - if (tmp) { - tmp++; - framing = strtol(tmp, NULL, 10); - if (framing == LONG_MIN || framing == LONG_MAX) { - framing = 0; - if (option_debug) - ast_log(LOG_DEBUG, "Can't read framing from SDP: %s\n", a); - } - } - if (framing && p->autoframing) { - struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp); - int codec_n; - int format = 0; - for (codec_n = 0; codec_n < MAX_RTP_PT; codec_n++) { - format = ast_rtp_codec_getformat(codec_n); - if (!format) /* non-codec or not found */ - continue; - if (option_debug) - ast_log(LOG_DEBUG, "Setting framing for %d to %ld\n", format, framing); - ast_codec_pref_setsize(pref, format, framing); - } - ast_rtp_codec_setpref(p->rtp, pref); - } - continue; - } else if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) == 2) { - /* We have a rtpmap to handle */ - int found = FALSE; - /* We should propably check if this is an audio or video codec - so we know where to look */ - - if (last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) { - /* Note: should really look at the 'freq' and '#chans' params too */ - if(ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype, - ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0) != -1) { - if (debug) - ast_verbose("Found audio description format %s for ID %d\n", mimeSubtype, codec); - found_rtpmap_codecs[last_rtpmap_codec] = codec; - last_rtpmap_codec++; - found = TRUE; - - } else if (p->vrtp) { - if(ast_rtp_set_rtpmap_type(newvideortp, codec, "video", mimeSubtype, 0) != -1) { - if (debug) - ast_verbose("Found video description format %s for ID %d\n", mimeSubtype, codec); - found_rtpmap_codecs[last_rtpmap_codec] = codec; - last_rtpmap_codec++; - found = TRUE; - } - } - } else { - if (debug) - ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec); - } - - if (!found) { - /* Remove this codec since it's an unknown media type for us */ - /* XXX This is buggy since the media line for audio and video can have the - same numbers. We need to check as described above, but for testing this works... */ - ast_rtp_unset_m_type(newaudiortp, codec); - ast_rtp_unset_m_type(newvideortp, codec); - if (debug) - ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec); - } - } - } - - if (udptlportno != -1) { - int found = 0, x; - - old = 0; - - /* Scan trough the a= lines for T38 attributes and set apropriate fileds */ - iterator = req->sdp_start; - while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') { - if ((sscanf(a, "T38FaxMaxBuffer:%d", &x) == 1)) { - found = 1; - if (option_debug > 2) - ast_log(LOG_DEBUG, "MaxBufferSize:%d\n",x); - } else if ((sscanf(a, "T38MaxBitRate:%d", &x) == 1) || (sscanf(a, "T38FaxMaxRate:%d", &x) == 1)) { - found = 1; - if (option_debug > 2) - ast_log(LOG_DEBUG,"T38MaxBitRate: %d\n",x); - switch (x) { - case 14400: - peert38capability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; - break; - case 12000: - peert38capability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; - break; - case 9600: - peert38capability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; - break; - case 7200: - peert38capability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; - break; - case 4800: - peert38capability |= T38FAX_RATE_4800 | T38FAX_RATE_2400; - break; - case 2400: - peert38capability |= T38FAX_RATE_2400; - break; - } - } else if ((sscanf(a, "T38FaxVersion:%d", &x) == 1)) { - found = 1; - if (option_debug > 2) - ast_log(LOG_DEBUG, "FaxVersion: %d\n",x); - if (x == 0) - peert38capability |= T38FAX_VERSION_0; - else if (x == 1) - peert38capability |= T38FAX_VERSION_1; - } else if ((sscanf(a, "T38FaxMaxDatagram:%d", &x) == 1) || (sscanf(a, "T38MaxDatagram:%d", &x) == 1)) { - found = 1; - if (option_debug > 2) - ast_log(LOG_DEBUG, "FaxMaxDatagram: %d\n",x); - ast_udptl_set_far_max_datagram(p->udptl, x); - ast_udptl_set_local_max_datagram(p->udptl, x); - } else if ((strncmp(a, "T38FaxFillBitRemoval", 20) == 0)) { - found = 1; - if ((sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1)) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "FillBitRemoval: %d\n",x); - if (x == 1) - peert38capability |= T38FAX_FILL_BIT_REMOVAL; - } else { - if (option_debug > 2) - ast_log(LOG_DEBUG, "FillBitRemoval\n"); - peert38capability |= T38FAX_FILL_BIT_REMOVAL; - } - } else if ((strncmp(a, "T38FaxTranscodingMMR", 20) == 0)) { - found = 1; - if ((sscanf(a, "T38FaxTranscodingMMR:%d", &x) == 1)) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Transcoding MMR: %d\n",x); - if (x == 1) - peert38capability |= T38FAX_TRANSCODING_MMR; - } else { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Transcoding MMR\n"); - peert38capability |= T38FAX_TRANSCODING_MMR; - } - } else if ((strncmp(a, "T38FaxTranscodingJBIG", 21) == 0)) { - found = 1; - if ((sscanf(a, "T38FaxTranscodingJBIG:%d", &x) == 1)) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Transcoding JBIG: %d\n",x); - if (x == 1) - peert38capability |= T38FAX_TRANSCODING_JBIG; - } else { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Transcoding JBIG\n"); - peert38capability |= T38FAX_TRANSCODING_JBIG; - } - } else if ((sscanf(a, "T38FaxRateManagement:%255s", s) == 1)) { - found = 1; - if (option_debug > 2) - ast_log(LOG_DEBUG, "RateManagement: %s\n", s); - if (!strcasecmp(s, "localTCF")) - peert38capability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF; - else if (!strcasecmp(s, "transferredTCF")) - peert38capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF; - } else if ((sscanf(a, "T38FaxUdpEC:%255s", s) == 1)) { - found = 1; - if (option_debug > 2) - ast_log(LOG_DEBUG, "UDP EC: %s\n", s); - if (!strcasecmp(s, "t38UDPRedundancy")) { - peert38capability |= T38FAX_UDP_EC_REDUNDANCY; - ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY); - } else if (!strcasecmp(s, "t38UDPFEC")) { - peert38capability |= T38FAX_UDP_EC_FEC; - ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC); - } else { - peert38capability |= T38FAX_UDP_EC_NONE; - ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE); - } - } - } - if (found) { /* Some cisco equipment returns nothing beside c= and m= lines in 200 OK T38 SDP */ - p->t38.peercapability = peert38capability; - p->t38.jointcapability = (peert38capability & 255); /* Put everything beside supported speeds settings */ - peert38capability &= (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400); - p->t38.jointcapability |= (peert38capability & p->t38.capability); /* Put the lower of our's and peer's speed */ - } - if (debug) - ast_log(LOG_DEBUG, "Our T38 capability = (%d), peer T38 capability (%d), joint T38 capability (%d)\n", - p->t38.capability, - p->t38.peercapability, - p->t38.jointcapability); - } else { - p->t38.state = T38_DISABLED; - if (option_debug > 2) - ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); - } - - /* Now gather all of the codecs that we are asked for: */ - ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability); - ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability); - - newjointcapability = p->capability & (peercapability | vpeercapability); - newpeercapability = (peercapability | vpeercapability); - newnoncodeccapability = p->noncodeccapability & peernoncodeccapability; - - - if (debug) { - /* shame on whoever coded this.... */ - char s1[SIPBUFSIZE], s2[SIPBUFSIZE], s3[SIPBUFSIZE], s4[SIPBUFSIZE]; - - ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s, combined - %s\n", - ast_getformatname_multiple(s1, SIPBUFSIZE, p->capability), - ast_getformatname_multiple(s2, SIPBUFSIZE, newpeercapability), - ast_getformatname_multiple(s3, SIPBUFSIZE, vpeercapability), - ast_getformatname_multiple(s4, SIPBUFSIZE, newjointcapability)); - - ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n", - ast_rtp_lookup_mime_multiple(s1, SIPBUFSIZE, p->noncodeccapability, 0, 0), - ast_rtp_lookup_mime_multiple(s2, SIPBUFSIZE, peernoncodeccapability, 0, 0), - ast_rtp_lookup_mime_multiple(s3, SIPBUFSIZE, newnoncodeccapability, 0, 0)); - } - if (!newjointcapability) { - /* If T.38 was not negotiated either, totally bail out... */ - if (!p->t38.jointcapability || !udptlportno) { - ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n"); - /* Do NOT Change current setting */ - return -1; - } else { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Have T.38 but no audio codecs, accepting offer anyway\n"); - return 0; - } - } - - /* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since - they are acceptable */ - p->jointcapability = newjointcapability; /* Our joint codec profile for this call */ - p->peercapability = newpeercapability; /* The other sides capability in latest offer */ - p->jointnoncodeccapability = newnoncodeccapability; /* DTMF capabilities */ - - ast_rtp_pt_copy(p->rtp, newaudiortp); - if (p->vrtp) - ast_rtp_pt_copy(p->vrtp, newvideortp); - - if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) { - ast_clear_flag(&p->flags[0], SIP_DTMF); - if (newnoncodeccapability & AST_RTP_DTMF) { - /* XXX Would it be reasonable to drop the DSP at this point? XXX */ - ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833); - /* Since RFC2833 is now negotiated we need to change some properties of the RTP stream */ - ast_rtp_setdtmf(p->rtp, 1); - ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); - } else { - ast_set_flag(&p->flags[0], SIP_DTMF_INBAND); - } - } - - /* Setup audio port number */ - if (p->rtp && sin.sin_port) { - ast_rtp_set_peer(p->rtp, &sin); - if (debug) - ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - } - - /* Setup video port number */ - if (p->vrtp && vsin.sin_port) { - ast_rtp_set_peer(p->vrtp, &vsin); - if (debug) - ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port)); - } - - /* Ok, we're going with this offer */ - if (option_debug > 1) { - char buf[SIPBUFSIZE]; - ast_log(LOG_DEBUG, "We're settling with these formats: %s\n", ast_getformatname_multiple(buf, SIPBUFSIZE, p->jointcapability)); - } - - if (!p->owner) /* There's no open channel owning us so we can return here. For a re-invite or so, we proceed */ - return 0; - - if (option_debug > 3) - ast_log(LOG_DEBUG, "We have an owner, now see if we need to change this call\n"); - - if (!(p->owner->nativeformats & p->jointcapability) && (p->jointcapability & AST_FORMAT_AUDIO_MASK)) { - if (debug) { - char s1[SIPBUFSIZE], s2[SIPBUFSIZE]; - ast_log(LOG_DEBUG, "Oooh, we need to change our audio formats since our peer supports only %s and not %s\n", - ast_getformatname_multiple(s1, SIPBUFSIZE, p->jointcapability), - ast_getformatname_multiple(s2, SIPBUFSIZE, p->owner->nativeformats)); - } - p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1) | (p->capability & vpeercapability); - ast_set_read_format(p->owner, p->owner->readformat); - ast_set_write_format(p->owner, p->owner->writeformat); - } - - if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) && sin.sin_addr.s_addr && (!sendonly || sendonly == -1)) { - ast_queue_control(p->owner, AST_CONTROL_UNHOLD); - /* Activate a re-invite */ - ast_queue_frame(p->owner, &ast_null_frame); - } else if (!sin.sin_addr.s_addr || (sendonly && sendonly != -1)) { - ast_queue_control_data(p->owner, AST_CONTROL_HOLD, - S_OR(p->mohsuggest, NULL), - !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0); - if (sendonly) - ast_rtp_stop(p->rtp); - /* RTCP needs to go ahead, even if we're on hold!!! */ - /* Activate a re-invite */ - ast_queue_frame(p->owner, &ast_null_frame); - } - - /* Manager Hold and Unhold events must be generated, if necessary */ - if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) && sin.sin_addr.s_addr && (!sendonly || sendonly == -1)) - change_hold_state(p, req, FALSE, sendonly); - else if (!sin.sin_addr.s_addr || (sendonly && sendonly != -1)) - change_hold_state(p, req, TRUE, sendonly); - return 0; -} - -#ifdef LOW_MEMORY -static void ts_ast_rtp_destroy(void *data) -{ - struct ast_rtp *tmp = data; - ast_rtp_destroy(tmp); -} -#endif - -/*! \brief Add header to SIP message */ -static int add_header(struct sip_request *req, const char *var, const char *value) -{ - int maxlen = sizeof(req->data) - 4 - req->len; /* 4 bytes are for two \r\n ? */ - - if (req->headers == SIP_MAX_HEADERS) { - ast_log(LOG_WARNING, "Out of SIP header space\n"); - return -1; - } - - if (req->lines) { - ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n"); - return -1; - } - - if (maxlen <= 0) { - ast_log(LOG_WARNING, "Out of space, can't add anymore (%s:%s)\n", var, value); - return -1; - } - - req->header[req->headers] = req->data + req->len; - - if (compactheaders) - var = find_alias(var, var); - - snprintf(req->header[req->headers], maxlen, "%s: %s\r\n", var, value); - req->len += strlen(req->header[req->headers]); - req->headers++; - - return 0; -} - -/*! \brief Add 'Content-Length' header to SIP message */ -static int add_header_contentLength(struct sip_request *req, int len) -{ - char clen[10]; - - snprintf(clen, sizeof(clen), "%d", len); - return add_header(req, "Content-Length", clen); -} - -/*! \brief Add content (not header) to SIP message */ -static int add_line(struct sip_request *req, const char *line) -{ - if (req->lines == SIP_MAX_LINES) { - ast_log(LOG_WARNING, "Out of SIP line space\n"); - return -1; - } - if (!req->lines) { - /* Add extra empty return */ - snprintf(req->data + req->len, sizeof(req->data) - req->len, "\r\n"); - req->len += strlen(req->data + req->len); - } - if (req->len >= sizeof(req->data) - 4) { - ast_log(LOG_WARNING, "Out of space, can't add anymore\n"); - return -1; - } - req->line[req->lines] = req->data + req->len; - snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line); - req->len += strlen(req->line[req->lines]); - req->lines++; - return 0; -} - -/*! \brief Copy one header field from one request to another */ -static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field) -{ - const char *tmp = get_header(orig, field); - - if (!ast_strlen_zero(tmp)) /* Add what we're responding to */ - return add_header(req, field, tmp); - ast_log(LOG_NOTICE, "No field '%s' present to copy\n", field); - return -1; -} - -/*! \brief Copy all headers from one request to another */ -static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field) -{ - int start = 0; - int copied = 0; - for (;;) { - const char *tmp = __get_header(orig, field, &start); - - if (ast_strlen_zero(tmp)) - break; - /* Add what we're responding to */ - add_header(req, field, tmp); - copied++; - } - return copied ? 0 : -1; -} - -/*! \brief Copy SIP VIA Headers from the request to the response -\note If the client indicates that it wishes to know the port we received from, - it adds ;rport without an argument to the topmost via header. We need to - add the port number (from our point of view) to that parameter. - We always add ;received=<ip address> to the topmost via header. - Received: RFC 3261, rport RFC 3581 */ -static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field) -{ - int copied = 0; - int start = 0; - - for (;;) { - char new[512]; - const char *oh = __get_header(orig, field, &start); - - if (ast_strlen_zero(oh)) - break; - - if (!copied) { /* Only check for empty rport in topmost via header */ - char leftmost[512], *others, *rport; - - /* Only work on leftmost value */ - ast_copy_string(leftmost, oh, sizeof(leftmost)); - others = strchr(leftmost, ','); - if (others) - *others++ = '\0'; - - /* Find ;rport; (empty request) */ - rport = strstr(leftmost, ";rport"); - if (rport && *(rport+6) == '=') - rport = NULL; /* We already have a parameter to rport */ - - /* Check rport if NAT=yes or NAT=rfc3581 (which is the default setting) */ - if (rport && ((ast_test_flag(&p->flags[0], SIP_NAT) == SIP_NAT_ALWAYS) || (ast_test_flag(&p->flags[0], SIP_NAT) == SIP_NAT_RFC3581))) { - /* We need to add received port - rport */ - char *end; - - rport = strstr(leftmost, ";rport"); - - if (rport) { - end = strchr(rport + 1, ';'); - if (end) - memmove(rport, end, strlen(end) + 1); - else - *rport = '\0'; - } - - /* Add rport to first VIA header if requested */ - snprintf(new, sizeof(new), "%s;received=%s;rport=%d%s%s", - leftmost, ast_inet_ntoa(p->recv.sin_addr), - ntohs(p->recv.sin_port), - others ? "," : "", others ? others : ""); - } else { - /* We should *always* add a received to the topmost via */ - snprintf(new, sizeof(new), "%s;received=%s%s%s", - leftmost, ast_inet_ntoa(p->recv.sin_addr), - others ? "," : "", others ? others : ""); - } - oh = new; /* the header to copy */ - } /* else add the following via headers untouched */ - add_header(req, field, oh); - copied++; - } - if (!copied) { - ast_log(LOG_NOTICE, "No header field '%s' present to copy\n", field); - return -1; - } - return 0; -} - -/*! \brief Add route header into request per learned route */ -static void add_route(struct sip_request *req, struct sip_route *route) -{ - char r[SIPBUFSIZE*2], *p; - int n, rem = sizeof(r); - - if (!route) - return; - - p = r; - for (;route ; route = route->next) { - n = strlen(route->hop); - if (rem < n+3) /* we need room for ",<route>" */ - break; - if (p != r) { /* add a separator after fist route */ - *p++ = ','; - --rem; - } - *p++ = '<'; - ast_copy_string(p, route->hop, rem); /* cannot fail */ - p += n; - *p++ = '>'; - rem -= (n+2); - } - *p = '\0'; - add_header(req, "Route", r); -} - -/*! \brief Set destination from SIP URI */ -static void set_destination(struct sip_pvt *p, char *uri) -{ - char *h, *maddr, hostname[256]; - int port, hn; - struct hostent *hp; - struct ast_hostent ahp; - int debug=sip_debug_test_pvt(p); - - /* Parse uri to h (host) and port - uri is already just the part inside the <> */ - /* general form we are expecting is sip[s]:username[:password]@host[:port][;...] */ - - if (debug) - ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri); - - /* Find and parse hostname */ - h = strchr(uri, '@'); - if (h) - ++h; - else { - h = uri; - if (strncasecmp(h, "sip:", 4) == 0) - h += 4; - else if (strncasecmp(h, "sips:", 5) == 0) - h += 5; - } - hn = strcspn(h, ":;>") + 1; - if (hn > sizeof(hostname)) - hn = sizeof(hostname); - ast_copy_string(hostname, h, hn); - /* XXX bug here if string has been trimmed to sizeof(hostname) */ - h += hn - 1; - - /* Is "port" present? if not default to STANDARD_SIP_PORT */ - if (*h == ':') { - /* Parse port */ - ++h; - port = strtol(h, &h, 10); - } - else - port = STANDARD_SIP_PORT; - - /* Got the hostname:port - but maybe there's a "maddr=" to override address? */ - maddr = strstr(h, "maddr="); - if (maddr) { - maddr += 6; - hn = strspn(maddr, "0123456789.") + 1; - if (hn > sizeof(hostname)) - hn = sizeof(hostname); - ast_copy_string(hostname, maddr, hn); - } - - hp = ast_gethostbyname(hostname, &ahp); - if (hp == NULL) { - ast_log(LOG_WARNING, "Can't find address for host '%s'\n", hostname); - return; - } - p->sa.sin_family = AF_INET; - memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr)); - p->sa.sin_port = htons(port); - if (debug) - ast_verbose("set_destination: set destination to %s, port %d\n", ast_inet_ntoa(p->sa.sin_addr), port); -} - -/*! \brief Initialize SIP response, based on SIP request */ -static int init_resp(struct sip_request *resp, const char *msg) -{ - /* Initialize a response */ - memset(resp, 0, sizeof(*resp)); - resp->method = SIP_RESPONSE; - resp->header[0] = resp->data; - snprintf(resp->header[0], sizeof(resp->data), "SIP/2.0 %s\r\n", msg); - resp->len = strlen(resp->header[0]); - resp->headers++; - return 0; -} - -/*! \brief Initialize SIP request */ -static int init_req(struct sip_request *req, int sipmethod, const char *recip) -{ - /* Initialize a request */ - memset(req, 0, sizeof(*req)); - req->method = sipmethod; - req->header[0] = req->data; - snprintf(req->header[0], sizeof(req->data), "%s %s SIP/2.0\r\n", sip_methods[sipmethod].text, recip); - req->len = strlen(req->header[0]); - req->headers++; - return 0; -} - - -/*! \brief Prepare SIP response packet */ -static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req) -{ - char newto[256]; - const char *ot; - - init_resp(resp, msg); - copy_via_headers(p, resp, req, "Via"); - if (msg[0] == '1' || msg[0] == '2') - copy_all_header(resp, req, "Record-Route"); - copy_header(resp, req, "From"); - ot = get_header(req, "To"); - if (!strcasestr(ot, "tag=") && strncmp(msg, "100", 3)) { - /* Add the proper tag if we don't have it already. If they have specified - their tag, use it. Otherwise, use our own tag */ - if (!ast_strlen_zero(p->theirtag) && ast_test_flag(&p->flags[0], SIP_OUTGOING)) - snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag); - else if (p->tag && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) - snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag); - else - ast_copy_string(newto, ot, sizeof(newto)); - ot = newto; - } - add_header(resp, "To", ot); - copy_header(resp, req, "Call-ID"); - copy_header(resp, req, "CSeq"); - if (!ast_strlen_zero(global_useragent)) - add_header(resp, "User-Agent", global_useragent); - add_header(resp, "Allow", ALLOWED_METHODS); - add_header(resp, "Supported", SUPPORTED_EXTENSIONS); - if (msg[0] == '2' && (p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER)) { - /* For registration responses, we also need expiry and - contact info */ - char tmp[256]; - - snprintf(tmp, sizeof(tmp), "%d", p->expiry); - add_header(resp, "Expires", tmp); - if (p->expiry) { /* Only add contact if we have an expiry time */ - char contact[SIPBUFSIZE]; - snprintf(contact, sizeof(contact), "%s;expires=%d", p->our_contact, p->expiry); - add_header(resp, "Contact", contact); /* Not when we unregister */ - } - } else if (msg[0] != '4' && !ast_strlen_zero(p->our_contact)) { - add_header(resp, "Contact", p->our_contact); - } - return 0; -} - -/*! \brief Initialize a SIP request message (not the initial one in a dialog) */ -static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch) -{ - struct sip_request *orig = &p->initreq; - char stripped[80]; - char tmp[80]; - char newto[256]; - const char *c; - const char *ot, *of; - int is_strict = FALSE; /*!< Strict routing flag */ - - memset(req, 0, sizeof(struct sip_request)); - - snprintf(p->lastmsg, sizeof(p->lastmsg), "Tx: %s", sip_methods[sipmethod].text); - - if (!seqno) { - p->ocseq++; - seqno = p->ocseq; - } - - if (sipmethod == SIP_CANCEL) { - p->branch = p->invite_branch; - build_via(p); - } else if (newbranch) { - p->branch ^= ast_random(); - build_via(p); - } - - /* Check for strict or loose router */ - if (p->route && !ast_strlen_zero(p->route->hop) && strstr(p->route->hop,";lr") == NULL) { - is_strict = TRUE; - if (sipdebug) - ast_log(LOG_DEBUG, "Strict routing enforced for session %s\n", p->callid); - } - - if (sipmethod == SIP_CANCEL) - c = p->initreq.rlPart2; /* Use original URI */ - else if (sipmethod == SIP_ACK) { - /* Use URI from Contact: in 200 OK (if INVITE) - (we only have the contacturi on INVITEs) */ - if (!ast_strlen_zero(p->okcontacturi)) - c = is_strict ? p->route->hop : p->okcontacturi; - else - c = p->initreq.rlPart2; - } else if (!ast_strlen_zero(p->okcontacturi)) - c = is_strict ? p->route->hop : p->okcontacturi; /* Use for BYE or REINVITE */ - else if (!ast_strlen_zero(p->uri)) - c = p->uri; - else { - char *n; - /* We have no URI, use To: or From: header as URI (depending on direction) */ - ast_copy_string(stripped, get_header(orig, (ast_test_flag(&p->flags[0], SIP_OUTGOING)) ? "To" : "From"), - sizeof(stripped)); - n = get_in_brackets(stripped); - c = strsep(&n, ";"); /* trim ; and beyond */ - } - init_req(req, sipmethod, c); - - snprintf(tmp, sizeof(tmp), "%d %s", seqno, sip_methods[sipmethod].text); - - add_header(req, "Via", p->via); - if (p->route) { - set_destination(p, p->route->hop); - add_route(req, is_strict ? p->route->next : p->route); - } - - ot = get_header(orig, "To"); - of = get_header(orig, "From"); - - /* Add tag *unless* this is a CANCEL, in which case we need to send it exactly - as our original request, including tag (or presumably lack thereof) */ - if (!strcasestr(ot, "tag=") && sipmethod != SIP_CANCEL) { - /* Add the proper tag if we don't have it already. If they have specified - their tag, use it. Otherwise, use our own tag */ - if (ast_test_flag(&p->flags[0], SIP_OUTGOING) && !ast_strlen_zero(p->theirtag)) - snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag); - else if (!ast_test_flag(&p->flags[0], SIP_OUTGOING)) - snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag); - else - snprintf(newto, sizeof(newto), "%s", ot); - ot = newto; - } - - if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - add_header(req, "From", of); - add_header(req, "To", ot); - } else { - add_header(req, "From", ot); - add_header(req, "To", of); - } - /* Do not add Contact for MESSAGE, BYE and Cancel requests */ - if (sipmethod != SIP_BYE && sipmethod != SIP_CANCEL && sipmethod != SIP_MESSAGE) - add_header(req, "Contact", p->our_contact); - - copy_header(req, orig, "Call-ID"); - add_header(req, "CSeq", tmp); - - if (!ast_strlen_zero(global_useragent)) - add_header(req, "User-Agent", global_useragent); - add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS); - - if (!ast_strlen_zero(p->rpid)) - add_header(req, "Remote-Party-ID", p->rpid); - - return 0; -} - -/*! \brief Base transmit response function */ -static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable) -{ - struct sip_request resp; - int seqno = 0; - - if (reliable && (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1)) { - ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq")); - return -1; - } - respprep(&resp, p, msg, req); - add_header_contentLength(&resp, 0); - /* If we are cancelling an incoming invite for some reason, add information - about the reason why we are doing this in clear text */ - if (p->method == SIP_INVITE && msg[0] != '1' && p->owner && p->owner->hangupcause) { - char buf[10]; - - add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause)); - snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause); - add_header(&resp, "X-Asterisk-HangupCauseCode", buf); - } - return send_response(p, &resp, reliable, seqno); -} - -static void temp_pvt_cleanup(void *data) -{ - struct sip_pvt *p = data; - - ast_string_field_free_memory(p); - - free(data); -} - -/*! \brief Transmit response, no retransmits, using a temporary pvt structure */ -static int transmit_response_using_temp(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg) -{ - struct sip_pvt *p = NULL; - - if (!(p = ast_threadstorage_get(&ts_temp_pvt, sizeof(*p)))) { - ast_log(LOG_NOTICE, "Failed to get temporary pvt\n"); - return -1; - } - - /* if the structure was just allocated, initialize it */ - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) { - ast_set_flag(&p->flags[0], SIP_NO_HISTORY); - if (ast_string_field_init(p, 512)) - return -1; - } - - /* Initialize the bare minimum */ - p->method = intended_method; - - if (sin) { - p->sa = *sin; - if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip)) - p->ourip = __ourip; - } else - p->ourip = __ourip; - - p->branch = ast_random(); - make_our_tag(p->tag, sizeof(p->tag)); - p->ocseq = INITIAL_CSEQ; - - if (useglobal_nat && sin) { - ast_copy_flags(&p->flags[0], &global_flags[0], SIP_NAT); - p->recv = *sin; - do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE); - } - check_via(p, req); - - ast_string_field_set(p, fromdomain, default_fromdomain); - build_via(p); - ast_string_field_set(p, callid, callid); - - /* Use this temporary pvt structure to send the message */ - __transmit_response(p, msg, req, XMIT_UNRELIABLE); - - /* Free the string fields, but not the pool space */ - ast_string_field_reset_all(p); - - return 0; -} - -/*! \brief Transmit response, no retransmits */ -static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req) -{ - return __transmit_response(p, msg, req, XMIT_UNRELIABLE); -} - -/*! \brief Transmit response, no retransmits */ -static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported) -{ - struct sip_request resp; - respprep(&resp, p, msg, req); - append_date(&resp); - add_header(&resp, "Unsupported", unsupported); - add_header_contentLength(&resp, 0); - return send_response(p, &resp, XMIT_UNRELIABLE, 0); -} - -/*! \brief Transmit response, Make sure you get an ACK - This is only used for responses to INVITEs, where we need to make sure we get an ACK -*/ -static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req) -{ - return __transmit_response(p, msg, req, XMIT_CRITICAL); -} - -/*! \brief Append date to SIP message */ -static void append_date(struct sip_request *req) -{ - char tmpdat[256]; - struct tm tm; - time_t t = time(NULL); - - gmtime_r(&t, &tm); - strftime(tmpdat, sizeof(tmpdat), "%a, %d %b %Y %T GMT", &tm); - add_header(req, "Date", tmpdat); -} - -/*! \brief Append date and content length before transmitting response */ -static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req) -{ - struct sip_request resp; - respprep(&resp, p, msg, req); - append_date(&resp); - add_header_contentLength(&resp, 0); - return send_response(p, &resp, XMIT_UNRELIABLE, 0); -} - -/*! \brief Append Accept header, content length before transmitting response */ -static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable) -{ - struct sip_request resp; - respprep(&resp, p, msg, req); - add_header(&resp, "Accept", "application/sdp"); - add_header_contentLength(&resp, 0); - return send_response(p, &resp, reliable, 0); -} - -/*! \brief Respond with authorization request */ -static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *randdata, enum xmittype reliable, const char *header, int stale) -{ - struct sip_request resp; - char tmp[512]; - int seqno = 0; - - if (reliable && (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1)) { - ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq")); - return -1; - } - /* Stale means that they sent us correct authentication, but - based it on an old challenge (nonce) */ - snprintf(tmp, sizeof(tmp), "Digest algorithm=MD5, realm=\"%s\", nonce=\"%s\"%s", global_realm, randdata, stale ? ", stale=true" : ""); - respprep(&resp, p, msg, req); - add_header(&resp, header, tmp); - add_header_contentLength(&resp, 0); - append_history(p, "AuthChal", "Auth challenge sent for %s - nc %d", p->username, p->noncecount); - return send_response(p, &resp, reliable, seqno); -} - -/*! \brief Add text body to SIP message */ -static int add_text(struct sip_request *req, const char *text) -{ - /* XXX Convert \n's to \r\n's XXX */ - add_header(req, "Content-Type", "text/plain"); - add_header_contentLength(req, strlen(text)); - add_line(req, text); - return 0; -} - -/*! \brief Add DTMF INFO tone to sip message */ -/* Always adds default duration 250 ms, regardless of what came in over the line */ -static int add_digit(struct sip_request *req, char digit, unsigned int duration) -{ - char tmp[256]; - - snprintf(tmp, sizeof(tmp), "Signal=%c\r\nDuration=%u\r\n", digit, duration); - add_header(req, "Content-Type", "application/dtmf-relay"); - add_header_contentLength(req, strlen(tmp)); - add_line(req, tmp); - return 0; -} - -/*! \brief add XML encoded media control with update - \note XML: The only way to turn 0 bits of information into a few hundred. (markster) */ -static int add_vidupdate(struct sip_request *req) -{ - const char *xml_is_a_huge_waste_of_space = - "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n" - " <media_control>\r\n" - " <vc_primitive>\r\n" - " <to_encoder>\r\n" - " <picture_fast_update>\r\n" - " </picture_fast_update>\r\n" - " </to_encoder>\r\n" - " </vc_primitive>\r\n" - " </media_control>\r\n"; - add_header(req, "Content-Type", "application/media_control+xml"); - add_header_contentLength(req, strlen(xml_is_a_huge_waste_of_space)); - add_line(req, xml_is_a_huge_waste_of_space); - return 0; -} - -/*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */ -static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate, - char **m_buf, size_t *m_size, char **a_buf, size_t *a_size, - int debug, int *min_packet_size) -{ - int rtp_code; - struct ast_format_list fmt; - - - if (debug) - ast_verbose("Adding codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec)); - if ((rtp_code = ast_rtp_lookup_code(p->rtp, 1, codec)) == -1) - return; - - if (p->rtp) { - struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp); - fmt = ast_codec_pref_getsize(pref, codec); - } else /* I dont see how you couldn't have p->rtp, but good to check for and error out if not there like earlier code */ - return; - ast_build_string(m_buf, m_size, " %d", rtp_code); - ast_build_string(a_buf, a_size, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(1, codec, - ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0), - sample_rate); - if (codec == AST_FORMAT_G729A) { - /* Indicate that we don't support VAD (G.729 annex B) */ - ast_build_string(a_buf, a_size, "a=fmtp:%d annexb=no\r\n", rtp_code); - } else if (codec == AST_FORMAT_G723_1) { - /* Indicate that we don't support VAD (G.723.1 annex A) */ - ast_build_string(a_buf, a_size, "a=fmtp:%d annexa=no\r\n", rtp_code); - } else if (codec == AST_FORMAT_ILBC) { - /* Add information about us using only 20/30 ms packetization */ - ast_build_string(a_buf, a_size, "a=fmtp:%d mode=%d\r\n", rtp_code, fmt.cur_ms); - } - - if (fmt.cur_ms && (fmt.cur_ms < *min_packet_size)) - *min_packet_size = fmt.cur_ms; - - /* Our first codec packetization processed cannot be less than zero */ - if ((*min_packet_size) == 0 && fmt.cur_ms) - *min_packet_size = fmt.cur_ms; -} - -/*! \brief Get Max T.38 Transmission rate from T38 capabilities */ -static int t38_get_rate(int t38cap) -{ - int maxrate = (t38cap & (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400)); - - if (maxrate & T38FAX_RATE_14400) { - if (option_debug > 1) - ast_log(LOG_DEBUG, "T38MaxBitRate 14400 found\n"); - return 14400; - } else if (maxrate & T38FAX_RATE_12000) { - if (option_debug > 1) - ast_log(LOG_DEBUG, "T38MaxBitRate 12000 found\n"); - return 12000; - } else if (maxrate & T38FAX_RATE_9600) { - if (option_debug > 1) - ast_log(LOG_DEBUG, "T38MaxBitRate 9600 found\n"); - return 9600; - } else if (maxrate & T38FAX_RATE_7200) { - if (option_debug > 1) - ast_log(LOG_DEBUG, "T38MaxBitRate 7200 found\n"); - return 7200; - } else if (maxrate & T38FAX_RATE_4800) { - if (option_debug > 1) - ast_log(LOG_DEBUG, "T38MaxBitRate 4800 found\n"); - return 4800; - } else if (maxrate & T38FAX_RATE_2400) { - if (option_debug > 1) - ast_log(LOG_DEBUG, "T38MaxBitRate 2400 found\n"); - return 2400; - } else { - if (option_debug > 1) - ast_log(LOG_DEBUG, "Strange, T38MaxBitRate NOT found in peers T38 SDP.\n"); - return 0; - } -} - -/*! \brief Add T.38 Session Description Protocol message */ -static int add_t38_sdp(struct sip_request *resp, struct sip_pvt *p) -{ - int len = 0; - int x = 0; - struct sockaddr_in udptlsin; - char v[256] = ""; - char s[256] = ""; - char o[256] = ""; - char c[256] = ""; - char t[256] = ""; - char m_modem[256]; - char a_modem[1024]; - char *m_modem_next = m_modem; - size_t m_modem_left = sizeof(m_modem); - char *a_modem_next = a_modem; - size_t a_modem_left = sizeof(a_modem); - struct sockaddr_in udptldest = { 0, }; - int debug; - - debug = sip_debug_test_pvt(p); - len = 0; - if (!p->udptl) { - ast_log(LOG_WARNING, "No way to add SDP without an UDPTL structure\n"); - return -1; - } - - if (!p->sessionid) { - p->sessionid = getpid(); - p->sessionversion = p->sessionid; - } else - p->sessionversion++; - - /* Our T.38 end is */ - ast_udptl_get_us(p->udptl, &udptlsin); - - /* Determine T.38 UDPTL destination */ - if (p->udptlredirip.sin_addr.s_addr) { - udptldest.sin_port = p->udptlredirip.sin_port; - udptldest.sin_addr = p->udptlredirip.sin_addr; - } else { - udptldest.sin_addr = p->ourip; - udptldest.sin_port = udptlsin.sin_port; - } - - if (debug) - ast_log(LOG_DEBUG, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(udptlsin.sin_port)); - - /* We break with the "recommendation" and send our IP, in order that our - peer doesn't have to ast_gethostbyname() us */ - - if (debug) { - ast_log(LOG_DEBUG, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n", - p->t38.capability, - p->t38.peercapability, - p->t38.jointcapability); - } - snprintf(v, sizeof(v), "v=0\r\n"); - snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(udptldest.sin_addr)); - snprintf(s, sizeof(s), "s=session\r\n"); - snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", ast_inet_ntoa(udptldest.sin_addr)); - snprintf(t, sizeof(t), "t=0 0\r\n"); - ast_build_string(&m_modem_next, &m_modem_left, "m=image %d udptl t38\r\n", ntohs(udptldest.sin_port)); - - if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0) - ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxVersion:0\r\n"); - if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1) - ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxVersion:1\r\n"); - if ((x = t38_get_rate(p->t38.jointcapability))) - ast_build_string(&a_modem_next, &a_modem_left, "a=T38MaxBitRate:%d\r\n",x); - if ((p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) == T38FAX_FILL_BIT_REMOVAL) - ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxFillBitRemoval\r\n"); - if ((p->t38.jointcapability & T38FAX_TRANSCODING_MMR) == T38FAX_TRANSCODING_MMR) - ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxTranscodingMMR\r\n"); - if ((p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) == T38FAX_TRANSCODING_JBIG) - ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxTranscodingJBIG\r\n"); - ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxRateManagement:%s\r\n", (p->t38.jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferredTCF"); - x = ast_udptl_get_local_max_datagram(p->udptl); - ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxMaxBuffer:%d\r\n",x); - ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxMaxDatagram:%d\r\n",x); - if (p->t38.jointcapability != T38FAX_UDP_EC_NONE) - ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxUdpEC:%s\r\n", (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC"); - len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m_modem) + strlen(a_modem); - add_header(resp, "Content-Type", "application/sdp"); - add_header_contentLength(resp, len); - add_line(resp, v); - add_line(resp, o); - add_line(resp, s); - add_line(resp, c); - add_line(resp, t); - add_line(resp, m_modem); - add_line(resp, a_modem); - - /* Update lastrtprx when we send our SDP */ - p->lastrtprx = p->lastrtptx = time(NULL); - - return 0; -} - - -/*! \brief Add RFC 2833 DTMF offer to SDP */ -static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate, - char **m_buf, size_t *m_size, char **a_buf, size_t *a_size, - int debug) -{ - int rtp_code; - - if (debug) - ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype(0, format, 0)); - if ((rtp_code = ast_rtp_lookup_code(p->rtp, 0, format)) == -1) - return; - - ast_build_string(m_buf, m_size, " %d", rtp_code); - ast_build_string(a_buf, a_size, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(0, format, 0), - sample_rate); - if (format == AST_RTP_DTMF) - /* Indicate we support DTMF and FLASH... */ - ast_build_string(a_buf, a_size, "a=fmtp:%d 0-16\r\n", rtp_code); -} - -/*! - * \note G.722 actually is supposed to specified as 8 kHz, even though it is - * really 16 kHz. Update this macro for other formats as they are added in - * the future. - */ -#define SDP_SAMPLE_RATE(x) 8000 - -/*! \brief Add Session Description Protocol message */ -static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p) -{ - int len = 0; - int alreadysent = 0; - - struct sockaddr_in sin; - struct sockaddr_in vsin; - struct sockaddr_in dest; - struct sockaddr_in vdest = { 0, }; - - /* SDP fields */ - char *version = "v=0\r\n"; /* Protocol version */ - char *subject = "s=session\r\n"; /* Subject of the session */ - char owner[256]; /* Session owner/creator */ - char connection[256]; /* Connection data */ - char *stime = "t=0 0\r\n"; /* Time the session is active */ - char bandwidth[256] = ""; /* Max bitrate */ - char *hold; - char m_audio[256]; /* Media declaration line for audio */ - char m_video[256]; /* Media declaration line for video */ - char a_audio[1024]; /* Attributes for audio */ - char a_video[1024]; /* Attributes for video */ - char *m_audio_next = m_audio; - char *m_video_next = m_video; - size_t m_audio_left = sizeof(m_audio); - size_t m_video_left = sizeof(m_video); - char *a_audio_next = a_audio; - char *a_video_next = a_video; - size_t a_audio_left = sizeof(a_audio); - size_t a_video_left = sizeof(a_video); - - int x; - int capability; - int needvideo = FALSE; - int debug = sip_debug_test_pvt(p); - int min_audio_packet_size = 0; - int min_video_packet_size = 0; - - m_video[0] = '\0'; /* Reset the video media string if it's not needed */ - - if (!p->rtp) { - ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n"); - return AST_FAILURE; - } - - /* Set RTP Session ID and version */ - if (!p->sessionid) { - p->sessionid = getpid(); - p->sessionversion = p->sessionid; - } else - p->sessionversion++; - - /* Get our addresses */ - ast_rtp_get_us(p->rtp, &sin); - if (p->vrtp) - ast_rtp_get_us(p->vrtp, &vsin); - - /* Is this a re-invite to move the media out, then use the original offer from caller */ - if (p->redirip.sin_addr.s_addr) { - dest.sin_port = p->redirip.sin_port; - dest.sin_addr = p->redirip.sin_addr; - } else { - dest.sin_addr = p->ourip; - dest.sin_port = sin.sin_port; - } - - capability = p->jointcapability; - - - if (option_debug > 1) { - char codecbuf[SIPBUFSIZE]; - ast_log(LOG_DEBUG, "** Our capability: %s Video flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability), ast_test_flag(&p->flags[0], SIP_NOVIDEO) ? "True" : "False"); - ast_log(LOG_DEBUG, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec)); - } - -#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS - if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP)) { - ast_build_string(&m_audio_next, &m_audio_left, " %d", 191); - ast_build_string(&a_audio_next, &a_audio_left, "a=rtpmap:%d %s/%d\r\n", 191, "t38", 8000); - } -#endif - - /* Check if we need video in this call */ - if ((capability & AST_FORMAT_VIDEO_MASK) && !ast_test_flag(&p->flags[0], SIP_NOVIDEO)) { - if (p->vrtp) { - needvideo = TRUE; - if (option_debug > 1) - ast_log(LOG_DEBUG, "This call needs video offers!\n"); - } else if (option_debug > 1) - ast_log(LOG_DEBUG, "This call needs video offers, but there's no video support enabled!\n"); - } - - - /* Ok, we need video. Let's add what we need for video and set codecs. - Video is handled differently than audio since we can not transcode. */ - if (needvideo) { - /* Determine video destination */ - if (p->vredirip.sin_addr.s_addr) { - vdest.sin_addr = p->vredirip.sin_addr; - vdest.sin_port = p->vredirip.sin_port; - } else { - vdest.sin_addr = p->ourip; - vdest.sin_port = vsin.sin_port; - } - ast_build_string(&m_video_next, &m_video_left, "m=video %d RTP/AVP", ntohs(vdest.sin_port)); - - /* Build max bitrate string */ - if (p->maxcallbitrate) - snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate); - if (debug) - ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(vsin.sin_port)); - } - - if (debug) - ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(sin.sin_port)); - - /* Start building generic SDP headers */ - - /* We break with the "recommendation" and send our IP, in order that our - peer doesn't have to ast_gethostbyname() us */ - - snprintf(owner, sizeof(owner), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr)); - snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr)); - ast_build_string(&m_audio_next, &m_audio_left, "m=audio %d RTP/AVP", ntohs(dest.sin_port)); - - if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR) - hold = "a=recvonly\r\n"; - else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_INACTIVE) - hold = "a=inactive\r\n"; - else - hold = "a=sendrecv\r\n"; - - /* Now, start adding audio codecs. These are added in this order: - - First what was requested by the calling channel - - Then preferences in order from sip.conf device config for this peer/user - - Then other codecs in capabilities, including video - */ - - /* Prefer the audio codec we were requested to use, first, no matter what - Note that p->prefcodec can include video codecs, so mask them out - */ - if (capability & p->prefcodec) { - int codec = p->prefcodec & AST_FORMAT_AUDIO_MASK; - - add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec), - &m_audio_next, &m_audio_left, - &a_audio_next, &a_audio_left, - debug, &min_audio_packet_size); - alreadysent |= codec; - } - - /* Start by sending our preferred audio codecs */ - for (x = 0; x < 32; x++) { - int codec; - - if (!(codec = ast_codec_pref_index(&p->prefs, x))) - break; - - if (!(capability & codec)) - continue; - - if (alreadysent & codec) - continue; - - add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec), - &m_audio_next, &m_audio_left, - &a_audio_next, &a_audio_left, - debug, &min_audio_packet_size); - alreadysent |= codec; - } - - /* Now send any other common audio and video codecs, and non-codec formats: */ - for (x = 1; x <= (needvideo ? AST_FORMAT_MAX_VIDEO : AST_FORMAT_MAX_AUDIO); x <<= 1) { - if (!(capability & x)) /* Codec not requested */ - continue; - - if (alreadysent & x) /* Already added to SDP */ - continue; - - if (x <= AST_FORMAT_MAX_AUDIO) - add_codec_to_sdp(p, x, SDP_SAMPLE_RATE(x), - &m_audio_next, &m_audio_left, - &a_audio_next, &a_audio_left, - debug, &min_audio_packet_size); - else - add_codec_to_sdp(p, x, 90000, - &m_video_next, &m_video_left, - &a_video_next, &a_video_left, - debug, &min_video_packet_size); - } - - /* Now add DTMF RFC2833 telephony-event as a codec */ - for (x = 1; x <= AST_RTP_MAX; x <<= 1) { - if (!(p->jointnoncodeccapability & x)) - continue; - - add_noncodec_to_sdp(p, x, 8000, - &m_audio_next, &m_audio_left, - &a_audio_next, &a_audio_left, - debug); - } - - if (option_debug > 2) - ast_log(LOG_DEBUG, "-- Done with adding codecs to SDP\n"); - - if (!p->owner || !ast_internal_timing_enabled(p->owner)) - ast_build_string(&a_audio_next, &a_audio_left, "a=silenceSupp:off - - - -\r\n"); - - if (min_audio_packet_size) - ast_build_string(&a_audio_next, &a_audio_left, "a=ptime:%d\r\n", min_audio_packet_size); - - if (min_video_packet_size) - ast_build_string(&a_video_next, &a_video_left, "a=ptime:%d\r\n", min_video_packet_size); - - if ((m_audio_left < 2) || (m_video_left < 2) || (a_audio_left == 0) || (a_video_left == 0)) - ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n"); - - ast_build_string(&m_audio_next, &m_audio_left, "\r\n"); - if (needvideo) - ast_build_string(&m_video_next, &m_video_left, "\r\n"); - - len = strlen(version) + strlen(subject) + strlen(owner) + strlen(connection) + strlen(stime) + strlen(m_audio) + strlen(a_audio) + strlen(hold); - if (needvideo) /* only if video response is appropriate */ - len += strlen(m_video) + strlen(a_video) + strlen(bandwidth) + strlen(hold); - - add_header(resp, "Content-Type", "application/sdp"); - add_header_contentLength(resp, len); - add_line(resp, version); - add_line(resp, owner); - add_line(resp, subject); - add_line(resp, connection); - if (needvideo) /* only if video response is appropriate */ - add_line(resp, bandwidth); - add_line(resp, stime); - add_line(resp, m_audio); - add_line(resp, a_audio); - add_line(resp, hold); - if (needvideo) { /* only if video response is appropriate */ - add_line(resp, m_video); - add_line(resp, a_video); - add_line(resp, hold); /* Repeat hold for the video stream */ - } - - /* Update lastrtprx when we send our SDP */ - p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */ - - if (option_debug > 2) { - char buf[SIPBUFSIZE]; - ast_log(LOG_DEBUG, "Done building SDP. Settling with this capability: %s\n", ast_getformatname_multiple(buf, SIPBUFSIZE, capability)); - } - - return AST_SUCCESS; -} - -/*! \brief Used for 200 OK and 183 early media */ -static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans) -{ - struct sip_request resp; - int seqno; - - if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) { - ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq")); - return -1; - } - respprep(&resp, p, msg, req); - if (p->udptl) { - ast_udptl_offered_from_local(p->udptl, 0); - add_t38_sdp(&resp, p); - } else - ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid); - if (retrans && !p->pendinginvite) - p->pendinginvite = seqno; /* Buggy clients sends ACK on RINGING too */ - return send_response(p, &resp, retrans, seqno); -} - -/*! \brief copy SIP request (mostly used to save request for responses) */ -static void copy_request(struct sip_request *dst, const struct sip_request *src) -{ - long offset; - int x; - offset = ((void *)dst) - ((void *)src); - /* First copy stuff */ - memcpy(dst, src, sizeof(*dst)); - /* Now fix pointer arithmetic */ - for (x=0; x < src->headers; x++) - dst->header[x] += offset; - for (x=0; x < src->lines; x++) - dst->line[x] += offset; - dst->rlPart1 += offset; - dst->rlPart2 += offset; -} - -/*! \brief Used for 200 OK and 183 early media - \return Will return XMIT_ERROR for network errors. -*/ -static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable) -{ - struct sip_request resp; - int seqno; - if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) { - ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq")); - return -1; - } - respprep(&resp, p, msg, req); - if (p->rtp) { - if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - if (option_debug) - ast_log(LOG_DEBUG, "Setting framing from config on incoming call\n"); - ast_rtp_codec_setpref(p->rtp, &p->prefs); - } - try_suggested_sip_codec(p); - add_sdp(&resp, p); - } else - ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid); - if (reliable && !p->pendinginvite) - p->pendinginvite = seqno; /* Buggy clients sends ACK on RINGING too */ - return send_response(p, &resp, reliable, seqno); -} - -/*! \brief Parse first line of incoming SIP request */ -static int determine_firstline_parts(struct sip_request *req) -{ - char *e = ast_skip_blanks(req->header[0]); /* there shouldn't be any */ - - if (!*e) - return -1; - req->rlPart1 = e; /* method or protocol */ - e = ast_skip_nonblanks(e); - if (*e) - *e++ = '\0'; - /* Get URI or status code */ - e = ast_skip_blanks(e); - if ( !*e ) - return -1; - ast_trim_blanks(e); - - if (!strcasecmp(req->rlPart1, "SIP/2.0") ) { /* We have a response */ - if (strlen(e) < 3) /* status code is 3 digits */ - return -1; - req->rlPart2 = e; - } else { /* We have a request */ - if ( *e == '<' ) { /* XXX the spec says it must not be in <> ! */ - ast_log(LOG_WARNING, "bogus uri in <> %s\n", e); - e++; - if (!*e) - return -1; - } - req->rlPart2 = e; /* URI */ - e = ast_skip_nonblanks(e); - if (*e) - *e++ = '\0'; - e = ast_skip_blanks(e); - if (strcasecmp(e, "SIP/2.0") ) { - ast_log(LOG_WARNING, "Bad request protocol %s\n", e); - return -1; - } - } - return 1; -} - -/*! \brief Transmit reinvite with SDP -\note A re-invite is basically a new INVITE with the same CALL-ID and TAG as the - INVITE that opened the SIP dialogue - We reinvite so that the audio stream (RTP) go directly between - the SIP UAs. SIP Signalling stays with * in the path. -*/ -static int transmit_reinvite_with_sdp(struct sip_pvt *p) -{ - struct sip_request req; - - reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1); - - add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); - if (sipdebug) - add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)"); - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) - append_history(p, "ReInv", "Re-invite sent"); - add_sdp(&req, p); - /* Use this as the basis */ - initialize_initreq(p, &req); - p->lastinvite = p->ocseq; - ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Change direction of this dialog */ - return send_request(p, &req, XMIT_CRITICAL, p->ocseq); -} - -/*! \brief Transmit reinvite with T38 SDP - We reinvite so that the T38 processing can take place. - SIP Signalling stays with * in the path. -*/ -static int transmit_reinvite_with_t38_sdp(struct sip_pvt *p) -{ - struct sip_request req; - - reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1); - - add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); - if (sipdebug) - add_header(&req, "X-asterisk-info", "SIP re-invite (T38 switchover)"); - ast_udptl_offered_from_local(p->udptl, 1); - add_t38_sdp(&req, p); - /* Use this as the basis */ - initialize_initreq(p, &req); - ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Change direction of this dialog */ - p->lastinvite = p->ocseq; - return send_request(p, &req, XMIT_CRITICAL, p->ocseq); -} - -/*! \brief Check Contact: URI of SIP message */ -static void extract_uri(struct sip_pvt *p, struct sip_request *req) -{ - char stripped[SIPBUFSIZE]; - char *c; - - ast_copy_string(stripped, get_header(req, "Contact"), sizeof(stripped)); - c = get_in_brackets(stripped); - c = strsep(&c, ";"); /* trim ; and beyond */ - if (!ast_strlen_zero(c)) - ast_string_field_set(p, uri, c); -} - -/*! \brief Build contact header - the contact header we send out */ -static void build_contact(struct sip_pvt *p) -{ - /* Construct Contact: header */ - if (ourport != STANDARD_SIP_PORT) - ast_string_field_build(p, our_contact, "<sip:%s%s%s:%d>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip), ourport); - else - ast_string_field_build(p, our_contact, "<sip:%s%s%s>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip)); -} - -/*! \brief Build the Remote Party-ID & From using callingpres options */ -static void build_rpid(struct sip_pvt *p) -{ - int send_pres_tags = TRUE; - const char *privacy=NULL; - const char *screen=NULL; - char buf[256]; - const char *clid = default_callerid; - const char *clin = NULL; - const char *fromdomain; - - if (!ast_strlen_zero(p->rpid) || !ast_strlen_zero(p->rpid_from)) - return; - - if (p->owner && p->owner->cid.cid_num) - clid = p->owner->cid.cid_num; - if (p->owner && p->owner->cid.cid_name) - clin = p->owner->cid.cid_name; - if (ast_strlen_zero(clin)) - clin = clid; - - switch (p->callingpres) { - case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED: - privacy = "off"; - screen = "no"; - break; - case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN: - privacy = "off"; - screen = "yes"; - break; - case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN: - privacy = "off"; - screen = "no"; - break; - case AST_PRES_ALLOWED_NETWORK_NUMBER: - privacy = "off"; - screen = "yes"; - break; - case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED: - privacy = "full"; - screen = "no"; - break; - case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN: - privacy = "full"; - screen = "yes"; - break; - case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN: - privacy = "full"; - screen = "no"; - break; - case AST_PRES_PROHIB_NETWORK_NUMBER: - privacy = "full"; - screen = "yes"; - break; - case AST_PRES_NUMBER_NOT_AVAILABLE: - send_pres_tags = FALSE; - break; - default: - ast_log(LOG_WARNING, "Unsupported callingpres (%d)\n", p->callingpres); - if ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED) - privacy = "full"; - else - privacy = "off"; - screen = "no"; - break; - } - - fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip)); - - snprintf(buf, sizeof(buf), "\"%s\" <sip:%s@%s>", clin, clid, fromdomain); - if (send_pres_tags) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ";privacy=%s;screen=%s", privacy, screen); - ast_string_field_set(p, rpid, buf); - - ast_string_field_build(p, rpid_from, "\"%s\" <sip:%s@%s>;tag=%s", clin, - S_OR(p->fromuser, clid), - fromdomain, p->tag); -} - -/*! \brief Initiate new SIP request to peer/user */ -static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod) -{ - char invite_buf[256] = ""; - char *invite = invite_buf; - size_t invite_max = sizeof(invite_buf); - char from[256]; - char to[256]; - char tmp[SIPBUFSIZE/2]; - char tmp2[SIPBUFSIZE/2]; - const char *l = NULL, *n = NULL; - const char *urioptions = ""; - - if (ast_test_flag(&p->flags[0], SIP_USEREQPHONE)) { - const char *s = p->username; /* being a string field, cannot be NULL */ - - /* Test p->username against allowed characters in AST_DIGIT_ANY - If it matches the allowed characters list, then sipuser = ";user=phone" - If not, then sipuser = "" - */ - /* + is allowed in first position in a tel: uri */ - if (*s == '+') - s++; - for (; *s; s++) { - if (!strchr(AST_DIGIT_ANYNUM, *s) ) - break; - } - /* If we have only digits, add ;user=phone to the uri */ - if (!*s) - urioptions = ";user=phone"; - } - - - snprintf(p->lastmsg, sizeof(p->lastmsg), "Init: %s", sip_methods[sipmethod].text); - - if (p->owner) { - l = p->owner->cid.cid_num; - n = p->owner->cid.cid_name; - } - /* if we are not sending RPID and user wants his callerid restricted */ - if (!ast_test_flag(&p->flags[0], SIP_SENDRPID) && - ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)) { - l = CALLERID_UNKNOWN; - n = l; - } - if (ast_strlen_zero(l)) - l = default_callerid; - if (ast_strlen_zero(n)) - n = l; - /* Allow user to be overridden */ - if (!ast_strlen_zero(p->fromuser)) - l = p->fromuser; - else /* Save for any further attempts */ - ast_string_field_set(p, fromuser, l); - - /* Allow user to be overridden */ - if (!ast_strlen_zero(p->fromname)) - n = p->fromname; - else /* Save for any further attempts */ - ast_string_field_set(p, fromname, n); - - if (pedanticsipchecking) { - ast_uri_encode(n, tmp, sizeof(tmp), 0); - n = tmp; - ast_uri_encode(l, tmp2, sizeof(tmp2), 0); - l = tmp2; - } - - if (ourport != STANDARD_SIP_PORT && ast_strlen_zero(p->fromdomain)) - snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s:%d>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip)), ourport, p->tag); - else - snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip)), p->tag); - - /* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */ - if (!ast_strlen_zero(p->fullcontact)) { - /* If we have full contact, trust it */ - ast_build_string(&invite, &invite_max, "%s", p->fullcontact); - } else { - /* Otherwise, use the username while waiting for registration */ - ast_build_string(&invite, &invite_max, "sip:"); - if (!ast_strlen_zero(p->username)) { - n = p->username; - if (pedanticsipchecking) { - ast_uri_encode(n, tmp, sizeof(tmp), 0); - n = tmp; - } - ast_build_string(&invite, &invite_max, "%s@", n); - } - ast_build_string(&invite, &invite_max, "%s", p->tohost); - if (ntohs(p->sa.sin_port) != STANDARD_SIP_PORT) - ast_build_string(&invite, &invite_max, ":%d", ntohs(p->sa.sin_port)); - ast_build_string(&invite, &invite_max, "%s", urioptions); - } - - /* If custom URI options have been provided, append them */ - if (p->options && !ast_strlen_zero(p->options->uri_options)) - ast_build_string(&invite, &invite_max, ";%s", p->options->uri_options); - - ast_string_field_set(p, uri, invite_buf); - - if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->theirtag)) { - /* If this is a NOTIFY, use the From: tag in the subscribe (RFC 3265) */ - snprintf(to, sizeof(to), "<%s%s>;tag=%s", (!strncasecmp(p->uri, "sip:", 4) ? "" : "sip:"), p->uri, p->theirtag); - } else if (p->options && p->options->vxml_url) { - /* If there is a VXML URL append it to the SIP URL */ - snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url); - } else - snprintf(to, sizeof(to), "<%s>", p->uri); - - init_req(req, sipmethod, p->uri); - snprintf(tmp, sizeof(tmp), "%d %s", ++p->ocseq, sip_methods[sipmethod].text); - - add_header(req, "Via", p->via); - /* SLD: FIXME?: do Route: here too? I think not cos this is the first request. - * OTOH, then we won't have anything in p->route anyway */ - /* Build Remote Party-ID and From */ - if (ast_test_flag(&p->flags[0], SIP_SENDRPID) && (sipmethod == SIP_INVITE)) { - build_rpid(p); - add_header(req, "From", p->rpid_from); - } else - add_header(req, "From", from); - add_header(req, "To", to); - ast_string_field_set(p, exten, l); - build_contact(p); - add_header(req, "Contact", p->our_contact); - add_header(req, "Call-ID", p->callid); - add_header(req, "CSeq", tmp); - if (!ast_strlen_zero(global_useragent)) - add_header(req, "User-Agent", global_useragent); - add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS); - if (!ast_strlen_zero(p->rpid)) - add_header(req, "Remote-Party-ID", p->rpid); -} - -/*! \brief Build REFER/INVITE/OPTIONS message and transmit it */ -static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init) -{ - struct sip_request req; - - req.method = sipmethod; - if (init) { /* Seems like init always is 2 */ - /* Bump branch even on initial requests */ - p->branch ^= ast_random(); - p->invite_branch = p->branch; - build_via(p); - if (init > 1) - initreqprep(&req, p, sipmethod); - else - reqprep(&req, p, sipmethod, 0, 1); - } else - reqprep(&req, p, sipmethod, 0, 1); - - if (p->options && p->options->auth) - add_header(&req, p->options->authheader, p->options->auth); - append_date(&req); - if (sipmethod == SIP_REFER) { /* Call transfer */ - if (p->refer) { - char buf[SIPBUFSIZE]; - if (!ast_strlen_zero(p->refer->refer_to)) - add_header(&req, "Refer-To", p->refer->refer_to); - if (!ast_strlen_zero(p->refer->referred_by)) { - snprintf(buf, sizeof(buf), "%s <%s>", p->refer->referred_by_name, p->refer->referred_by); - add_header(&req, "Referred-By", buf); - } - } - } - /* This new INVITE is part of an attended transfer. Make sure that the - other end knows and replace the current call with this new call */ - if (p->options && p->options->replaces && !ast_strlen_zero(p->options->replaces)) { - add_header(&req, "Replaces", p->options->replaces); - add_header(&req, "Require", "replaces"); - } - - add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); - if (p->options && p->options->addsipheaders && p->owner) { - struct ast_channel *chan = p->owner; /* The owner channel */ - struct varshead *headp; - - ast_channel_lock(chan); - - headp = &chan->varshead; - - if (!headp) - ast_log(LOG_WARNING,"No Headp for the channel...ooops!\n"); - else { - const struct ast_var_t *current; - AST_LIST_TRAVERSE(headp, current, entries) { - /* SIPADDHEADER: Add SIP header to outgoing call */ - if (!strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) { - char *content, *end; - const char *header = ast_var_value(current); - char *headdup = ast_strdupa(header); - - /* Strip of the starting " (if it's there) */ - if (*headdup == '"') - headdup++; - if ((content = strchr(headdup, ':'))) { - *content++ = '\0'; - content = ast_skip_blanks(content); /* Skip white space */ - /* Strip the ending " (if it's there) */ - end = content + strlen(content) -1; - if (*end == '"') - *end = '\0'; - - add_header(&req, headdup, content); - if (sipdebug) - ast_log(LOG_DEBUG, "Adding SIP Header \"%s\" with content :%s: \n", headdup, content); - } - } - } - } - - ast_channel_unlock(chan); - } - if (sdp) { - if (p->udptl && (p->t38.state == T38_LOCAL_DIRECT || p->t38.state == T38_LOCAL_REINVITE)) { - ast_udptl_offered_from_local(p->udptl, 1); - if (option_debug) - ast_log(LOG_DEBUG, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); - add_t38_sdp(&req, p); - } else if (p->rtp) - add_sdp(&req, p); - } else { - add_header_contentLength(&req, 0); - } - - if (!p->initreq.headers || init > 2) - initialize_initreq(p, &req); - p->lastinvite = p->ocseq; - return send_request(p, &req, init ? XMIT_CRITICAL : XMIT_RELIABLE, p->ocseq); -} - -/*! \brief Used in the SUBSCRIBE notification subsystem */ -static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout) -{ - char tmp[4000], from[256], to[256]; - char *t = tmp, *c, *mfrom, *mto; - size_t maxbytes = sizeof(tmp); - struct sip_request req; - char hint[AST_MAX_EXTENSION]; - char *statestring = "terminated"; - const struct cfsubscription_types *subscriptiontype; - enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN; - char *pidfstate = "--"; - char *pidfnote= "Ready"; - - memset(from, 0, sizeof(from)); - memset(to, 0, sizeof(to)); - memset(tmp, 0, sizeof(tmp)); - - switch (state) { - case (AST_EXTENSION_RINGING | AST_EXTENSION_INUSE): - statestring = (global_notifyringing) ? "early" : "confirmed"; - local_state = NOTIFY_INUSE; - pidfstate = "busy"; - pidfnote = "Ringing"; - break; - case AST_EXTENSION_RINGING: - statestring = "early"; - local_state = NOTIFY_INUSE; - pidfstate = "busy"; - pidfnote = "Ringing"; - break; - case AST_EXTENSION_INUSE: - statestring = "confirmed"; - local_state = NOTIFY_INUSE; - pidfstate = "busy"; - pidfnote = "On the phone"; - break; - case AST_EXTENSION_BUSY: - statestring = "confirmed"; - local_state = NOTIFY_CLOSED; - pidfstate = "busy"; - pidfnote = "On the phone"; - break; - case AST_EXTENSION_UNAVAILABLE: - statestring = "terminated"; - local_state = NOTIFY_CLOSED; - pidfstate = "away"; - pidfnote = "Unavailable"; - break; - case AST_EXTENSION_ONHOLD: - statestring = "confirmed"; - local_state = NOTIFY_CLOSED; - pidfstate = "busy"; - pidfnote = "On Hold"; - break; - case AST_EXTENSION_NOT_INUSE: - default: - /* Default setting */ - break; - } - - subscriptiontype = find_subscription_type(p->subscribed); - - /* Check which device/devices we are watching and if they are registered */ - if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten)) { - char *hint2 = hint, *individual_hint = NULL; - int hint_count = 0, unavailable_count = 0; - - while ((individual_hint = strsep(&hint2, "&"))) { - hint_count++; - - if (ast_device_state(individual_hint) == AST_DEVICE_UNAVAILABLE) - unavailable_count++; - } - - /* If none of the hinted devices are registered, we will - * override notification and show no availability. - */ - if (hint_count > 0 && hint_count == unavailable_count) { - local_state = NOTIFY_CLOSED; - pidfstate = "away"; - pidfnote = "Not online"; - } - } - - ast_copy_string(from, get_header(&p->initreq, "From"), sizeof(from)); - c = get_in_brackets(from); - if (strncasecmp(c, "sip:", 4)) { - ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); - return -1; - } - mfrom = strsep(&c, ";"); /* trim ; and beyond */ - - ast_copy_string(to, get_header(&p->initreq, "To"), sizeof(to)); - c = get_in_brackets(to); - if (strncasecmp(c, "sip:", 4)) { - ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); - return -1; - } - mto = strsep(&c, ";"); /* trim ; and beyond */ - - reqprep(&req, p, SIP_NOTIFY, 0, 1); - - - add_header(&req, "Event", subscriptiontype->event); - add_header(&req, "Content-Type", subscriptiontype->mediatype); - switch(state) { - case AST_EXTENSION_DEACTIVATED: - if (timeout) - add_header(&req, "Subscription-State", "terminated;reason=timeout"); - else { - add_header(&req, "Subscription-State", "terminated;reason=probation"); - add_header(&req, "Retry-After", "60"); - } - break; - case AST_EXTENSION_REMOVED: - add_header(&req, "Subscription-State", "terminated;reason=noresource"); - break; - default: - if (p->expiry) - add_header(&req, "Subscription-State", "active"); - else /* Expired */ - add_header(&req, "Subscription-State", "terminated;reason=timeout"); - } - switch (p->subscribed) { - case XPIDF_XML: - case CPIM_PIDF_XML: - ast_build_string(&t, &maxbytes, "<?xml version=\"1.0\"?>\n"); - ast_build_string(&t, &maxbytes, "<!DOCTYPE presence PUBLIC \"-//IETF//DTD RFCxxxx XPIDF 1.0//EN\" \"xpidf.dtd\">\n"); - ast_build_string(&t, &maxbytes, "<presence>\n"); - ast_build_string(&t, &maxbytes, "<presentity uri=\"%s;method=SUBSCRIBE\" />\n", mfrom); - ast_build_string(&t, &maxbytes, "<atom id=\"%s\">\n", p->exten); - ast_build_string(&t, &maxbytes, "<address uri=\"%s;user=ip\" priority=\"0.800000\">\n", mto); - ast_build_string(&t, &maxbytes, "<status status=\"%s\" />\n", (local_state == NOTIFY_OPEN) ? "open" : (local_state == NOTIFY_INUSE) ? "inuse" : "closed"); - ast_build_string(&t, &maxbytes, "<msnsubstatus substatus=\"%s\" />\n", (local_state == NOTIFY_OPEN) ? "online" : (local_state == NOTIFY_INUSE) ? "onthephone" : "offline"); - ast_build_string(&t, &maxbytes, "</address>\n</atom>\n</presence>\n"); - break; - case PIDF_XML: /* Eyebeam supports this format */ - ast_build_string(&t, &maxbytes, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"); - ast_build_string(&t, &maxbytes, "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" \nxmlns:pp=\"urn:ietf:params:xml:ns:pidf:person\"\nxmlns:es=\"urn:ietf:params:xml:ns:pidf:rpid:status:rpid-status\"\nxmlns:ep=\"urn:ietf:params:xml:ns:pidf:rpid:rpid-person\"\nentity=\"%s\">\n", mfrom); - ast_build_string(&t, &maxbytes, "<pp:person><status>\n"); - if (pidfstate[0] != '-') - ast_build_string(&t, &maxbytes, "<ep:activities><ep:%s/></ep:activities>\n", pidfstate); - ast_build_string(&t, &maxbytes, "</status></pp:person>\n"); - ast_build_string(&t, &maxbytes, "<note>%s</note>\n", pidfnote); /* Note */ - ast_build_string(&t, &maxbytes, "<tuple id=\"%s\">\n", p->exten); /* Tuple start */ - ast_build_string(&t, &maxbytes, "<contact priority=\"1\">%s</contact>\n", mto); - if (pidfstate[0] == 'b') /* Busy? Still open ... */ - ast_build_string(&t, &maxbytes, "<status><basic>open</basic></status>\n"); - else - ast_build_string(&t, &maxbytes, "<status><basic>%s</basic></status>\n", (local_state != NOTIFY_CLOSED) ? "open" : "closed"); - ast_build_string(&t, &maxbytes, "</tuple>\n</presence>\n"); - break; - case DIALOG_INFO_XML: /* SNOM subscribes in this format */ - ast_build_string(&t, &maxbytes, "<?xml version=\"1.0\"?>\n"); - ast_build_string(&t, &maxbytes, "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\"%d\" state=\"%s\" entity=\"%s\">\n", p->dialogver++, full ? "full":"partial", mto); - if ((state & AST_EXTENSION_RINGING) && global_notifyringing) - ast_build_string(&t, &maxbytes, "<dialog id=\"%s\" direction=\"recipient\">\n", p->exten); - else - ast_build_string(&t, &maxbytes, "<dialog id=\"%s\">\n", p->exten); - ast_build_string(&t, &maxbytes, "<state>%s</state>\n", statestring); - if (state == AST_EXTENSION_ONHOLD) { - ast_build_string(&t, &maxbytes, "<local>\n<target uri=\"%s\">\n" - "<param pname=\"+sip.rendering\" pvalue=\"no\"/>\n" - "</target>\n</local>\n", mto); - } - ast_build_string(&t, &maxbytes, "</dialog>\n</dialog-info>\n"); - break; - case NONE: - default: - break; - } - - if (t > tmp + sizeof(tmp)) - ast_log(LOG_WARNING, "Buffer overflow detected!! (Please file a bug report)\n"); - - add_header_contentLength(&req, strlen(tmp)); - add_line(&req, tmp); - p->pendinginvite = p->ocseq; /* Remember that we have a pending NOTIFY in order not to confuse the NOTIFY subsystem */ - - return send_request(p, &req, XMIT_RELIABLE, p->ocseq); -} - -/*! \brief Notify user of messages waiting in voicemail -\note - Notification only works for registered peers with mailbox= definitions - in sip.conf - - We use the SIP Event package message-summary - MIME type defaults to "application/simple-message-summary"; - */ -static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, char *vmexten) -{ - struct sip_request req; - char tmp[500]; - char *t = tmp; - size_t maxbytes = sizeof(tmp); - - initreqprep(&req, p, SIP_NOTIFY); - add_header(&req, "Event", "message-summary"); - add_header(&req, "Content-Type", default_notifymime); - - ast_build_string(&t, &maxbytes, "Messages-Waiting: %s\r\n", newmsgs ? "yes" : "no"); - ast_build_string(&t, &maxbytes, "Message-Account: sip:%s@%s\r\n", - S_OR(vmexten, default_vmexten), S_OR(p->fromdomain, ast_inet_ntoa(p->ourip))); - /* Cisco has a bug in the SIP stack where it can't accept the - (0/0) notification. This can temporarily be disabled in - sip.conf with the "buggymwi" option */ - ast_build_string(&t, &maxbytes, "Voice-Message: %d/%d%s\r\n", newmsgs, oldmsgs, (ast_test_flag(&p->flags[1], SIP_PAGE2_BUGGY_MWI) ? "" : " (0/0)")); - - if (p->subscribed) { - if (p->expiry) - add_header(&req, "Subscription-State", "active"); - else /* Expired */ - add_header(&req, "Subscription-State", "terminated;reason=timeout"); - } - - if (t > tmp + sizeof(tmp)) - ast_log(LOG_WARNING, "Buffer overflow detected!! (Please file a bug report)\n"); - - add_header_contentLength(&req, strlen(tmp)); - add_line(&req, tmp); - - if (!p->initreq.headers) - initialize_initreq(p, &req); - return send_request(p, &req, XMIT_RELIABLE, p->ocseq); -} - -/*! \brief Transmit SIP request unreliably (only used in sip_notify subsystem) */ -static int transmit_sip_request(struct sip_pvt *p, struct sip_request *req) -{ - if (!p->initreq.headers) /* Initialize first request before sending */ - initialize_initreq(p, req); - return send_request(p, req, XMIT_UNRELIABLE, p->ocseq); -} - -/*! \brief Notify a transferring party of the status of transfer */ -static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate) -{ - struct sip_request req; - char tmp[SIPBUFSIZE/2]; - - reqprep(&req, p, SIP_NOTIFY, 0, 1); - snprintf(tmp, sizeof(tmp), "refer;id=%d", cseq); - add_header(&req, "Event", tmp); - add_header(&req, "Subscription-state", terminate ? "terminated;reason=noresource" : "active"); - add_header(&req, "Content-Type", "message/sipfrag;version=2.0"); - add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); - - snprintf(tmp, sizeof(tmp), "SIP/2.0 %s\r\n", message); - add_header_contentLength(&req, strlen(tmp)); - add_line(&req, tmp); - - if (!p->initreq.headers) - initialize_initreq(p, &req); - - p->lastnoninvite = p->ocseq; - - return send_request(p, &req, XMIT_RELIABLE, p->ocseq); -} - -/*! \brief Convert registration state status to string */ -static char *regstate2str(enum sipregistrystate regstate) -{ - switch(regstate) { - case REG_STATE_FAILED: - return "Failed"; - case REG_STATE_UNREGISTERED: - return "Unregistered"; - case REG_STATE_REGSENT: - return "Request Sent"; - case REG_STATE_AUTHSENT: - return "Auth. Sent"; - case REG_STATE_REGISTERED: - return "Registered"; - case REG_STATE_REJECTED: - return "Rejected"; - case REG_STATE_TIMEOUT: - return "Timeout"; - case REG_STATE_NOAUTH: - return "No Authentication"; - default: - return "Unknown"; - } -} - -/*! \brief Update registration with SIP Proxy */ -static int sip_reregister(const void *data) -{ - /* if we are here, we know that we need to reregister. */ - struct sip_registry *r= ASTOBJ_REF((struct sip_registry *) data); - - /* if we couldn't get a reference to the registry object, punt */ - if (!r) - return 0; - - if (r->call && !ast_test_flag(&r->call->flags[0], SIP_NO_HISTORY)) - append_history(r->call, "RegistryRenew", "Account: %s@%s", r->username, r->hostname); - /* Since registry's are only added/removed by the the monitor thread, this - may be overkill to reference/dereference at all here */ - if (sipdebug) - ast_log(LOG_NOTICE, " -- Re-registration for %s@%s\n", r->username, r->hostname); - - r->expire = -1; - __sip_do_register(r); - ASTOBJ_UNREF(r, sip_registry_destroy); - return 0; -} - -/*! \brief Register with SIP proxy */ -static int __sip_do_register(struct sip_registry *r) -{ - int res; - - res = transmit_register(r, SIP_REGISTER, NULL, NULL); - return res; -} - -/*! \brief Registration timeout, register again */ -static int sip_reg_timeout(const void *data) -{ - - /* if we are here, our registration timed out, so we'll just do it over */ - struct sip_registry *r = ASTOBJ_REF((struct sip_registry *) data); - struct sip_pvt *p; - int res; - - /* if we couldn't get a reference to the registry object, punt */ - if (!r) - return 0; - - ast_log(LOG_NOTICE, " -- Registration for '%s@%s' timed out, trying again (Attempt #%d)\n", r->username, r->hostname, r->regattempts); - if (r->call) { - /* Unlink us, destroy old call. Locking is not relevant here because all this happens - in the single SIP manager thread. */ - p = r->call; - ast_mutex_lock(&p->lock); - if (p->registry) - ASTOBJ_UNREF(p->registry, sip_registry_destroy); - r->call = NULL; - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - /* Pretend to ACK anything just in case */ - __sip_pretend_ack(p); - ast_mutex_unlock(&p->lock); - } - /* If we have a limit, stop registration and give up */ - if (global_regattempts_max && (r->regattempts > global_regattempts_max)) { - /* Ok, enough is enough. Don't try any more */ - /* We could add an external notification here... - steal it from app_voicemail :-) */ - ast_log(LOG_NOTICE, " -- Giving up forever trying to register '%s@%s'\n", r->username, r->hostname); - r->regstate = REG_STATE_FAILED; - } else { - r->regstate = REG_STATE_UNREGISTERED; - r->timeout = -1; - res=transmit_register(r, SIP_REGISTER, NULL, NULL); - } - manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelDriver: SIP\r\nUsername: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate)); - ASTOBJ_UNREF(r, sip_registry_destroy); - return 0; -} - -/*! \brief Transmit register to SIP proxy or UA */ -static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader) -{ - struct sip_request req; - char from[256]; - char to[256]; - char tmp[80]; - char addr[80]; - struct sip_pvt *p; - char *fromdomain; - - /* exit if we are already in process with this registrar ?*/ - if ( r == NULL || ((auth==NULL) && (r->regstate==REG_STATE_REGSENT || r->regstate==REG_STATE_AUTHSENT))) { - if (r) { - ast_log(LOG_NOTICE, "Strange, trying to register %s@%s when registration already pending\n", r->username, r->hostname); - } - return 0; - } - - if (r->call) { /* We have a registration */ - if (!auth) { - ast_log(LOG_WARNING, "Already have a REGISTER going on to %s@%s?? \n", r->username, r->hostname); - return 0; - } else { - p = r->call; - make_our_tag(p->tag, sizeof(p->tag)); /* create a new local tag for every register attempt */ - ast_string_field_free(p, theirtag); /* forget their old tag, so we don't match tags when getting response */ - } - } else { - /* Build callid for registration if we haven't registered before */ - if (!r->callid_valid) { - build_callid_registry(r, __ourip, default_fromdomain); - r->callid_valid = TRUE; - } - /* Allocate SIP packet for registration */ - if (!(p = sip_alloc( r->callid, NULL, 0, SIP_REGISTER))) { - ast_log(LOG_WARNING, "Unable to allocate registration transaction (memory or socket error)\n"); - return 0; - } - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) - append_history(p, "RegistryInit", "Account: %s@%s", r->username, r->hostname); - /* Find address to hostname */ - if (create_addr(p, r->hostname)) { - /* we have what we hope is a temporary network error, - * probably DNS. We need to reschedule a registration try */ - sip_destroy(p); - - if (r->timeout > -1) - ast_log(LOG_WARNING, "Still have a registration timeout for %s@%s (create_addr() error), %d\n", r->username, r->hostname, r->timeout); - else - ast_log(LOG_WARNING, "Probably a DNS error for registration to %s@%s, trying REGISTER again (after %d seconds)\n", r->username, r->hostname, global_reg_timeout); - - AST_SCHED_DEL(sched, r->timeout); - r->timeout = ast_sched_add(sched, global_reg_timeout * 1000, sip_reg_timeout, r); - r->regattempts++; - return 0; - } - /* Copy back Call-ID in case create_addr changed it */ - ast_string_field_set(r, callid, p->callid); - if (r->portno) { - p->sa.sin_port = htons(r->portno); - p->recv.sin_port = htons(r->portno); - } else /* Set registry port to the port set from the peer definition/srv or default */ - r->portno = ntohs(p->sa.sin_port); - ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Registration is outgoing call */ - r->call=p; /* Save pointer to SIP packet */ - p->registry = ASTOBJ_REF(r); /* Add pointer to registry in packet */ - if (!ast_strlen_zero(r->secret)) /* Secret (password) */ - ast_string_field_set(p, peersecret, r->secret); - if (!ast_strlen_zero(r->md5secret)) - ast_string_field_set(p, peermd5secret, r->md5secret); - /* User name in this realm - - if authuser is set, use that, otherwise use username */ - if (!ast_strlen_zero(r->authuser)) { - ast_string_field_set(p, peername, r->authuser); - ast_string_field_set(p, authname, r->authuser); - } else if (!ast_strlen_zero(r->username)) { - ast_string_field_set(p, peername, r->username); - ast_string_field_set(p, authname, r->username); - ast_string_field_set(p, fromuser, r->username); - } - if (!ast_strlen_zero(r->username)) - ast_string_field_set(p, username, r->username); - /* Save extension in packet */ - ast_string_field_set(p, exten, r->contact); - - /* - check which address we should use in our contact header - based on whether the remote host is on the external or - internal network so we can register through nat - */ - if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip)) - p->ourip = bindaddr.sin_addr; - build_contact(p); - } - - /* set up a timeout */ - if (auth == NULL) { - if (r->timeout > -1) - ast_log(LOG_WARNING, "Still have a registration timeout, #%d - deleting it\n", r->timeout); - AST_SCHED_DEL(sched, r->timeout); - r->timeout = ast_sched_add(sched, global_reg_timeout * 1000, sip_reg_timeout, r); - if (option_debug) - ast_log(LOG_DEBUG, "Scheduled a registration timeout for %s id #%d \n", r->hostname, r->timeout); - } - - if ((fromdomain = strchr(r->username, '@'))) { - /* the domain name is just behind '@' */ - fromdomain++ ; - /* We have a domain in the username for registration */ - snprintf(from, sizeof(from), "<sip:%s>;tag=%s", r->username, p->tag); - if (!ast_strlen_zero(p->theirtag)) - snprintf(to, sizeof(to), "<sip:%s>;tag=%s", r->username, p->theirtag); - else - snprintf(to, sizeof(to), "<sip:%s>", r->username); - - /* If the registration username contains '@', then the domain should be used as - the equivalent of "fromdomain" for the registration */ - if (ast_strlen_zero(p->fromdomain)) { - ast_string_field_set(p, fromdomain, fromdomain); - } - } else { - snprintf(from, sizeof(from), "<sip:%s@%s>;tag=%s", r->username, p->tohost, p->tag); - if (!ast_strlen_zero(p->theirtag)) - snprintf(to, sizeof(to), "<sip:%s@%s>;tag=%s", r->username, p->tohost, p->theirtag); - else - snprintf(to, sizeof(to), "<sip:%s@%s>", r->username, p->tohost); - } - - /* Fromdomain is what we are registering to, regardless of actual - host name from SRV */ - if (!ast_strlen_zero(p->fromdomain)) { - if (r->portno && r->portno != STANDARD_SIP_PORT) - snprintf(addr, sizeof(addr), "sip:%s:%d", p->fromdomain, r->portno); - else - snprintf(addr, sizeof(addr), "sip:%s", p->fromdomain); - } else { - if (r->portno && r->portno != STANDARD_SIP_PORT) - snprintf(addr, sizeof(addr), "sip:%s:%d", r->hostname, r->portno); - else - snprintf(addr, sizeof(addr), "sip:%s", r->hostname); - } - ast_string_field_set(p, uri, addr); - - p->branch ^= ast_random(); - - init_req(&req, sipmethod, addr); - - /* Add to CSEQ */ - snprintf(tmp, sizeof(tmp), "%u %s", ++r->ocseq, sip_methods[sipmethod].text); - p->ocseq = r->ocseq; - - build_via(p); - add_header(&req, "Via", p->via); - add_header(&req, "From", from); - add_header(&req, "To", to); - add_header(&req, "Call-ID", p->callid); - add_header(&req, "CSeq", tmp); - if (!ast_strlen_zero(global_useragent)) - add_header(&req, "User-Agent", global_useragent); - add_header(&req, "Max-Forwards", DEFAULT_MAX_FORWARDS); - - - if (auth) /* Add auth header */ - add_header(&req, authheader, auth); - else if (!ast_strlen_zero(r->nonce)) { - char digest[1024]; - - /* We have auth data to reuse, build a digest header! */ - if (sipdebug) - ast_log(LOG_DEBUG, " >>> Re-using Auth data for %s@%s\n", r->username, r->hostname); - ast_string_field_set(p, realm, r->realm); - ast_string_field_set(p, nonce, r->nonce); - ast_string_field_set(p, domain, r->domain); - ast_string_field_set(p, opaque, r->opaque); - ast_string_field_set(p, qop, r->qop); - r->noncecount++; - p->noncecount = r->noncecount; - - memset(digest,0,sizeof(digest)); - if(!build_reply_digest(p, sipmethod, digest, sizeof(digest))) - add_header(&req, "Authorization", digest); - else - ast_log(LOG_NOTICE, "No authorization available for authentication of registration to %s@%s\n", r->username, r->hostname); - - } - - snprintf(tmp, sizeof(tmp), "%d", default_expiry); - add_header(&req, "Expires", tmp); - add_header(&req, "Contact", p->our_contact); - add_header(&req, "Event", "registration"); - add_header_contentLength(&req, 0); - - initialize_initreq(p, &req); - if (sip_debug_test_pvt(p)) - ast_verbose("REGISTER %d headers, %d lines\n", p->initreq.headers, p->initreq.lines); - r->regstate = auth ? REG_STATE_AUTHSENT : REG_STATE_REGSENT; - r->regattempts++; /* Another attempt */ - if (option_debug > 3) - ast_verbose("REGISTER attempt %d to %s@%s\n", r->regattempts, r->username, r->hostname); - return send_request(p, &req, XMIT_CRITICAL, p->ocseq); -} - -/*! \brief Transmit text with SIP MESSAGE method */ -static int transmit_message_with_text(struct sip_pvt *p, const char *text) -{ - struct sip_request req; - - reqprep(&req, p, SIP_MESSAGE, 0, 1); - add_text(&req, text); - return send_request(p, &req, XMIT_RELIABLE, p->ocseq); -} - -/*! \brief Allocate SIP refer structure */ -static int sip_refer_allocate(struct sip_pvt *p) -{ - p->refer = ast_calloc(1, sizeof(struct sip_refer)); - return p->refer ? 1 : 0; -} - -/*! \brief Transmit SIP REFER message (initiated by the transfer() dialplan application - \note this is currently broken as we have no way of telling the dialplan - engine whether a transfer succeeds or fails. - \todo Fix the transfer() dialplan function so that a transfer may fail -*/ -static int transmit_refer(struct sip_pvt *p, const char *dest) -{ - struct sip_request req = { - .headers = 0, - }; - char from[256]; - const char *of; - char *c; - char referto[256]; - char *ttag, *ftag; - char *theirtag = ast_strdupa(p->theirtag); - - if (option_debug || sipdebug) - ast_log(LOG_DEBUG, "SIP transfer of %s to %s\n", p->callid, dest); - - /* Are we transfering an inbound or outbound call ? */ - if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - of = get_header(&p->initreq, "To"); - ttag = theirtag; - ftag = p->tag; - } else { - of = get_header(&p->initreq, "From"); - ftag = theirtag; - ttag = p->tag; - } - - ast_copy_string(from, of, sizeof(from)); - of = get_in_brackets(from); - ast_string_field_set(p, from, of); - if (strncasecmp(of, "sip:", 4)) - ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n"); - else - of += 4; - /* Get just the username part */ - if ((c = strchr(dest, '@'))) - c = NULL; - else if ((c = strchr(of, '@'))) - *c++ = '\0'; - if (c) - snprintf(referto, sizeof(referto), "<sip:%s@%s>", dest, c); - else - snprintf(referto, sizeof(referto), "<sip:%s>", dest); - - /* save in case we get 407 challenge */ - sip_refer_allocate(p); - ast_copy_string(p->refer->refer_to, referto, sizeof(p->refer->refer_to)); - ast_copy_string(p->refer->referred_by, p->our_contact, sizeof(p->refer->referred_by)); - p->refer->status = REFER_SENT; /* Set refer status */ - - reqprep(&req, p, SIP_REFER, 0, 1); - - add_header(&req, "Refer-To", referto); - add_header(&req, "Allow", ALLOWED_METHODS); - add_header(&req, "Supported", SUPPORTED_EXTENSIONS); - if (!ast_strlen_zero(p->our_contact)) - add_header(&req, "Referred-By", p->our_contact); - - return send_request(p, &req, XMIT_RELIABLE, p->ocseq); - /* We should propably wait for a NOTIFY here until we ack the transfer */ - /* Maybe fork a new thread and wait for a STATUS of REFER_200OK on the refer status before returning to app_transfer */ - - /*! \todo In theory, we should hang around and wait for a reply, before - returning to the dial plan here. Don't know really how that would - affect the transfer() app or the pbx, but, well, to make this - useful we should have a STATUS code on transfer(). - */ -} - - -/*! \brief Send SIP INFO dtmf message, see Cisco documentation on cisco.com */ -static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration) -{ - struct sip_request req; - - reqprep(&req, p, SIP_INFO, 0, 1); - add_digit(&req, digit, duration); - return send_request(p, &req, XMIT_RELIABLE, p->ocseq); -} - -/*! \brief Send SIP INFO with video update request */ -static int transmit_info_with_vidupdate(struct sip_pvt *p) -{ - struct sip_request req; - - reqprep(&req, p, SIP_INFO, 0, 1); - add_vidupdate(&req); - return send_request(p, &req, XMIT_RELIABLE, p->ocseq); -} - -/*! \brief Transmit generic SIP request - returns XMIT_ERROR if transmit failed with a critical error (don't retry) -*/ -static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch) -{ - struct sip_request resp; - - if (sipmethod == SIP_ACK) - p->invitestate = INV_CONFIRMED; - - reqprep(&resp, p, sipmethod, seqno, newbranch); - add_header_contentLength(&resp, 0); - return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq); -} - -/*! \brief Transmit SIP request, auth added */ -static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch) -{ - struct sip_request resp; - - reqprep(&resp, p, sipmethod, seqno, newbranch); - if (!ast_strlen_zero(p->realm)) { - char digest[1024]; - - memset(digest, 0, sizeof(digest)); - if(!build_reply_digest(p, sipmethod, digest, sizeof(digest))) { - if (p->options && p->options->auth_type == PROXY_AUTH) - add_header(&resp, "Proxy-Authorization", digest); - else if (p->options && p->options->auth_type == WWW_AUTH) - add_header(&resp, "Authorization", digest); - else /* Default, to be backwards compatible (maybe being too careful, but leaving it for now) */ - add_header(&resp, "Proxy-Authorization", digest); - } else - ast_log(LOG_WARNING, "No authentication available for call %s\n", p->callid); - } - /* If we are hanging up and know a cause for that, send it in clear text to make - debugging easier. */ - if (sipmethod == SIP_BYE && p->owner && p->owner->hangupcause) { - char buf[10]; - - add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause)); - snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause); - add_header(&resp, "X-Asterisk-HangupCauseCode", buf); - } - - add_header_contentLength(&resp, 0); - return send_request(p, &resp, reliable, seqno ? seqno : p->ocseq); -} - -/*! \brief Remove registration data from realtime database or AST/DB when registration expires */ -static void destroy_association(struct sip_peer *peer) -{ - if (!ast_test_flag(&global_flags[1], SIP_PAGE2_IGNOREREGEXPIRE)) { - if (ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT)) - ast_update_realtime("sippeers", "name", peer->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", "username", "", "regserver", "", NULL); - else - ast_db_del("SIP/Registry", peer->name); - } -} - -/*! \brief Expire registration of SIP peer */ -static int expire_register(const void *data) -{ - struct sip_peer *peer = (struct sip_peer *)data; - - if (!peer) /* Hmmm. We have no peer. Weird. */ - return 0; - - memset(&peer->addr, 0, sizeof(peer->addr)); - - destroy_association(peer); /* remove registration data from storage */ - - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name); - register_peer_exten(peer, FALSE); /* Remove regexten */ - peer->expire = -1; - ast_device_state_changed("SIP/%s", peer->name); - - /* Do we need to release this peer from memory? - Only for realtime peers and autocreated peers - */ - if (ast_test_flag(&peer->flags[1], SIP_PAGE2_SELFDESTRUCT) || - ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) { - struct sip_peer *peer_ptr = peer_ptr; - peer_ptr = ASTOBJ_CONTAINER_UNLINK(&peerl, peer); - if (peer_ptr) { - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - } - - ASTOBJ_UNREF(peer, sip_destroy_peer); - - return 0; -} - -/*! \brief Poke peer (send qualify to check if peer is alive and well) */ -static int sip_poke_peer_s(const void *data) -{ - struct sip_peer *peer = (struct sip_peer *) data; - - peer->pokeexpire = -1; - - sip_poke_peer(peer); - - ASTOBJ_UNREF(peer, sip_destroy_peer); - - return 0; -} - -/*! \brief Get registration details from Asterisk DB */ -static void reg_source_db(struct sip_peer *peer) -{ - char data[256]; - struct in_addr in; - int expiry; - int port; - char *scan, *addr, *port_str, *expiry_str, *username, *contact; - - if (ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT)) - return; - if (ast_db_get("SIP/Registry", peer->name, data, sizeof(data))) - return; - - scan = data; - addr = strsep(&scan, ":"); - port_str = strsep(&scan, ":"); - expiry_str = strsep(&scan, ":"); - username = strsep(&scan, ":"); - contact = scan; /* Contact include sip: and has to be the last part of the database entry as long as we use : as a separator */ - - if (!inet_aton(addr, &in)) - return; - - if (port_str) - port = atoi(port_str); - else - return; - - if (expiry_str) - expiry = atoi(expiry_str); - else - return; - - if (username) - ast_copy_string(peer->username, username, sizeof(peer->username)); - if (contact) - ast_copy_string(peer->fullcontact, contact, sizeof(peer->fullcontact)); - - if (option_debug > 1) - ast_log(LOG_DEBUG, "SIP Seeding peer from astdb: '%s' at %s@%s:%d for %d\n", - peer->name, peer->username, ast_inet_ntoa(in), port, expiry); - - memset(&peer->addr, 0, sizeof(peer->addr)); - peer->addr.sin_family = AF_INET; - peer->addr.sin_addr = in; - peer->addr.sin_port = htons(port); - if (sipsock < 0) { - /* SIP isn't up yet, so schedule a poke only, pretty soon */ - if (!AST_SCHED_DEL(sched, peer->pokeexpire)) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - peer->pokeexpire = ast_sched_add(sched, ast_random() % 5000 + 1, sip_poke_peer_s, ASTOBJ_REF(peer)); - if (peer->pokeexpire == -1) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - } else - sip_poke_peer(peer); - if (!AST_SCHED_DEL(sched, peer->expire)) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - peer->expire = ast_sched_add(sched, (expiry + 10) * 1000, expire_register, ASTOBJ_REF(peer)); - if (peer->expire == -1) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - register_peer_exten(peer, TRUE); -} - -/*! \brief Save contact header for 200 OK on INVITE */ -static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req) -{ - char contact[SIPBUFSIZE]; - char *c; - - /* Look for brackets */ - ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact)); - c = get_in_brackets(contact); - - /* Save full contact to call pvt for later bye or re-invite */ - ast_string_field_set(pvt, fullcontact, c); - - /* Save URI for later ACKs, BYE or RE-invites */ - ast_string_field_set(pvt, okcontacturi, c); - - /* We should return false for URI:s we can't handle, - like sips:, tel:, mailto:,ldap: etc */ - return TRUE; -} - -static int __set_address_from_contact(const char *fullcontact, struct sockaddr_in *sin) -{ - struct hostent *hp; - struct ast_hostent ahp; - int port; - char *c, *host, *pt; - char contact_buf[256]; - char *contact; - - /* Work on a copy */ - ast_copy_string(contact_buf, fullcontact, sizeof(contact_buf)); - contact = contact_buf; - - /* Make sure it's a SIP URL */ - if (strncasecmp(contact, "sip:", 4)) { - ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", contact); - } else - contact += 4; - - /* Ditch arguments */ - /* XXX this code is replicated also shortly below */ - - /* Grab host */ - host = strchr(contact, '@'); - if (!host) { /* No username part */ - host = contact; - c = NULL; - } else { - *host++ = '\0'; - } - pt = strchr(host, ':'); - if (pt) { - *pt++ = '\0'; - port = atoi(pt); - } else - port = STANDARD_SIP_PORT; - - contact = strsep(&contact, ";"); /* trim ; and beyond in username part */ - host = strsep(&host, ";"); /* trim ; and beyond in host/domain part */ - - /* XXX This could block for a long time XXX */ - /* We should only do this if it's a name, not an IP */ - hp = ast_gethostbyname(host, &ahp); - if (!hp) { - ast_log(LOG_WARNING, "Invalid host name in Contact: (can't resolve in DNS) : '%s'\n", host); - return -1; - } - sin->sin_family = AF_INET; - memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr)); - sin->sin_port = htons(port); - - return 0; -} - -/*! \brief Change the other partys IP address based on given contact */ -static int set_address_from_contact(struct sip_pvt *pvt) -{ - if (ast_test_flag(&pvt->flags[0], SIP_NAT_ROUTE)) { - /* NAT: Don't trust the contact field. Just use what they came to us - with. */ - pvt->sa = pvt->recv; - return 0; - } - - return __set_address_from_contact(pvt->fullcontact, &pvt->sa); -} - - -/*! \brief Parse contact header and save registration (peer registration) */ -static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *peer, struct sip_request *req) -{ - char contact[SIPBUFSIZE]; - char data[SIPBUFSIZE]; - const char *expires = get_header(req, "Expires"); - int expiry = atoi(expires); - char *curi, *n, *pt; - int port; - const char *useragent; - struct hostent *hp; - struct ast_hostent ahp; - struct sockaddr_in oldsin, testsin; - - ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact)); - - if (ast_strlen_zero(expires)) { /* No expires header */ - expires = strcasestr(contact, ";expires="); - if (expires) { - /* XXX bug here, we overwrite the string */ - expires = strsep((char **) &expires, ";"); /* trim ; and beyond */ - if (sscanf(expires + 9, "%d", &expiry) != 1) - expiry = default_expiry; - } else { - /* Nothing has been specified */ - expiry = default_expiry; - } - } - - /* Look for brackets */ - curi = contact; - if (strchr(contact, '<') == NULL) /* No <, check for ; and strip it */ - strsep(&curi, ";"); /* This is Header options, not URI options */ - curi = get_in_brackets(contact); - - /* if they did not specify Contact: or Expires:, they are querying - what we currently have stored as their contact address, so return - it - */ - if (ast_strlen_zero(curi) && ast_strlen_zero(expires)) { - /* If we have an active registration, tell them when the registration is going to expire */ - if (peer->expire > -1 && !ast_strlen_zero(peer->fullcontact)) - pvt->expiry = ast_sched_when(sched, peer->expire); - return PARSE_REGISTER_QUERY; - } else if (!strcasecmp(curi, "*") || !expiry) { /* Unregister this peer */ - /* This means remove all registrations and return OK */ - memset(&peer->addr, 0, sizeof(peer->addr)); - if (!AST_SCHED_DEL(sched, peer->expire)) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - - destroy_association(peer); - - register_peer_exten(peer, 0); /* Add extension from regexten= setting in sip.conf */ - peer->fullcontact[0] = '\0'; - peer->useragent[0] = '\0'; - peer->sipoptions = 0; - peer->lastms = 0; - pvt->expiry = 0; - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Unregistered SIP '%s'\n", peer->name); - - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\n", peer->name); - return PARSE_REGISTER_UPDATE; - } - - /* Store whatever we got as a contact from the client */ - ast_copy_string(peer->fullcontact, curi, sizeof(peer->fullcontact)); - - /* For the 200 OK, we should use the received contact */ - ast_string_field_build(pvt, our_contact, "<%s>", curi); - - /* Make sure it's a SIP URL */ - if (strncasecmp(curi, "sip:", 4)) { - ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", curi); - } else - curi += 4; - /* Ditch q */ - curi = strsep(&curi, ";"); - /* Grab host */ - n = strchr(curi, '@'); - if (!n) { - n = curi; - curi = NULL; - } else - *n++ = '\0'; - pt = strchr(n, ':'); - if (pt) { - *pt++ = '\0'; - port = atoi(pt); - } else - port = STANDARD_SIP_PORT; - oldsin = peer->addr; - - /* Check that they're allowed to register at this IP */ - /* XXX This could block for a long time XXX */ - hp = ast_gethostbyname(n, &ahp); - if (!hp) { - ast_log(LOG_WARNING, "Invalid host '%s'\n", n); - *peer->fullcontact = '\0'; - ast_string_field_set(pvt, our_contact, ""); - return PARSE_REGISTER_FAILED; - } - memcpy(&testsin.sin_addr, hp->h_addr, sizeof(testsin.sin_addr)); - if ( ast_apply_ha(global_contact_ha, &testsin) != AST_SENSE_ALLOW || - ast_apply_ha(peer->contactha, &testsin) != AST_SENSE_ALLOW) { - ast_log(LOG_WARNING, "Host '%s' disallowed by rule\n", n); - *peer->fullcontact = '\0'; - ast_string_field_set(pvt, our_contact, ""); - return PARSE_REGISTER_FAILED; - } - - if (!ast_test_flag(&peer->flags[0], SIP_NAT_ROUTE)) { - peer->addr.sin_family = AF_INET; - memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr)); - peer->addr.sin_port = htons(port); - } else { - /* Don't trust the contact field. Just use what they came to us - with */ - peer->addr = pvt->recv; - } - - /* Save SIP options profile */ - peer->sipoptions = pvt->sipoptions; - - if (curi && ast_strlen_zero(peer->username)) - ast_copy_string(peer->username, curi, sizeof(peer->username)); - - if (!AST_SCHED_DEL(sched, peer->expire)) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - if (expiry > max_expiry) - expiry = max_expiry; - if (expiry < min_expiry) - expiry = min_expiry; - if (ast_test_flag(&peer->flags[0], SIP_REALTIME) && !ast_test_flag(&peer->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) { - peer->expire = -1; - } else { - peer->expire = ast_sched_add(sched, (expiry + 10) * 1000, expire_register, ASTOBJ_REF(peer)); - if (peer->expire == -1) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - } - pvt->expiry = expiry; - snprintf(data, sizeof(data), "%s:%d:%d:%s:%s", ast_inet_ntoa(peer->addr.sin_addr), ntohs(peer->addr.sin_port), expiry, peer->username, peer->fullcontact); - if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT)) - ast_db_put("SIP/Registry", peer->name, data); - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Registered\r\n", peer->name); - - /* Is this a new IP address for us? */ - if (option_verbose > 2 && inaddrcmp(&peer->addr, &oldsin)) { - ast_verbose(VERBOSE_PREFIX_3 "Registered SIP '%s' at %s port %d\n", peer->name, ast_inet_ntoa(peer->addr.sin_addr), ntohs(peer->addr.sin_port)); - } - sip_poke_peer(peer); - register_peer_exten(peer, 1); - - /* Save User agent */ - useragent = get_header(req, "User-Agent"); - if (strcasecmp(useragent, peer->useragent)) { /* XXX copy if they are different ? */ - ast_copy_string(peer->useragent, useragent, sizeof(peer->useragent)); - if (option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_3 "Saved useragent \"%s\" for peer %s\n", peer->useragent, peer->name); - } - return PARSE_REGISTER_UPDATE; -} - -/*! \brief Remove route from route list */ -static void free_old_route(struct sip_route *route) -{ - struct sip_route *next; - - while (route) { - next = route->next; - free(route); - route = next; - } -} - -/*! \brief List all routes - mostly for debugging */ -static void list_route(struct sip_route *route) -{ - if (!route) - ast_verbose("list_route: no route\n"); - else { - for (;route; route = route->next) - ast_verbose("list_route: hop: <%s>\n", route->hop); - } -} - -/*! \brief Build route list from Record-Route header */ -static void build_route(struct sip_pvt *p, struct sip_request *req, int backwards) -{ - struct sip_route *thishop, *head, *tail; - int start = 0; - int len; - const char *rr, *contact, *c; - - /* Once a persistant route is set, don't fool with it */ - if (p->route && p->route_persistant) { - if (option_debug) - ast_log(LOG_DEBUG, "build_route: Retaining previous route: <%s>\n", p->route->hop); - return; - } - - if (p->route) { - free_old_route(p->route); - p->route = NULL; - } - - /* We only want to create the route set the first time this is called */ - p->route_persistant = 1; - - /* Build a tailq, then assign it to p->route when done. - * If backwards, we add entries from the head so they end up - * in reverse order. However, we do need to maintain a correct - * tail pointer because the contact is always at the end. - */ - head = NULL; - tail = head; - /* 1st we pass through all the hops in any Record-Route headers */ - for (;;) { - /* Each Record-Route header */ - rr = __get_header(req, "Record-Route", &start); - if (*rr == '\0') - break; - for (; (rr = strchr(rr, '<')) ; rr += len) { /* Each route entry */ - ++rr; - len = strcspn(rr, ">") + 1; - /* Make a struct route */ - if ((thishop = ast_malloc(sizeof(*thishop) + len))) { - /* ast_calloc is not needed because all fields are initialized in this block */ - ast_copy_string(thishop->hop, rr, len); - if (option_debug > 1) - ast_log(LOG_DEBUG, "build_route: Record-Route hop: <%s>\n", thishop->hop); - /* Link in */ - if (backwards) { - /* Link in at head so they end up in reverse order */ - thishop->next = head; - head = thishop; - /* If this was the first then it'll be the tail */ - if (!tail) - tail = thishop; - } else { - thishop->next = NULL; - /* Link in at the end */ - if (tail) - tail->next = thishop; - else - head = thishop; - tail = thishop; - } - } - } - } - - /* Only append the contact if we are dealing with a strict router */ - if (!head || (!ast_strlen_zero(head->hop) && strstr(head->hop,";lr") == NULL) ) { - /* 2nd append the Contact: if there is one */ - /* Can be multiple Contact headers, comma separated values - we just take the first */ - contact = get_header(req, "Contact"); - if (!ast_strlen_zero(contact)) { - if (option_debug > 1) - ast_log(LOG_DEBUG, "build_route: Contact hop: %s\n", contact); - /* Look for <: delimited address */ - c = strchr(contact, '<'); - if (c) { - /* Take to > */ - ++c; - len = strcspn(c, ">") + 1; - } else { - /* No <> - just take the lot */ - c = contact; - len = strlen(contact) + 1; - } - if ((thishop = ast_malloc(sizeof(*thishop) + len))) { - /* ast_calloc is not needed because all fields are initialized in this block */ - ast_copy_string(thishop->hop, c, len); - thishop->next = NULL; - /* Goes at the end */ - if (tail) - tail->next = thishop; - else - head = thishop; - } - } - } - - /* Store as new route */ - p->route = head; - - /* For debugging dump what we ended up with */ - if (sip_debug_test_pvt(p)) - list_route(p->route); -} - -AST_THREADSTORAGE(check_auth_buf, check_auth_buf_init); -#define CHECK_AUTH_BUF_INITLEN 256 - -/*! \brief Check user authorization from peer definition - Some actions, like REGISTER and INVITEs from peers require - authentication (if peer have secret set) - \return 0 on success, non-zero on error -*/ -static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username, - const char *secret, const char *md5secret, int sipmethod, - char *uri, enum xmittype reliable, int ignore) -{ - const char *response = "407 Proxy Authentication Required"; - const char *reqheader = "Proxy-Authorization"; - const char *respheader = "Proxy-Authenticate"; - const char *authtoken; - char a1_hash[256]; - char resp_hash[256]=""; - char *c; - int wrongnonce = FALSE; - int good_response; - const char *usednonce = p->randdata; - struct ast_dynamic_str *buf; - int res; - - /* table of recognised keywords, and their value in the digest */ - enum keys { K_RESP, K_URI, K_USER, K_NONCE, K_LAST }; - struct x { - const char *key; - const char *s; - } *i, keys[] = { - [K_RESP] = { "response=", "" }, - [K_URI] = { "uri=", "" }, - [K_USER] = { "username=", "" }, - [K_NONCE] = { "nonce=", "" }, - [K_LAST] = { NULL, NULL} - }; - - /* Always OK if no secret */ - if (ast_strlen_zero(secret) && ast_strlen_zero(md5secret)) - return AUTH_SUCCESSFUL; - if (sipmethod == SIP_REGISTER || sipmethod == SIP_SUBSCRIBE) { - /* On a REGISTER, we have to use 401 and its family of headers instead of 407 and its family - of headers -- GO SIP! Whoo hoo! Two things that do the same thing but are used in - different circumstances! What a surprise. */ - response = "401 Unauthorized"; - reqheader = "Authorization"; - respheader = "WWW-Authenticate"; - } - authtoken = get_header(req, reqheader); - if (ignore && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) { - /* This is a retransmitted invite/register/etc, don't reconstruct authentication - information */ - if (!reliable) { - /* Resend message if this was NOT a reliable delivery. Otherwise the - retransmission should get it */ - transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); - /* Schedule auto destroy in 32 seconds (according to RFC 3261) */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - return AUTH_CHALLENGE_SENT; - } else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) { - /* We have no auth, so issue challenge and request authentication */ - ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */ - transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); - /* Schedule auto destroy in 32 seconds */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return AUTH_CHALLENGE_SENT; - } - - /* --- We have auth, so check it */ - - /* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting - an example in the spec of just what it is you're doing a hash on. */ - - if (!(buf = ast_dynamic_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN))) - return AUTH_SECRET_FAILED; /*! XXX \todo need a better return code here */ - - /* Make a copy of the response and parse it */ - res = ast_dynamic_str_thread_set(&buf, 0, &check_auth_buf, "%s", authtoken); - - if (res == AST_DYNSTR_BUILD_FAILED) - return AUTH_SECRET_FAILED; /*! XXX \todo need a better return code here */ - - c = buf->str; - - while(c && *(c = ast_skip_blanks(c)) ) { /* lookup for keys */ - for (i = keys; i->key != NULL; i++) { - const char *separator = ","; /* default */ - - if (strncasecmp(c, i->key, strlen(i->key)) != 0) - continue; - /* Found. Skip keyword, take text in quotes or up to the separator. */ - c += strlen(i->key); - if (*c == '"') { /* in quotes. Skip first and look for last */ - c++; - separator = "\""; - } - i->s = c; - strsep(&c, separator); - break; - } - if (i->key == NULL) /* not found, jump after space or comma */ - strsep(&c, " ,"); - } - - /* Verify that digest username matches the username we auth as */ - if (strcmp(username, keys[K_USER].s)) { - ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n", - username, keys[K_USER].s); - /* Oops, we're trying something here */ - return AUTH_USERNAME_MISMATCH; - } - - /* Verify nonce from request matches our nonce. If not, send 401 with new nonce */ - if (strcasecmp(p->randdata, keys[K_NONCE].s)) { /* XXX it was 'n'casecmp ? */ - wrongnonce = TRUE; - usednonce = keys[K_NONCE].s; - } - - if (!ast_strlen_zero(md5secret)) - ast_copy_string(a1_hash, md5secret, sizeof(a1_hash)); - else { - char a1[256]; - snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, secret); - ast_md5_hash(a1_hash, a1); - } - - /* compute the expected response to compare with what we received */ - { - char a2[256]; - char a2_hash[256]; - char resp[256]; - - snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text, - S_OR(keys[K_URI].s, uri)); - ast_md5_hash(a2_hash, a2); - snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash); - ast_md5_hash(resp_hash, resp); - } - - good_response = keys[K_RESP].s && - !strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash)); - if (wrongnonce) { - if (good_response) { - if (sipdebug) - ast_log(LOG_NOTICE, "Correct auth, but based on stale nonce received from '%s'\n", get_header(req, "To")); - /* We got working auth token, based on stale nonce . */ - ast_string_field_build(p, randdata, "%08lx", ast_random()); - transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, TRUE); - } else { - /* Everything was wrong, so give the device one more try with a new challenge */ - if (!ast_test_flag(req, SIP_PKT_IGNORE)) { - if (sipdebug) - ast_log(LOG_NOTICE, "Bad authentication received from '%s'\n", get_header(req, "To")); - ast_string_field_build(p, randdata, "%08lx", ast_random()); - } else { - if (sipdebug) - ast_log(LOG_NOTICE, "Duplicate authentication received from '%s'\n", get_header(req, "To")); - } - transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, FALSE); - } - - /* Schedule auto destroy in 32 seconds */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return AUTH_CHALLENGE_SENT; - } - if (good_response) { - append_history(p, "AuthOK", "Auth challenge succesful for %s", username); - return AUTH_SUCCESSFUL; - } - - /* Ok, we have a bad username/secret pair */ - /* Tell the UAS not to re-send this authentication data, because - it will continue to fail - */ - - return AUTH_SECRET_FAILED; -} - -/*! \brief Change onhold state of a peer using a pvt structure */ -static void sip_peer_hold(struct sip_pvt *p, int hold) -{ - struct sip_peer *peer = find_peer(p->peername, NULL, 1, 0); - - if (!peer) - return; - - /* If they put someone on hold, increment the value... otherwise decrement it */ - if (hold) - peer->onHold++; - else - peer->onHold--; - - /* Request device state update */ - ast_device_state_changed("SIP/%s", peer->name); - - return; -} - -/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem -\note If you add an "hint" priority to the extension in the dial plan, - you will get notifications on device state changes */ -static int cb_extensionstate(char *context, char* exten, int state, void *data) -{ - struct sip_pvt *p = data; - - ast_mutex_lock(&p->lock); - - switch(state) { - case AST_EXTENSION_DEACTIVATED: /* Retry after a while */ - case AST_EXTENSION_REMOVED: /* Extension is gone */ - if (p->autokillid > -1 && sip_cancel_destroy(p)) /* Remove subscription expiry for renewals */ - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); /* Delete subscription in 32 secs */ - ast_verbose(VERBOSE_PREFIX_2 "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username); - p->stateid = -1; - p->subscribed = NONE; - append_history(p, "Subscribestatus", "%s", state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated"); - break; - default: /* Tell user */ - p->laststate = state; - break; - } - if (p->subscribed != NONE) { /* Only send state NOTIFY if we know the format */ - if (!p->pendinginvite) { - transmit_state_notify(p, state, 1, FALSE); - } else { - /* We already have a NOTIFY sent that is not answered. Queue the state up. - if many state changes happen meanwhile, we will only send a notification of the last one */ - ast_set_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE); - } - } - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_1 "Extension Changed %s[%s] new state %s for Notify User %s %s\n", exten, context, ast_extension_state2str(state), p->username, - ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE) ? "(queued)" : ""); - - - ast_mutex_unlock(&p->lock); - - return 0; -} - -/*! \brief Send a fake 401 Unauthorized response when the administrator - wants to hide the names of local users/peers from fishers - */ -static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, int reliable) -{ - ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */ - transmit_response_with_auth(p, "401 Unauthorized", req, p->randdata, reliable, "WWW-Authenticate", 0); -} - -/*! \brief Verify registration of user - - Registration is done in several steps, first a REGISTER without auth - to get a challenge (nonce) then a second one with auth - - Registration requests are only matched with peers that are marked as "dynamic" - */ -static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr_in *sin, - struct sip_request *req, char *uri) -{ - enum check_auth_result res = AUTH_NOT_FOUND; - struct sip_peer *peer; - char tmp[256]; - char *name, *c; - char *t; - char *domain; - - /* Terminate URI */ - t = uri; - while(*t && (*t > 32) && (*t != ';')) - t++; - *t = '\0'; - - ast_copy_string(tmp, get_header(req, "To"), sizeof(tmp)); - if (pedanticsipchecking) - ast_uri_decode(tmp); - - c = get_in_brackets(tmp); - c = strsep(&c, ";"); /* Ditch ;user=phone */ - - if (!strncasecmp(c, "sip:", 4)) { - name = c + 4; - } else { - name = c; - ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_inet_ntoa(sin->sin_addr)); - } - - /* Strip off the domain name */ - if ((c = strchr(name, '@'))) { - *c++ = '\0'; - domain = c; - if ((c = strchr(domain, ':'))) /* Remove :port */ - *c = '\0'; - if (!AST_LIST_EMPTY(&domain_list)) { - if (!check_sip_domain(domain, NULL, 0)) { - transmit_response(p, "404 Not found (unknown domain)", &p->initreq); - return AUTH_UNKNOWN_DOMAIN; - } - } - } - - ast_string_field_set(p, exten, name); - build_contact(p); - peer = find_peer(name, NULL, 1, 0); - if (!(peer && ast_apply_ha(peer->ha, sin))) { - /* Peer fails ACL check */ - if (peer) { - ASTOBJ_UNREF(peer, sip_destroy_peer); - res = AUTH_ACL_FAILED; - } else - res = AUTH_NOT_FOUND; - } - if (peer) { - /* Set Frame packetization */ - if (p->rtp) { - ast_rtp_codec_setpref(p->rtp, &peer->prefs); - p->autoframing = peer->autoframing; - } - if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC)) { - ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name); - res = AUTH_PEER_NOT_DYNAMIC; - } else { - ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT); - transmit_response(p, "100 Trying", req); - if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri, XMIT_UNRELIABLE, ast_test_flag(req, SIP_PKT_IGNORE)))) { - if (sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - - /* We have a succesful registration attemp with proper authentication, - now, update the peer */ - switch (parse_register_contact(p, peer, req)) { - case PARSE_REGISTER_FAILED: - ast_log(LOG_WARNING, "Failed to parse contact info\n"); - transmit_response_with_date(p, "400 Bad Request", req); - peer->lastmsgssent = -1; - res = 0; - break; - case PARSE_REGISTER_QUERY: - transmit_response_with_date(p, "200 OK", req); - peer->lastmsgssent = -1; - res = 0; - break; - case PARSE_REGISTER_UPDATE: - update_peer(peer, p->expiry); - /* Say OK and ask subsystem to retransmit msg counter */ - transmit_response_with_date(p, "200 OK", req); - if (!ast_test_flag((&peer->flags[1]), SIP_PAGE2_SUBSCRIBEMWIONLY)) - peer->lastmsgssent = -1; - res = 0; - break; - } - } - } - } - if (!peer && autocreatepeer) { - /* Create peer if we have autocreate mode enabled */ - peer = temp_peer(name); - if (peer) { - ASTOBJ_CONTAINER_LINK(&peerl, peer); - if (sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - switch (parse_register_contact(p, peer, req)) { - case PARSE_REGISTER_FAILED: - ast_log(LOG_WARNING, "Failed to parse contact info\n"); - transmit_response_with_date(p, "400 Bad Request", req); - peer->lastmsgssent = -1; - res = 0; - break; - case PARSE_REGISTER_QUERY: - transmit_response_with_date(p, "200 OK", req); - peer->lastmsgssent = -1; - res = 0; - break; - case PARSE_REGISTER_UPDATE: - /* Say OK and ask subsystem to retransmit msg counter */ - transmit_response_with_date(p, "200 OK", req); - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Registered\r\n", peer->name); - peer->lastmsgssent = -1; - res = 0; - break; - } - } - } - if (!res) { - ast_device_state_changed("SIP/%s", peer->name); - } - if (res < 0) { - switch (res) { - case AUTH_SECRET_FAILED: - /* Wrong password in authentication. Go away, don't try again until you fixed it */ - transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq); - break; - case AUTH_USERNAME_MISMATCH: - /* Username and digest username does not match. - Asterisk uses the From: username for authentication. We need the - users to use the same authentication user name until we support - proper authentication by digest auth name */ - transmit_response(p, "403 Authentication user name does not match account name", &p->initreq); - break; - case AUTH_NOT_FOUND: - case AUTH_PEER_NOT_DYNAMIC: - case AUTH_ACL_FAILED: - if (global_alwaysauthreject) { - transmit_fake_auth_response(p, &p->initreq, 1); - } else { - /* URI not found */ - if (res == AUTH_PEER_NOT_DYNAMIC) - transmit_response(p, "403 Forbidden", &p->initreq); - else - transmit_response(p, "404 Not found", &p->initreq); - } - break; - default: - break; - } - } - if (peer) - ASTOBJ_UNREF(peer, sip_destroy_peer); - - return res; -} - -/*! \brief Get referring dnis */ -static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq) -{ - char tmp[256], *c, *a; - struct sip_request *req; - - req = oreq; - if (!req) - req = &p->initreq; - ast_copy_string(tmp, get_header(req, "Diversion"), sizeof(tmp)); - if (ast_strlen_zero(tmp)) - return 0; - c = get_in_brackets(tmp); - if (strncasecmp(c, "sip:", 4)) { - ast_log(LOG_WARNING, "Huh? Not an RDNIS SIP header (%s)?\n", c); - return -1; - } - c += 4; - a = c; - strsep(&a, "@;"); /* trim anything after @ or ; */ - if (sip_debug_test_pvt(p)) - ast_verbose("RDNIS is %s\n", c); - ast_string_field_set(p, rdnis, c); - - return 0; -} - -/*! \brief Find out who the call is for - We use the INVITE uri to find out -*/ -static int get_destination(struct sip_pvt *p, struct sip_request *oreq) -{ - char tmp[256] = "", *uri, *a; - char tmpf[256] = "", *from; - struct sip_request *req; - char *colon; - char *decoded_uri; - - req = oreq; - if (!req) - req = &p->initreq; - - /* Find the request URI */ - if (req->rlPart2) - ast_copy_string(tmp, req->rlPart2, sizeof(tmp)); - - if (pedanticsipchecking) - ast_uri_decode(tmp); - - uri = get_in_brackets(tmp); - - if (strncasecmp(uri, "sip:", 4)) { - ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", uri); - return -1; - } - uri += 4; - - /* Now find the From: caller ID and name */ - ast_copy_string(tmpf, get_header(req, "From"), sizeof(tmpf)); - if (!ast_strlen_zero(tmpf)) { - if (pedanticsipchecking) - ast_uri_decode(tmpf); - from = get_in_brackets(tmpf); - } else { - from = NULL; - } - - if (!ast_strlen_zero(from)) { - if (strncasecmp(from, "sip:", 4)) { - ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", from); - return -1; - } - from += 4; - if ((a = strchr(from, '@'))) - *a++ = '\0'; - else - a = from; /* just a domain */ - from = strsep(&from, ";"); /* Remove userinfo options */ - a = strsep(&a, ";"); /* Remove URI options */ - ast_string_field_set(p, fromdomain, a); - } - - /* Skip any options and find the domain */ - - /* Get the target domain */ - if ((a = strchr(uri, '@'))) { - *a++ = '\0'; - } else { /* No username part */ - a = uri; - uri = "s"; /* Set extension to "s" */ - } - colon = strchr(a, ':'); /* Remove :port */ - if (colon) - *colon = '\0'; - - uri = strsep(&uri, ";"); /* Remove userinfo options */ - a = strsep(&a, ";"); /* Remove URI options */ - - ast_string_field_set(p, domain, a); - - if (!AST_LIST_EMPTY(&domain_list)) { - char domain_context[AST_MAX_EXTENSION]; - - domain_context[0] = '\0'; - if (!check_sip_domain(p->domain, domain_context, sizeof(domain_context))) { - if (!allow_external_domains && (req->method == SIP_INVITE || req->method == SIP_REFER)) { - if (option_debug) - ast_log(LOG_DEBUG, "Got SIP %s to non-local domain '%s'; refusing request.\n", sip_methods[req->method].text, p->domain); - return -2; - } - } - /* If we have a context defined, overwrite the original context */ - if (!ast_strlen_zero(domain_context)) - ast_string_field_set(p, context, domain_context); - } - - /* If the request coming in is a subscription and subscribecontext has been specified use it */ - if (req->method == SIP_SUBSCRIBE && !ast_strlen_zero(p->subscribecontext)) - ast_string_field_set(p, context, p->subscribecontext); - - if (sip_debug_test_pvt(p)) - ast_verbose("Looking for %s in %s (domain %s)\n", uri, p->context, p->domain); - - /* If this is a subscription we actually just need to see if a hint exists for the extension */ - if (req->method == SIP_SUBSCRIBE) { - char hint[AST_MAX_EXTENSION]; - return (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, p->context, p->exten) ? 0 : -1); - } else { - decoded_uri = ast_strdupa(uri); - ast_uri_decode(decoded_uri); - /* Check the dialplan for the username part of the request URI, - the domain will be stored in the SIPDOMAIN variable - Since extensions.conf can have unescaped characters, try matching a decoded - uri in addition to the non-decoded uri - Return 0 if we have a matching extension */ - if (ast_exists_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from)) || ast_exists_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from)) || - !strcmp(decoded_uri, ast_pickup_ext())) { - if (!oreq) - ast_string_field_set(p, exten, decoded_uri); - return 0; - } - } - - /* Return 1 for pickup extension or overlap dialling support (if we support it) */ - if((ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP) && - ast_canmatch_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from))) || - !strncmp(decoded_uri, ast_pickup_ext(), strlen(decoded_uri))) { - return 1; - } - - return -1; -} - -/*! \brief Lock interface lock and find matching pvt lock -*/ -static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag) -{ - struct sip_pvt *sip_pvt_ptr; - - ast_mutex_lock(&iflock); - - if (option_debug > 3 && totag) - ast_log(LOG_DEBUG, "Looking for callid %s (fromtag %s totag %s)\n", callid, fromtag ? fromtag : "<no fromtag>", totag ? totag : "<no totag>"); - - /* Search interfaces and find the match */ - for (sip_pvt_ptr = iflist; sip_pvt_ptr; sip_pvt_ptr = sip_pvt_ptr->next) { - if (!strcmp(sip_pvt_ptr->callid, callid)) { - int match = 1; - - /* Go ahead and lock it (and its owner) before returning */ - ast_mutex_lock(&sip_pvt_ptr->lock); - - /* Check if tags match. If not, this is not the call we want - (With a forking SIP proxy, several call legs share the - call id, but have different tags) - */ - if (pedanticsipchecking) { - const char *pvt_fromtag, *pvt_totag; - - if (ast_test_flag(&sip_pvt_ptr->flags[1], SIP_PAGE2_OUTGOING_CALL)) { - /* Outgoing call tags : from is "our", to is "their" */ - pvt_fromtag = sip_pvt_ptr->tag ; - pvt_totag = sip_pvt_ptr->theirtag ; - } else { - /* Incoming call tags : from is "their", to is "our" */ - pvt_fromtag = sip_pvt_ptr->theirtag ; - pvt_totag = sip_pvt_ptr->tag ; - } - if (ast_strlen_zero(fromtag) || strcmp(fromtag, pvt_fromtag) || (!ast_strlen_zero(totag) && strcmp(totag, pvt_totag))) - match = 0; - } - - if (!match) { - ast_mutex_unlock(&sip_pvt_ptr->lock); - continue; - } - - if (option_debug > 3 && totag) - ast_log(LOG_DEBUG, "Matched %s call - their tag is %s Our tag is %s\n", - ast_test_flag(&sip_pvt_ptr->flags[1], SIP_PAGE2_OUTGOING_CALL) ? "OUTGOING": "INCOMING", - sip_pvt_ptr->theirtag, sip_pvt_ptr->tag); - - /* deadlock avoidance... */ - while (sip_pvt_ptr->owner && ast_channel_trylock(sip_pvt_ptr->owner)) { - DEADLOCK_AVOIDANCE(&sip_pvt_ptr->lock); - } - break; - } - } - ast_mutex_unlock(&iflock); - if (option_debug > 3 && !sip_pvt_ptr) - ast_log(LOG_DEBUG, "Found no match for callid %s to-tag %s from-tag %s\n", callid, totag, fromtag); - return sip_pvt_ptr; -} - -/*! \brief Call transfer support (the REFER method) - * Extracts Refer headers into pvt dialog structure */ -static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req) -{ - - const char *p_referred_by = NULL; - char *h_refer_to = NULL; - char *h_referred_by = NULL; - char *refer_to; - const char *p_refer_to; - char *referred_by_uri = NULL; - char *ptr; - struct sip_request *req = NULL; - const char *transfer_context = NULL; - struct sip_refer *referdata; - - - req = outgoing_req; - referdata = transferer->refer; - - if (!req) - req = &transferer->initreq; - - p_refer_to = get_header(req, "Refer-To"); - if (ast_strlen_zero(p_refer_to)) { - ast_log(LOG_WARNING, "Refer-To Header missing. Skipping transfer.\n"); - return -2; /* Syntax error */ - } - h_refer_to = ast_strdupa(p_refer_to); - refer_to = get_in_brackets(h_refer_to); - if (pedanticsipchecking) - ast_uri_decode(refer_to); - - if (strncasecmp(refer_to, "sip:", 4)) { - ast_log(LOG_WARNING, "Can't transfer to non-sip: URI. (Refer-to: %s)?\n", refer_to); - return -3; - } - refer_to += 4; /* Skip sip: */ - - /* Get referred by header if it exists */ - p_referred_by = get_header(req, "Referred-By"); - if (!ast_strlen_zero(p_referred_by)) { - char *lessthan; - h_referred_by = ast_strdupa(p_referred_by); - if (pedanticsipchecking) - ast_uri_decode(h_referred_by); - - /* Store referrer's caller ID name */ - ast_copy_string(referdata->referred_by_name, h_referred_by, sizeof(referdata->referred_by_name)); - if ((lessthan = strchr(referdata->referred_by_name, '<'))) { - *(lessthan - 1) = '\0'; /* Space */ - } - - referred_by_uri = get_in_brackets(h_referred_by); - if(strncasecmp(referred_by_uri, "sip:", 4)) { - ast_log(LOG_WARNING, "Huh? Not a sip: header (Referred-by: %s). Skipping.\n", referred_by_uri); - referred_by_uri = (char *) NULL; - } else { - referred_by_uri += 4; /* Skip sip: */ - } - } - - /* Check for arguments in the refer_to header */ - if ((ptr = strchr(refer_to, '?'))) { /* Search for arguments */ - *ptr++ = '\0'; - if (!strncasecmp(ptr, "REPLACES=", 9)) { - char *to = NULL, *from = NULL; - - /* This is an attended transfer */ - referdata->attendedtransfer = 1; - ast_copy_string(referdata->replaces_callid, ptr+9, sizeof(referdata->replaces_callid)); - ast_uri_decode(referdata->replaces_callid); - if ((ptr = strchr(referdata->replaces_callid, ';'))) /* Find options */ { - *ptr++ = '\0'; - } - - if (ptr) { - /* Find the different tags before we destroy the string */ - to = strcasestr(ptr, "to-tag="); - from = strcasestr(ptr, "from-tag="); - } - - /* Grab the to header */ - if (to) { - ptr = to + 7; - if ((to = strchr(ptr, '&'))) - *to = '\0'; - if ((to = strchr(ptr, ';'))) - *to = '\0'; - ast_copy_string(referdata->replaces_callid_totag, ptr, sizeof(referdata->replaces_callid_totag)); - } - - if (from) { - ptr = from + 9; - if ((to = strchr(ptr, '&'))) - *to = '\0'; - if ((to = strchr(ptr, ';'))) - *to = '\0'; - ast_copy_string(referdata->replaces_callid_fromtag, ptr, sizeof(referdata->replaces_callid_fromtag)); - } - - if (option_debug > 1) { - if (!pedanticsipchecking) - ast_log(LOG_DEBUG,"Attended transfer: Will use Replace-Call-ID : %s (No check of from/to tags)\n", referdata->replaces_callid ); - else - ast_log(LOG_DEBUG,"Attended transfer: Will use Replace-Call-ID : %s F-tag: %s T-tag: %s\n", referdata->replaces_callid, referdata->replaces_callid_fromtag ? referdata->replaces_callid_fromtag : "<none>", referdata->replaces_callid_totag ? referdata->replaces_callid_totag : "<none>" ); - } - } - } - - if ((ptr = strchr(refer_to, '@'))) { /* Separate domain */ - char *urioption = NULL, *domain; - *ptr++ = '\0'; - - if ((urioption = strchr(ptr, ';'))) /* Separate urioptions */ - *urioption++ = '\0'; - - domain = ptr; - if ((ptr = strchr(domain, ':'))) /* Remove :port */ - *ptr = '\0'; - - /* Save the domain for the dial plan */ - ast_copy_string(referdata->refer_to_domain, domain, sizeof(referdata->refer_to_domain)); - if (urioption) - ast_copy_string(referdata->refer_to_urioption, urioption, sizeof(referdata->refer_to_urioption)); - } - - if ((ptr = strchr(refer_to, ';'))) /* Remove options */ - *ptr = '\0'; - ast_copy_string(referdata->refer_to, refer_to, sizeof(referdata->refer_to)); - - if (referred_by_uri) { - if ((ptr = strchr(referred_by_uri, ';'))) /* Remove options */ - *ptr = '\0'; - ast_copy_string(referdata->referred_by, referred_by_uri, sizeof(referdata->referred_by)); - } else { - referdata->referred_by[0] = '\0'; - } - - /* Determine transfer context */ - if (transferer->owner) /* Mimic behaviour in res_features.c */ - transfer_context = pbx_builtin_getvar_helper(transferer->owner, "TRANSFER_CONTEXT"); - - /* By default, use the context in the channel sending the REFER */ - if (ast_strlen_zero(transfer_context)) { - transfer_context = S_OR(transferer->owner->macrocontext, - S_OR(transferer->context, default_context)); - } - - ast_copy_string(referdata->refer_to_context, transfer_context, sizeof(referdata->refer_to_context)); - - /* Either an existing extension or the parking extension */ - if (ast_exists_extension(NULL, transfer_context, refer_to, 1, NULL) ) { - if (sip_debug_test_pvt(transferer)) { - ast_verbose("SIP transfer to extension %s@%s by %s\n", refer_to, transfer_context, referred_by_uri); - } - /* We are ready to transfer to the extension */ - return 0; - } - if (sip_debug_test_pvt(transferer)) - ast_verbose("Failed SIP Transfer to non-existing extension %s in context %s\n n", refer_to, transfer_context); - - /* Failure, we can't find this extension */ - return -1; -} - - -/*! \brief Call transfer support (old way, deprecated by the IETF)--*/ -static int get_also_info(struct sip_pvt *p, struct sip_request *oreq) -{ - char tmp[256] = "", *c, *a; - struct sip_request *req = oreq ? oreq : &p->initreq; - struct sip_refer *referdata = NULL; - const char *transfer_context = NULL; - - if (!p->refer && !sip_refer_allocate(p)) - return -1; - - referdata = p->refer; - - ast_copy_string(tmp, get_header(req, "Also"), sizeof(tmp)); - c = get_in_brackets(tmp); - - if (pedanticsipchecking) - ast_uri_decode(c); - - if (strncasecmp(c, "sip:", 4)) { - ast_log(LOG_WARNING, "Huh? Not a SIP header in Also: transfer (%s)?\n", c); - return -1; - } - c += 4; - if ((a = strchr(c, ';'))) /* Remove arguments */ - *a = '\0'; - - if ((a = strchr(c, '@'))) { /* Separate Domain */ - *a++ = '\0'; - ast_copy_string(referdata->refer_to_domain, a, sizeof(referdata->refer_to_domain)); - } - - if (sip_debug_test_pvt(p)) - ast_verbose("Looking for %s in %s\n", c, p->context); - - if (p->owner) /* Mimic behaviour in res_features.c */ - transfer_context = pbx_builtin_getvar_helper(p->owner, "TRANSFER_CONTEXT"); - - /* By default, use the context in the channel sending the REFER */ - if (ast_strlen_zero(transfer_context)) { - transfer_context = S_OR(p->owner->macrocontext, - S_OR(p->context, default_context)); - } - if (ast_exists_extension(NULL, transfer_context, c, 1, NULL)) { - /* This is a blind transfer */ - if (option_debug) - ast_log(LOG_DEBUG,"SIP Bye-also transfer to Extension %s@%s \n", c, transfer_context); - ast_copy_string(referdata->refer_to, c, sizeof(referdata->refer_to)); - ast_copy_string(referdata->referred_by, "", sizeof(referdata->referred_by)); - ast_copy_string(referdata->refer_contact, "", sizeof(referdata->refer_contact)); - referdata->refer_call = NULL; - /* Set new context */ - ast_string_field_set(p, context, transfer_context); - return 0; - } else if (ast_canmatch_extension(NULL, p->context, c, 1, NULL)) { - return 1; - } - - return -1; -} -/*! \brief check Via: header for hostname, port and rport request/answer */ -static void check_via(struct sip_pvt *p, const struct sip_request *req) -{ - char via[512]; - char *c, *pt; - struct hostent *hp; - struct ast_hostent ahp; - - ast_copy_string(via, get_header(req, "Via"), sizeof(via)); - - /* Work on the leftmost value of the topmost Via header */ - c = strchr(via, ','); - if (c) - *c = '\0'; - - /* Check for rport */ - c = strstr(via, ";rport"); - if (c && (c[6] != '=')) /* rport query, not answer */ - ast_set_flag(&p->flags[0], SIP_NAT_ROUTE); - - c = strchr(via, ';'); - if (c) - *c = '\0'; - - c = strchr(via, ' '); - if (c) { - *c = '\0'; - c = ast_skip_blanks(c+1); - if (strcasecmp(via, "SIP/2.0/UDP")) { - ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via); - return; - } - pt = strchr(c, ':'); - if (pt) - *pt++ = '\0'; /* remember port pointer */ - hp = ast_gethostbyname(c, &ahp); - if (!hp) { - ast_log(LOG_WARNING, "'%s' is not a valid host\n", c); - return; - } - memset(&p->sa, 0, sizeof(p->sa)); - p->sa.sin_family = AF_INET; - memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr)); - p->sa.sin_port = htons(pt ? atoi(pt) : STANDARD_SIP_PORT); - - if (sip_debug_test_pvt(p)) { - const struct sockaddr_in *dst = sip_real_dst(p); - ast_verbose("Sending to %s : %d (%s)\n", ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), sip_nat_mode(p)); - } - } -} - -/*! \brief Get caller id name from SIP headers */ -static char *get_calleridname(const char *input, char *output, size_t outputsize) -{ - const char *end = strchr(input,'<'); /* first_bracket */ - const char *tmp = strchr(input,'"'); /* first quote */ - int bytes = 0; - int maxbytes = outputsize - 1; - - if (!end || end == input) /* we require a part in brackets */ - return NULL; - - end--; /* move just before "<" */ - - if (tmp && tmp <= end) { - /* The quote (tmp) precedes the bracket (end+1). - * Find the matching quote and return the content. - */ - end = strchr(tmp+1, '"'); - if (!end) - return NULL; - bytes = (int) (end - tmp); - /* protect the output buffer */ - if (bytes > maxbytes) - bytes = maxbytes; - ast_copy_string(output, tmp + 1, bytes); - } else { - /* No quoted string, or it is inside brackets. */ - /* clear the empty characters in the begining*/ - input = ast_skip_blanks(input); - /* clear the empty characters in the end */ - while(*end && *end < 33 && end > input) - end--; - if (end >= input) { - bytes = (int) (end - input) + 2; - /* protect the output buffer */ - if (bytes > maxbytes) - bytes = maxbytes; - ast_copy_string(output, input, bytes); - } else - return NULL; - } - return output; -} - -/*! \brief Get caller id number from Remote-Party-ID header field - * Returns true if number should be restricted (privacy setting found) - * output is set to NULL if no number found - */ -static int get_rpid_num(const char *input, char *output, int maxlen) -{ - char *start; - char *end; - - start = strchr(input,':'); - if (!start) { - output[0] = '\0'; - return 0; - } - start++; - - /* we found "number" */ - ast_copy_string(output,start,maxlen); - output[maxlen-1] = '\0'; - - end = strchr(output,'@'); - if (end) - *end = '\0'; - else - output[0] = '\0'; - if (strstr(input,"privacy=full") || strstr(input,"privacy=uri")) - return AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; - - return 0; -} - - -/*! \brief Check if matching user or peer is defined - Match user on From: user name and peer on IP/port - This is used on first invite (not re-invites) and subscribe requests - \return 0 on success, non-zero on failure -*/ -static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req, - int sipmethod, char *uri, enum xmittype reliable, - struct sockaddr_in *sin, struct sip_peer **authpeer) -{ - struct sip_user *user = NULL; - struct sip_peer *peer; - char from[256], *c; - char *of; - char rpid_num[50]; - const char *rpid; - enum check_auth_result res = AUTH_SUCCESSFUL; - char *t; - char calleridname[50]; - int debug=sip_debug_test_addr(sin); - struct ast_variable *tmpvar = NULL, *v = NULL; - char *uri2 = ast_strdupa(uri); - - /* Terminate URI */ - t = uri2; - while (*t && *t > 32 && *t != ';') - t++; - *t = '\0'; - ast_copy_string(from, get_header(req, "From"), sizeof(from)); /* XXX bug in original code, overwrote string */ - if (pedanticsipchecking) - ast_uri_decode(from); - /* XXX here tries to map the username for invite things */ - memset(calleridname, 0, sizeof(calleridname)); - get_calleridname(from, calleridname, sizeof(calleridname)); - if (calleridname[0]) - ast_string_field_set(p, cid_name, calleridname); - - rpid = get_header(req, "Remote-Party-ID"); - memset(rpid_num, 0, sizeof(rpid_num)); - if (!ast_strlen_zero(rpid)) - p->callingpres = get_rpid_num(rpid, rpid_num, sizeof(rpid_num)); - - of = get_in_brackets(from); - if (ast_strlen_zero(p->exten)) { - t = uri2; - if (!strncasecmp(t, "sip:", 4)) - t+= 4; - ast_string_field_set(p, exten, t); - t = strchr(p->exten, '@'); - if (t) - *t = '\0'; - if (ast_strlen_zero(p->our_contact)) - build_contact(p); - } - /* save the URI part of the From header */ - ast_string_field_set(p, from, of); - if (strncasecmp(of, "sip:", 4)) { - ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n"); - } else - of += 4; - /* Get just the username part */ - if ((c = strchr(of, '@'))) { - char *tmp; - *c = '\0'; - if ((c = strchr(of, ':'))) - *c = '\0'; - tmp = ast_strdupa(of); - /* We need to be able to handle auth-headers looking like - <sip:8164444422;phone-context=+1@1.2.3.4:5060;user=phone;tag=SDadkoa01-gK0c3bdb43> - */ - tmp = strsep(&tmp, ";"); - if (ast_is_shrinkable_phonenumber(tmp)) - ast_shrink_phone_number(tmp); - ast_string_field_set(p, cid_num, tmp); - } - - if (!authpeer) /* If we are looking for a peer, don't check the user objects (or realtime) */ - user = find_user(of, 1); - - /* Find user based on user name in the from header */ - if (user && ast_apply_ha(user->ha, sin)) { - ast_copy_flags(&p->flags[0], &user->flags[0], SIP_FLAGS_TO_COPY); - ast_copy_flags(&p->flags[1], &user->flags[1], SIP_PAGE2_FLAGS_TO_COPY); - if (sipmethod == SIP_INVITE) { - /* copy channel vars */ - for (v = user->chanvars ; v ; v = v->next) { - if ((tmpvar = ast_variable_new(v->name, v->value))) { - tmpvar->next = p->chanvars; - p->chanvars = tmpvar; - } - } - } - p->prefs = user->prefs; - /* Set Frame packetization */ - if (p->rtp) { - ast_rtp_codec_setpref(p->rtp, &p->prefs); - p->autoframing = user->autoframing; - } - /* replace callerid if rpid found, and not restricted */ - if (!ast_strlen_zero(rpid_num) && ast_test_flag(&p->flags[0], SIP_TRUSTRPID)) { - char *tmp; - if (*calleridname) - ast_string_field_set(p, cid_name, calleridname); - tmp = ast_strdupa(rpid_num); - if (ast_is_shrinkable_phonenumber(tmp)) - ast_shrink_phone_number(tmp); - ast_string_field_set(p, cid_num, tmp); - } - - do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT_ROUTE) ); - - if (!(res = check_auth(p, req, user->name, user->secret, user->md5secret, sipmethod, uri2, reliable, ast_test_flag(req, SIP_PKT_IGNORE)))) { - if (sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - ast_copy_flags(&p->flags[0], &user->flags[0], SIP_FLAGS_TO_COPY); - ast_copy_flags(&p->flags[1], &user->flags[1], SIP_PAGE2_FLAGS_TO_COPY); - /* Copy SIP extensions profile from INVITE */ - if (p->sipoptions) - user->sipoptions = p->sipoptions; - - /* If we have a call limit, set flag */ - if (user->call_limit) - ast_set_flag(&p->flags[0], SIP_CALL_LIMIT); - if (!ast_strlen_zero(user->context)) - ast_string_field_set(p, context, user->context); - if (!ast_strlen_zero(user->cid_num)) { - char *tmp = ast_strdupa(user->cid_num); - if (ast_is_shrinkable_phonenumber(tmp)) - ast_shrink_phone_number(tmp); - ast_string_field_set(p, cid_num, tmp); - } - if (!ast_strlen_zero(user->cid_name)) - ast_string_field_set(p, cid_name, user->cid_name); - ast_string_field_set(p, username, user->name); - ast_string_field_set(p, peername, user->name); - ast_string_field_set(p, peersecret, user->secret); - ast_string_field_set(p, peermd5secret, user->md5secret); - ast_string_field_set(p, subscribecontext, user->subscribecontext); - ast_string_field_set(p, accountcode, user->accountcode); - ast_string_field_set(p, language, user->language); - ast_string_field_set(p, mohsuggest, user->mohsuggest); - ast_string_field_set(p, mohinterpret, user->mohinterpret); - p->allowtransfer = user->allowtransfer; - p->amaflags = user->amaflags; - p->callgroup = user->callgroup; - p->pickupgroup = user->pickupgroup; - if (user->callingpres) /* User callingpres setting will override RPID header */ - p->callingpres = user->callingpres; - - /* Set default codec settings for this call */ - p->capability = user->capability; /* User codec choice */ - p->jointcapability = user->capability; /* Our codecs */ - if (p->peercapability) /* AND with peer's codecs */ - p->jointcapability &= p->peercapability; - if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || - (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) - p->noncodeccapability |= AST_RTP_DTMF; - else - p->noncodeccapability &= ~AST_RTP_DTMF; - p->jointnoncodeccapability = p->noncodeccapability; - if (p->t38.peercapability) - p->t38.jointcapability &= p->t38.peercapability; - p->maxcallbitrate = user->maxcallbitrate; - /* If we do not support video, remove video from call structure */ - if ((!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) || !(p->capability & AST_FORMAT_VIDEO_MASK)) && p->vrtp) { - ast_rtp_destroy(p->vrtp); - p->vrtp = NULL; - } - } - if (user && debug) - ast_verbose("Found user '%s'\n", user->name); - } else { - if (user) { - if (!authpeer && debug) - ast_verbose("Found user '%s', but fails host access\n", user->name); - ASTOBJ_UNREF(user,sip_destroy_user); - } - user = NULL; - } - - if (!user) { - /* If we didn't find a user match, check for peers */ - if (sipmethod == SIP_SUBSCRIBE) - /* For subscribes, match on peer name only */ - peer = find_peer(of, NULL, 1, 0); - else - /* Look for peer based on the IP address we received data from */ - /* If peer is registered from this IP address or have this as a default - IP address, this call is from the peer - */ - peer = find_peer(NULL, &p->recv, 1, 0); - - if (peer) { - /* Set Frame packetization */ - if (p->rtp) { - ast_rtp_codec_setpref(p->rtp, &peer->prefs); - p->autoframing = peer->autoframing; - } - if (debug) - ast_verbose("Found peer '%s'\n", peer->name); - - /* Take the peer */ - ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY); - ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); - - /* Copy SIP extensions profile to peer */ - if (p->sipoptions) - peer->sipoptions = p->sipoptions; - - /* replace callerid if rpid found, and not restricted */ - if (!ast_strlen_zero(rpid_num) && ast_test_flag(&p->flags[0], SIP_TRUSTRPID)) { - char *tmp = ast_strdupa(rpid_num); - if (*calleridname) - ast_string_field_set(p, cid_name, calleridname); - if (ast_is_shrinkable_phonenumber(tmp)) - ast_shrink_phone_number(tmp); - ast_string_field_set(p, cid_num, tmp); - } - do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT_ROUTE)); - - ast_string_field_set(p, peersecret, peer->secret); - ast_string_field_set(p, peermd5secret, peer->md5secret); - ast_string_field_set(p, subscribecontext, peer->subscribecontext); - ast_string_field_set(p, mohinterpret, peer->mohinterpret); - ast_string_field_set(p, mohsuggest, peer->mohsuggest); - if (peer->callingpres) /* Peer calling pres setting will override RPID */ - p->callingpres = peer->callingpres; - if (peer->maxms && peer->lastms) - p->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms; - if (ast_test_flag(&peer->flags[0], SIP_INSECURE_INVITE)) { - /* Pretend there is no required authentication */ - ast_string_field_free(p, peersecret); - ast_string_field_free(p, peermd5secret); - } - if (!(res = check_auth(p, req, peer->name, p->peersecret, p->peermd5secret, sipmethod, uri2, reliable, ast_test_flag(req, SIP_PKT_IGNORE)))) { - ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY); - ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); - /* If we have a call limit, set flag */ - if (peer->call_limit) - ast_set_flag(&p->flags[0], SIP_CALL_LIMIT); - ast_string_field_set(p, peername, peer->name); - ast_string_field_set(p, authname, peer->name); - - if (sipmethod == SIP_INVITE) { - /* copy channel vars */ - for (v = peer->chanvars ; v ; v = v->next) { - if ((tmpvar = ast_variable_new(v->name, v->value))) { - tmpvar->next = p->chanvars; - p->chanvars = tmpvar; - } - } - } - if (authpeer) { - (*authpeer) = ASTOBJ_REF(peer); /* Add a ref to the object here, to keep it in memory a bit longer if it is realtime */ - } - - if (!ast_strlen_zero(peer->username)) { - ast_string_field_set(p, username, peer->username); - /* Use the default username for authentication on outbound calls */ - /* XXX this takes the name from the caller... can we override ? */ - ast_string_field_set(p, authname, peer->username); - } - if (!ast_strlen_zero(peer->cid_num)) { - char *tmp = ast_strdupa(peer->cid_num); - if (ast_is_shrinkable_phonenumber(tmp)) - ast_shrink_phone_number(tmp); - ast_string_field_set(p, cid_num, tmp); - } - if (!ast_strlen_zero(peer->cid_name)) - ast_string_field_set(p, cid_name, peer->cid_name); - ast_string_field_set(p, fullcontact, peer->fullcontact); - if (!ast_strlen_zero(peer->context)) - ast_string_field_set(p, context, peer->context); - ast_string_field_set(p, peersecret, peer->secret); - ast_string_field_set(p, peermd5secret, peer->md5secret); - ast_string_field_set(p, language, peer->language); - ast_string_field_set(p, accountcode, peer->accountcode); - p->amaflags = peer->amaflags; - p->callgroup = peer->callgroup; - p->pickupgroup = peer->pickupgroup; - p->capability = peer->capability; - p->prefs = peer->prefs; - p->jointcapability = peer->capability; - if (p->peercapability) - p->jointcapability &= p->peercapability; - p->maxcallbitrate = peer->maxcallbitrate; - if ((!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) || !(p->capability & AST_FORMAT_VIDEO_MASK)) && p->vrtp) { - ast_rtp_destroy(p->vrtp); - p->vrtp = NULL; - } - if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || - (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) - p->noncodeccapability |= AST_RTP_DTMF; - else - p->noncodeccapability &= ~AST_RTP_DTMF; - p->jointnoncodeccapability = p->noncodeccapability; - if (p->t38.peercapability) - p->t38.jointcapability &= p->t38.peercapability; - } - ASTOBJ_UNREF(peer, sip_destroy_peer); - } else { - if (debug) - ast_verbose("Found no matching peer or user for '%s:%d'\n", ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port)); - - /* do we allow guests? */ - if (!global_allowguest) { - if (global_alwaysauthreject) - res = AUTH_FAKE_AUTH; /* reject with fake authorization request */ - else - res = AUTH_SECRET_FAILED; /* we don't want any guests, authentication will fail */ - } else if (!ast_strlen_zero(rpid_num) && ast_test_flag(&p->flags[0], SIP_TRUSTRPID)) { - char *tmp = ast_strdupa(rpid_num); - if (*calleridname) - ast_string_field_set(p, cid_name, calleridname); - if (ast_is_shrinkable_phonenumber(tmp)) - ast_shrink_phone_number(tmp); - ast_string_field_set(p, cid_num, tmp); - } - } - - } - - if (user) - ASTOBJ_UNREF(user, sip_destroy_user); - return res; -} - -/*! \brief Find user - If we get a match, this will add a reference pointer to the user object in ASTOBJ, that needs to be unreferenced -*/ -static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, char *uri, enum xmittype reliable, struct sockaddr_in *sin) -{ - return check_user_full(p, req, sipmethod, uri, reliable, sin, NULL); -} - -/*! \brief Get text out of a SIP MESSAGE packet */ -static int get_msg_text(char *buf, int len, struct sip_request *req) -{ - int x; - int y; - - buf[0] = '\0'; - y = len - strlen(buf) - 5; - if (y < 0) - y = 0; - for (x=0;x<req->lines;x++) { - strncat(buf, req->line[x], y); /* safe */ - y -= strlen(req->line[x]) + 1; - if (y < 0) - y = 0; - if (y != 0) - strcat(buf, "\n"); /* safe */ - } - return 0; -} - - -/*! \brief Receive SIP MESSAGE method messages -\note We only handle messages within current calls currently - Reference: RFC 3428 */ -static void receive_message(struct sip_pvt *p, struct sip_request *req) -{ - char buf[1024]; - struct ast_frame f; - const char *content_type = get_header(req, "Content-Type"); - - if (strncmp(content_type, "text/plain", strlen("text/plain"))) { /* No text/plain attachment */ - transmit_response(p, "415 Unsupported Media Type", req); /* Good enough, or? */ - if (!p->owner) - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return; - } - - if (get_msg_text(buf, sizeof(buf), req)) { - ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid); - transmit_response(p, "202 Accepted", req); - if (!p->owner) - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return; - } - - if (p->owner) { - if (sip_debug_test_pvt(p)) - ast_verbose("Message received: '%s'\n", buf); - memset(&f, 0, sizeof(f)); - f.frametype = AST_FRAME_TEXT; - f.subclass = 0; - f.offset = 0; - f.data = buf; - f.datalen = strlen(buf); - ast_queue_frame(p->owner, &f); - transmit_response(p, "202 Accepted", req); /* We respond 202 accepted, since we relay the message */ - } else { /* Message outside of a call, we do not support that */ - ast_log(LOG_WARNING,"Received message to %s from %s, dropped it...\n Content-Type:%s\n Message: %s\n", get_header(req,"To"), get_header(req,"From"), content_type, buf); - transmit_response(p, "405 Method Not Allowed", req); /* Good enough, or? */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - return; -} - -/*! \brief CLI Command to show calls within limits set by call_limit */ -static int sip_show_inuse(int fd, int argc, char *argv[]) -{ -#define FORMAT "%-25.25s %-15.15s %-15.15s \n" -#define FORMAT2 "%-25.25s %-15.15s %-15.15s \n" - char ilimits[40]; - char iused[40]; - int showall = FALSE; - - if (argc < 3) - return RESULT_SHOWUSAGE; - - if (argc == 4 && !strcmp(argv[3],"all")) - showall = TRUE; - - ast_cli(fd, FORMAT, "* User name", "In use", "Limit"); - ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do { - ASTOBJ_RDLOCK(iterator); - if (iterator->call_limit) - snprintf(ilimits, sizeof(ilimits), "%d", iterator->call_limit); - else - ast_copy_string(ilimits, "N/A", sizeof(ilimits)); - snprintf(iused, sizeof(iused), "%d", iterator->inUse); - if (showall || iterator->call_limit) - ast_cli(fd, FORMAT2, iterator->name, iused, ilimits); - ASTOBJ_UNLOCK(iterator); - } while (0) ); - - ast_cli(fd, FORMAT, "* Peer name", "In use", "Limit"); - - ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do { - ASTOBJ_RDLOCK(iterator); - if (iterator->call_limit) - snprintf(ilimits, sizeof(ilimits), "%d", iterator->call_limit); - else - ast_copy_string(ilimits, "N/A", sizeof(ilimits)); - snprintf(iused, sizeof(iused), "%d/%d", iterator->inUse, iterator->inRinging); - if (showall || iterator->call_limit) - ast_cli(fd, FORMAT2, iterator->name, iused, ilimits); - ASTOBJ_UNLOCK(iterator); - } while (0) ); - - return RESULT_SUCCESS; -#undef FORMAT -#undef FORMAT2 -} - -/*! \brief Convert transfer mode to text string */ -static char *transfermode2str(enum transfermodes mode) -{ - if (mode == TRANSFER_OPENFORALL) - return "open"; - else if (mode == TRANSFER_CLOSED) - return "closed"; - return "strict"; -} - -/*! \brief Convert NAT setting to text string */ -static char *nat2str(int nat) -{ - switch(nat) { - case SIP_NAT_NEVER: - return "No"; - case SIP_NAT_ROUTE: - return "Route"; - case SIP_NAT_ALWAYS: - return "Always"; - case SIP_NAT_RFC3581: - return "RFC3581"; - default: - return "Unknown"; - } -} - -/*! \brief Report Peer status in character string - * \return 0 if peer is unreachable, 1 if peer is online, -1 if unmonitored - */ -static int peer_status(struct sip_peer *peer, char *status, int statuslen) -{ - int res = 0; - if (peer->maxms) { - if (peer->lastms < 0) { - ast_copy_string(status, "UNREACHABLE", statuslen); - } else if (peer->lastms > peer->maxms) { - snprintf(status, statuslen, "LAGGED (%d ms)", peer->lastms); - res = 1; - } else if (peer->lastms) { - snprintf(status, statuslen, "OK (%d ms)", peer->lastms); - res = 1; - } else { - ast_copy_string(status, "UNKNOWN", statuslen); - } - } else { - ast_copy_string(status, "Unmonitored", statuslen); - /* Checking if port is 0 */ - res = -1; - } - return res; -} - -/*! \brief CLI Command 'SIP Show Users' */ -static int sip_show_users(int fd, int argc, char *argv[]) -{ - regex_t regexbuf; - int havepattern = FALSE; - -#define FORMAT "%-25.25s %-15.15s %-15.15s %-15.15s %-5.5s%-10.10s\n" - - switch (argc) { - case 5: - if (!strcasecmp(argv[3], "like")) { - if (regcomp(®exbuf, argv[4], REG_EXTENDED | REG_NOSUB)) - return RESULT_SHOWUSAGE; - havepattern = TRUE; - } else - return RESULT_SHOWUSAGE; - case 3: - break; - default: - return RESULT_SHOWUSAGE; - } - - ast_cli(fd, FORMAT, "Username", "Secret", "Accountcode", "Def.Context", "ACL", "NAT"); - ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do { - ASTOBJ_RDLOCK(iterator); - - if (havepattern && regexec(®exbuf, iterator->name, 0, NULL, 0)) { - ASTOBJ_UNLOCK(iterator); - continue; - } - - ast_cli(fd, FORMAT, iterator->name, - iterator->secret, - iterator->accountcode, - iterator->context, - iterator->ha ? "Yes" : "No", - nat2str(ast_test_flag(&iterator->flags[0], SIP_NAT))); - ASTOBJ_UNLOCK(iterator); - } while (0) - ); - - if (havepattern) - regfree(®exbuf); - - return RESULT_SUCCESS; -#undef FORMAT -} - -static char mandescr_show_peers[] = -"Description: Lists SIP peers in text format with details on current status.\n" -"Variables: \n" -" ActionID: <id> Action ID for this transaction. Will be returned.\n"; - -/*! \brief Show SIP peers in the manager API */ -/* Inspired from chan_iax2 */ -static int manager_sip_show_peers(struct mansession *s, const struct message *m) -{ - const char *id = astman_get_header(m,"ActionID"); - const char *a[] = {"sip", "show", "peers"}; - char idtext[256] = ""; - int total = 0; - - if (!ast_strlen_zero(id)) - snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id); - - astman_send_ack(s, m, "Peer status list will follow"); - /* List the peers in separate manager events */ - _sip_show_peers(-1, &total, s, m, 3, a); - /* Send final confirmation */ - astman_append(s, - "Event: PeerlistComplete\r\n" - "ListItems: %d\r\n" - "%s" - "\r\n", total, idtext); - return 0; -} - -/*! \brief CLI Show Peers command */ -static int sip_show_peers(int fd, int argc, char *argv[]) -{ - return _sip_show_peers(fd, NULL, NULL, NULL, argc, (const char **) argv); -} - -/*! \brief _sip_show_peers: Execute sip show peers command */ -static int _sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]) -{ - regex_t regexbuf; - int havepattern = FALSE; - -#define FORMAT2 "%-25.25s %-15.15s %-3.3s %-3.3s %-3.3s %-8s %-10s %-10s\n" -#define FORMAT "%-25.25s %-15.15s %-3.3s %-3.3s %-3.3s %-8d %-10s %-10s\n" - - char name[256]; - int total_peers = 0; - int peers_mon_online = 0; - int peers_mon_offline = 0; - int peers_unmon_offline = 0; - int peers_unmon_online = 0; - const char *id; - char idtext[256] = ""; - int realtimepeers; - - realtimepeers = ast_check_realtime("sippeers"); - - if (s) { /* Manager - get ActionID */ - id = astman_get_header(m,"ActionID"); - if (!ast_strlen_zero(id)) - snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id); - } - - switch (argc) { - case 5: - if (!strcasecmp(argv[3], "like")) { - if (regcomp(®exbuf, argv[4], REG_EXTENDED | REG_NOSUB)) - return RESULT_SHOWUSAGE; - havepattern = TRUE; - } else - return RESULT_SHOWUSAGE; - case 3: - break; - default: - return RESULT_SHOWUSAGE; - } - - if (!s) /* Normal list */ - ast_cli(fd, FORMAT2, "Name/username", "Host", "Dyn", "Nat", "ACL", "Port", "Status", (realtimepeers ? "Realtime" : "")); - - ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do { - char status[20] = ""; - char srch[2000]; - char pstatus; - - ASTOBJ_RDLOCK(iterator); - - if (havepattern && regexec(®exbuf, iterator->name, 0, NULL, 0)) { - ASTOBJ_UNLOCK(iterator); - continue; - } - - if (!ast_strlen_zero(iterator->username) && !s) - snprintf(name, sizeof(name), "%s/%s", iterator->name, iterator->username); - else - ast_copy_string(name, iterator->name, sizeof(name)); - - pstatus = peer_status(iterator, status, sizeof(status)); - if (pstatus == 1) - peers_mon_online++; - else if (pstatus == 0) - peers_mon_offline++; - else { - if (iterator->addr.sin_port == 0) - peers_unmon_offline++; - else - peers_unmon_online++; - } - - snprintf(srch, sizeof(srch), FORMAT, name, - iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iterator->addr.sin_addr) : "(Unspecified)", - ast_test_flag(&iterator->flags[1], SIP_PAGE2_DYNAMIC) ? " D " : " ", /* Dynamic or not? */ - ast_test_flag(&iterator->flags[0], SIP_NAT_ROUTE) ? " N " : " ", /* NAT=yes? */ - iterator->ha ? " A " : " ", /* permit/deny */ - ntohs(iterator->addr.sin_port), status, - realtimepeers ? (ast_test_flag(&iterator->flags[0], SIP_REALTIME) ? "Cached RT":"") : ""); - - if (!s) {/* Normal CLI list */ - ast_cli(fd, FORMAT, name, - iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iterator->addr.sin_addr) : "(Unspecified)", - ast_test_flag(&iterator->flags[1], SIP_PAGE2_DYNAMIC) ? " D " : " ", /* Dynamic or not? */ - ast_test_flag(&iterator->flags[0], SIP_NAT_ROUTE) ? " N " : " ", /* NAT=yes? */ - iterator->ha ? " A " : " ", /* permit/deny */ - - ntohs(iterator->addr.sin_port), status, - realtimepeers ? (ast_test_flag(&iterator->flags[0], SIP_REALTIME) ? "Cached RT":"") : ""); - } else { /* Manager format */ - /* The names here need to be the same as other channels */ - astman_append(s, - "Event: PeerEntry\r\n%s" - "Channeltype: SIP\r\n" - "ObjectName: %s\r\n" - "ChanObjectType: peer\r\n" /* "peer" or "user" */ - "IPaddress: %s\r\n" - "IPport: %d\r\n" - "Dynamic: %s\r\n" - "Natsupport: %s\r\n" - "VideoSupport: %s\r\n" - "ACL: %s\r\n" - "Status: %s\r\n" - "RealtimeDevice: %s\r\n\r\n", - idtext, - iterator->name, - iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iterator->addr.sin_addr) : "-none-", - ntohs(iterator->addr.sin_port), - ast_test_flag(&iterator->flags[1], SIP_PAGE2_DYNAMIC) ? "yes" : "no", /* Dynamic or not? */ - ast_test_flag(&iterator->flags[0], SIP_NAT_ROUTE) ? "yes" : "no", /* NAT=yes? */ - ast_test_flag(&iterator->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no", /* VIDEOSUPPORT=yes? */ - iterator->ha ? "yes" : "no", /* permit/deny */ - status, - realtimepeers ? (ast_test_flag(&iterator->flags[0], SIP_REALTIME) ? "yes":"no") : "no"); - } - - ASTOBJ_UNLOCK(iterator); - - total_peers++; - } while(0) ); - - if (!s) - ast_cli(fd, "%d sip peers [Monitored: %d online, %d offline Unmonitored: %d online, %d offline]\n", - total_peers, peers_mon_online, peers_mon_offline, peers_unmon_online, peers_unmon_offline); - - if (havepattern) - regfree(®exbuf); - - if (total) - *total = total_peers; - - - return RESULT_SUCCESS; -#undef FORMAT -#undef FORMAT2 -} - -/*! \brief List all allocated SIP Objects (realtime or static) */ -static int sip_show_objects(int fd, int argc, char *argv[]) -{ - char tmp[256]; - if (argc != 3) - return RESULT_SHOWUSAGE; - ast_cli(fd, "-= User objects: %d static, %d realtime =-\n\n", suserobjs, ruserobjs); - ASTOBJ_CONTAINER_DUMP(fd, tmp, sizeof(tmp), &userl); - ast_cli(fd, "-= Peer objects: %d static, %d realtime, %d autocreate =-\n\n", speerobjs, rpeerobjs, apeerobjs); - ASTOBJ_CONTAINER_DUMP(fd, tmp, sizeof(tmp), &peerl); - ast_cli(fd, "-= Registry objects: %d =-\n\n", regobjs); - ASTOBJ_CONTAINER_DUMP(fd, tmp, sizeof(tmp), ®l); - return RESULT_SUCCESS; -} -/*! \brief Print call group and pickup group */ -static void print_group(int fd, ast_group_t group, int crlf) -{ - char buf[256]; - ast_cli(fd, crlf ? "%s\r\n" : "%s\n", ast_print_group(buf, sizeof(buf), group) ); -} - -/*! \brief Convert DTMF mode to printable string */ -static const char *dtmfmode2str(int mode) -{ - switch (mode) { - case SIP_DTMF_RFC2833: - return "rfc2833"; - case SIP_DTMF_INFO: - return "info"; - case SIP_DTMF_INBAND: - return "inband"; - case SIP_DTMF_AUTO: - return "auto"; - } - return "<error>"; -} - -/*! \brief Convert Insecure setting to printable string */ -static const char *insecure2str(int port, int invite) -{ - if (port && invite) - return "port,invite"; - else if (port) - return "port"; - else if (invite) - return "invite"; - else - return "no"; -} - -/*! \brief Destroy disused contexts between reloads - Only used in reload_config so the code for regcontext doesn't get ugly -*/ -static void cleanup_stale_contexts(char *new, char *old) -{ - char *oldcontext, *newcontext, *stalecontext, *stringp, newlist[AST_MAX_CONTEXT]; - - while ((oldcontext = strsep(&old, "&"))) { - stalecontext = '\0'; - ast_copy_string(newlist, new, sizeof(newlist)); - stringp = newlist; - while ((newcontext = strsep(&stringp, "&"))) { - if (strcmp(newcontext, oldcontext) == 0) { - /* This is not the context you're looking for */ - stalecontext = '\0'; - break; - } else if (strcmp(newcontext, oldcontext)) { - stalecontext = oldcontext; - } - - } - if (stalecontext) - ast_context_destroy(ast_context_find(stalecontext), "SIP"); - } -} - -/*! \brief Remove temporary realtime objects from memory (CLI) */ -static int sip_prune_realtime(int fd, int argc, char *argv[]) -{ - struct sip_peer *peer; - struct sip_user *user; - int pruneuser = FALSE; - int prunepeer = FALSE; - int multi = FALSE; - char *name = NULL; - regex_t regexbuf; - - switch (argc) { - case 4: - if (!strcasecmp(argv[3], "user")) - return RESULT_SHOWUSAGE; - if (!strcasecmp(argv[3], "peer")) - return RESULT_SHOWUSAGE; - if (!strcasecmp(argv[3], "like")) - return RESULT_SHOWUSAGE; - if (!strcasecmp(argv[3], "all")) { - multi = TRUE; - pruneuser = prunepeer = TRUE; - } else { - pruneuser = prunepeer = TRUE; - name = argv[3]; - } - break; - case 5: - if (!strcasecmp(argv[4], "like")) - return RESULT_SHOWUSAGE; - if (!strcasecmp(argv[3], "all")) - return RESULT_SHOWUSAGE; - if (!strcasecmp(argv[3], "like")) { - multi = TRUE; - name = argv[4]; - pruneuser = prunepeer = TRUE; - } else if (!strcasecmp(argv[3], "user")) { - pruneuser = TRUE; - if (!strcasecmp(argv[4], "all")) - multi = TRUE; - else - name = argv[4]; - } else if (!strcasecmp(argv[3], "peer")) { - prunepeer = TRUE; - if (!strcasecmp(argv[4], "all")) - multi = TRUE; - else - name = argv[4]; - } else - return RESULT_SHOWUSAGE; - break; - case 6: - if (strcasecmp(argv[4], "like")) - return RESULT_SHOWUSAGE; - if (!strcasecmp(argv[3], "user")) { - pruneuser = TRUE; - name = argv[5]; - } else if (!strcasecmp(argv[3], "peer")) { - prunepeer = TRUE; - name = argv[5]; - } else - return RESULT_SHOWUSAGE; - break; - default: - return RESULT_SHOWUSAGE; - } - - if (multi && name) { - if (regcomp(®exbuf, name, REG_EXTENDED | REG_NOSUB)) - return RESULT_SHOWUSAGE; - } - - if (multi) { - if (prunepeer) { - int pruned = 0; - - ASTOBJ_CONTAINER_WRLOCK(&peerl); - ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do { - ASTOBJ_RDLOCK(iterator); - if (name && regexec(®exbuf, iterator->name, 0, NULL, 0)) { - ASTOBJ_UNLOCK(iterator); - continue; - }; - if (ast_test_flag(&iterator->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) { - ASTOBJ_MARK(iterator); - pruned++; - } - ASTOBJ_UNLOCK(iterator); - } while (0) ); - if (pruned) { - ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl, sip_destroy_peer); - ast_cli(fd, "%d peers pruned.\n", pruned); - } else - ast_cli(fd, "No peers found to prune.\n"); - ASTOBJ_CONTAINER_UNLOCK(&peerl); - } - if (pruneuser) { - int pruned = 0; - - ASTOBJ_CONTAINER_WRLOCK(&userl); - ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do { - ASTOBJ_RDLOCK(iterator); - if (name && regexec(®exbuf, iterator->name, 0, NULL, 0)) { - ASTOBJ_UNLOCK(iterator); - continue; - }; - if (ast_test_flag(&iterator->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) { - ASTOBJ_MARK(iterator); - pruned++; - } - ASTOBJ_UNLOCK(iterator); - } while (0) ); - if (pruned) { - ASTOBJ_CONTAINER_PRUNE_MARKED(&userl, sip_destroy_user); - ast_cli(fd, "%d users pruned.\n", pruned); - } else - ast_cli(fd, "No users found to prune.\n"); - ASTOBJ_CONTAINER_UNLOCK(&userl); - } - } else { - if (prunepeer) { - if ((peer = ASTOBJ_CONTAINER_FIND_UNLINK(&peerl, name))) { - if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) { - ast_cli(fd, "Peer '%s' is not a Realtime peer, cannot be pruned.\n", name); - ASTOBJ_CONTAINER_LINK(&peerl, peer); - } else - ast_cli(fd, "Peer '%s' pruned.\n", name); - ASTOBJ_UNREF(peer, sip_destroy_peer); - } else - ast_cli(fd, "Peer '%s' not found.\n", name); - } - if (pruneuser) { - if ((user = ASTOBJ_CONTAINER_FIND_UNLINK(&userl, name))) { - if (!ast_test_flag(&user->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) { - ast_cli(fd, "User '%s' is not a Realtime user, cannot be pruned.\n", name); - ASTOBJ_CONTAINER_LINK(&userl, user); - } else - ast_cli(fd, "User '%s' pruned.\n", name); - ASTOBJ_UNREF(user, sip_destroy_user); - } else - ast_cli(fd, "User '%s' not found.\n", name); - } - } - - return RESULT_SUCCESS; -} - -/*! \brief Print codec list from preference to CLI/manager */ -static void print_codec_to_cli(int fd, struct ast_codec_pref *pref) -{ - int x, codec; - - for(x = 0; x < 32 ; x++) { - codec = ast_codec_pref_index(pref, x); - if (!codec) - break; - ast_cli(fd, "%s", ast_getformatname(codec)); - ast_cli(fd, ":%d", pref->framing[x]); - if (x < 31 && ast_codec_pref_index(pref, x + 1)) - ast_cli(fd, ","); - } - if (!x) - ast_cli(fd, "none"); -} - -/*! \brief Print domain mode to cli */ -static const char *domain_mode_to_text(const enum domain_mode mode) -{ - switch (mode) { - case SIP_DOMAIN_AUTO: - return "[Automatic]"; - case SIP_DOMAIN_CONFIG: - return "[Configured]"; - } - - return ""; -} - -/*! \brief CLI command to list local domains */ -static int sip_show_domains(int fd, int argc, char *argv[]) -{ - struct domain *d; -#define FORMAT "%-40.40s %-20.20s %-16.16s\n" - - if (AST_LIST_EMPTY(&domain_list)) { - ast_cli(fd, "SIP Domain support not enabled.\n\n"); - return RESULT_SUCCESS; - } else { - ast_cli(fd, FORMAT, "Our local SIP domains:", "Context", "Set by"); - AST_LIST_LOCK(&domain_list); - AST_LIST_TRAVERSE(&domain_list, d, list) - ast_cli(fd, FORMAT, d->domain, S_OR(d->context, "(default)"), - domain_mode_to_text(d->mode)); - AST_LIST_UNLOCK(&domain_list); - ast_cli(fd, "\n"); - return RESULT_SUCCESS; - } -} -#undef FORMAT - -static char mandescr_show_peer[] = -"Description: Show one SIP peer with details on current status.\n" -"Variables: \n" -" Peer: <name> The peer name you want to check.\n" -" ActionID: <id> Optional action ID for this AMI transaction.\n"; - -/*! \brief Show SIP peers in the manager API */ -static int manager_sip_show_peer(struct mansession *s, const struct message *m) -{ - const char *a[4]; - const char *peer; - int ret; - - peer = astman_get_header(m,"Peer"); - if (ast_strlen_zero(peer)) { - astman_send_error(s, m, "Peer: <name> missing."); - return 0; - } - a[0] = "sip"; - a[1] = "show"; - a[2] = "peer"; - a[3] = peer; - - ret = _sip_show_peer(1, -1, s, m, 4, a); - astman_append(s, "\r\n\r\n" ); - return ret; -} - - - -/*! \brief Show one peer in detail */ -static int sip_show_peer(int fd, int argc, char *argv[]) -{ - return _sip_show_peer(0, fd, NULL, NULL, argc, (const char **) argv); -} - -/*! \brief Show one peer in detail (main function) */ -static int _sip_show_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[]) -{ - char status[30] = ""; - char cbuf[256]; - struct sip_peer *peer; - char codec_buf[512]; - struct ast_codec_pref *pref; - struct ast_variable *v; - struct sip_auth *auth; - int x = 0, codec = 0, load_realtime; - int realtimepeers; - - realtimepeers = ast_check_realtime("sippeers"); - - if (argc < 4) - return RESULT_SHOWUSAGE; - - load_realtime = (argc == 5 && !strcmp(argv[4], "load")) ? TRUE : FALSE; - peer = find_peer(argv[3], NULL, load_realtime, 0); - if (s) { /* Manager */ - if (peer) { - const char *id = astman_get_header(m,"ActionID"); - - astman_append(s, "Response: Success\r\n"); - if (!ast_strlen_zero(id)) - astman_append(s, "ActionID: %s\r\n",id); - } else { - snprintf (cbuf, sizeof(cbuf), "Peer %s not found.", argv[3]); - astman_send_error(s, m, cbuf); - return 0; - } - } - if (peer && type==0 ) { /* Normal listing */ - ast_cli(fd,"\n\n"); - ast_cli(fd, " * Name : %s\n", peer->name); - if (realtimepeers) { /* Realtime is enabled */ - ast_cli(fd, " Realtime peer: %s\n", ast_test_flag(&peer->flags[0], SIP_REALTIME) ? "Yes, cached" : "No"); - } - ast_cli(fd, " Secret : %s\n", ast_strlen_zero(peer->secret)?"<Not set>":"<Set>"); - ast_cli(fd, " MD5Secret : %s\n", ast_strlen_zero(peer->md5secret)?"<Not set>":"<Set>"); - for (auth = peer->auth; auth; auth = auth->next) { - ast_cli(fd, " Realm-auth : Realm %-15.15s User %-10.20s ", auth->realm, auth->username); - ast_cli(fd, "%s\n", !ast_strlen_zero(auth->secret)?"<Secret set>":(!ast_strlen_zero(auth->md5secret)?"<MD5secret set>" : "<Not set>")); - } - ast_cli(fd, " Context : %s\n", peer->context); - ast_cli(fd, " Subscr.Cont. : %s\n", S_OR(peer->subscribecontext, "<Not set>") ); - ast_cli(fd, " Language : %s\n", peer->language); - if (!ast_strlen_zero(peer->accountcode)) - ast_cli(fd, " Accountcode : %s\n", peer->accountcode); - ast_cli(fd, " AMA flags : %s\n", ast_cdr_flags2str(peer->amaflags)); - ast_cli(fd, " Transfer mode: %s\n", transfermode2str(peer->allowtransfer)); - ast_cli(fd, " CallingPres : %s\n", ast_describe_caller_presentation(peer->callingpres)); - if (!ast_strlen_zero(peer->fromuser)) - ast_cli(fd, " FromUser : %s\n", peer->fromuser); - if (!ast_strlen_zero(peer->fromdomain)) - ast_cli(fd, " FromDomain : %s\n", peer->fromdomain); - ast_cli(fd, " Callgroup : "); - print_group(fd, peer->callgroup, 0); - ast_cli(fd, " Pickupgroup : "); - print_group(fd, peer->pickupgroup, 0); - ast_cli(fd, " Mailbox : %s\n", peer->mailbox); - ast_cli(fd, " VM Extension : %s\n", peer->vmexten); - ast_cli(fd, " LastMsgsSent : %d/%d\n", (peer->lastmsgssent & 0x7fff0000) >> 16, peer->lastmsgssent & 0xffff); - ast_cli(fd, " Call limit : %d\n", peer->call_limit); - ast_cli(fd, " Dynamic : %s\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC)?"Yes":"No")); - ast_cli(fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>")); - ast_cli(fd, " MaxCallBR : %d kbps\n", peer->maxcallbitrate); - ast_cli(fd, " Expire : %ld\n", ast_sched_when(sched, peer->expire)); - ast_cli(fd, " Insecure : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT), ast_test_flag(&peer->flags[0], SIP_INSECURE_INVITE))); - ast_cli(fd, " Nat : %s\n", nat2str(ast_test_flag(&peer->flags[0], SIP_NAT))); - ast_cli(fd, " ACL : %s\n", (peer->ha?"Yes":"No")); - ast_cli(fd, " T38 pt UDPTL : %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_UDPTL)?"Yes":"No"); -#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS - ast_cli(fd, " T38 pt RTP : %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_RTP)?"Yes":"No"); - ast_cli(fd, " T38 pt TCP : %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT_TCP)?"Yes":"No"); -#endif - ast_cli(fd, " CanReinvite : %s\n", ast_test_flag(&peer->flags[0], SIP_CAN_REINVITE)?"Yes":"No"); - ast_cli(fd, " PromiscRedir : %s\n", ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)?"Yes":"No"); - ast_cli(fd, " User=Phone : %s\n", ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)?"Yes":"No"); - ast_cli(fd, " Video Support: %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT)?"Yes":"No"); - ast_cli(fd, " Trust RPID : %s\n", ast_test_flag(&peer->flags[0], SIP_TRUSTRPID) ? "Yes" : "No"); - ast_cli(fd, " Send RPID : %s\n", ast_test_flag(&peer->flags[0], SIP_SENDRPID) ? "Yes" : "No"); - ast_cli(fd, " Subscriptions: %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE) ? "Yes" : "No"); - ast_cli(fd, " Overlap dial : %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWOVERLAP) ? "Yes" : "No"); - - /* - is enumerated */ - ast_cli(fd, " DTMFmode : %s\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF))); - ast_cli(fd, " LastMsg : %d\n", peer->lastmsg); - ast_cli(fd, " ToHost : %s\n", peer->tohost); - ast_cli(fd, " Addr->IP : %s Port %d\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", ntohs(peer->addr.sin_port)); - ast_cli(fd, " Defaddr->IP : %s Port %d\n", ast_inet_ntoa(peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port)); - if (!ast_strlen_zero(global_regcontext)) - ast_cli(fd, " Reg. exten : %s\n", peer->regexten); - ast_cli(fd, " Def. Username: %s\n", peer->username); - ast_cli(fd, " SIP Options : "); - if (peer->sipoptions) { - int lastoption = -1; - for (x=0 ; (x < (sizeof(sip_options) / sizeof(sip_options[0]))); x++) { - if (sip_options[x].id != lastoption) { - if (peer->sipoptions & sip_options[x].id) - ast_cli(fd, "%s ", sip_options[x].text); - lastoption = x; - } - } - } else - ast_cli(fd, "(none)"); - - ast_cli(fd, "\n"); - ast_cli(fd, " Codecs : "); - ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability); - ast_cli(fd, "%s\n", codec_buf); - ast_cli(fd, " Codec Order : ("); - print_codec_to_cli(fd, &peer->prefs); - ast_cli(fd, ")\n"); - - ast_cli(fd, " Auto-Framing: %s \n", peer->autoframing ? "Yes" : "No"); - ast_cli(fd, " Status : "); - peer_status(peer, status, sizeof(status)); - ast_cli(fd, "%s\n",status); - ast_cli(fd, " Useragent : %s\n", peer->useragent); - ast_cli(fd, " Reg. Contact : %s\n", peer->fullcontact); - if (peer->chanvars) { - ast_cli(fd, " Variables :\n"); - for (v = peer->chanvars ; v ; v = v->next) - ast_cli(fd, " %s = %s\n", v->name, v->value); - } - ast_cli(fd,"\n"); - ASTOBJ_UNREF(peer,sip_destroy_peer); - } else if (peer && type == 1) { /* manager listing */ - char buf[256]; - astman_append(s, "Channeltype: SIP\r\n"); - astman_append(s, "ObjectName: %s\r\n", peer->name); - astman_append(s, "ChanObjectType: peer\r\n"); - astman_append(s, "SecretExist: %s\r\n", ast_strlen_zero(peer->secret)?"N":"Y"); - astman_append(s, "MD5SecretExist: %s\r\n", ast_strlen_zero(peer->md5secret)?"N":"Y"); - astman_append(s, "Context: %s\r\n", peer->context); - astman_append(s, "Language: %s\r\n", peer->language); - if (!ast_strlen_zero(peer->accountcode)) - astman_append(s, "Accountcode: %s\r\n", peer->accountcode); - astman_append(s, "AMAflags: %s\r\n", ast_cdr_flags2str(peer->amaflags)); - astman_append(s, "CID-CallingPres: %s\r\n", ast_describe_caller_presentation(peer->callingpres)); - if (!ast_strlen_zero(peer->fromuser)) - astman_append(s, "SIP-FromUser: %s\r\n", peer->fromuser); - if (!ast_strlen_zero(peer->fromdomain)) - astman_append(s, "SIP-FromDomain: %s\r\n", peer->fromdomain); - astman_append(s, "Callgroup: "); - astman_append(s, "%s\r\n", ast_print_group(buf, sizeof(buf), peer->callgroup)); - astman_append(s, "Pickupgroup: "); - astman_append(s, "%s\r\n", ast_print_group(buf, sizeof(buf), peer->pickupgroup)); - astman_append(s, "VoiceMailbox: %s\r\n", peer->mailbox); - astman_append(s, "TransferMode: %s\r\n", transfermode2str(peer->allowtransfer)); - astman_append(s, "LastMsgsSent: %d\r\n", peer->lastmsgssent); - astman_append(s, "Call-limit: %d\r\n", peer->call_limit); - astman_append(s, "MaxCallBR: %d kbps\r\n", peer->maxcallbitrate); - astman_append(s, "Dynamic: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC)?"Y":"N")); - astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "")); - astman_append(s, "RegExpire: %ld seconds\r\n", ast_sched_when(sched,peer->expire)); - astman_append(s, "SIP-AuthInsecure: %s\r\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT), ast_test_flag(&peer->flags[0], SIP_INSECURE_INVITE))); - astman_append(s, "SIP-NatSupport: %s\r\n", nat2str(ast_test_flag(&peer->flags[0], SIP_NAT))); - astman_append(s, "ACL: %s\r\n", (peer->ha?"Y":"N")); - astman_append(s, "SIP-CanReinvite: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_CAN_REINVITE)?"Y":"N")); - astman_append(s, "SIP-PromiscRedir: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR)?"Y":"N")); - astman_append(s, "SIP-UserPhone: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)?"Y":"N")); - astman_append(s, "SIP-VideoSupport: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT)?"Y":"N")); - - /* - is enumerated */ - astman_append(s, "SIP-DTMFmode: %s\r\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF))); - astman_append(s, "SIPLastMsg: %d\r\n", peer->lastmsg); - astman_append(s, "ToHost: %s\r\n", peer->tohost); - astman_append(s, "Address-IP: %s\r\nAddress-Port: %d\r\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "", ntohs(peer->addr.sin_port)); - astman_append(s, "Default-addr-IP: %s\r\nDefault-addr-port: %d\r\n", ast_inet_ntoa(peer->defaddr.sin_addr), ntohs(peer->defaddr.sin_port)); - astman_append(s, "Default-Username: %s\r\n", peer->username); - if (!ast_strlen_zero(global_regcontext)) - astman_append(s, "RegExtension: %s\r\n", peer->regexten); - astman_append(s, "Codecs: "); - ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, peer->capability); - astman_append(s, "%s\r\n", codec_buf); - astman_append(s, "CodecOrder: "); - pref = &peer->prefs; - for(x = 0; x < 32 ; x++) { - codec = ast_codec_pref_index(pref,x); - if (!codec) - break; - astman_append(s, "%s", ast_getformatname(codec)); - if (x < 31 && ast_codec_pref_index(pref,x+1)) - astman_append(s, ","); - } - - astman_append(s, "\r\n"); - astman_append(s, "Status: "); - peer_status(peer, status, sizeof(status)); - astman_append(s, "%s\r\n", status); - astman_append(s, "SIP-Useragent: %s\r\n", peer->useragent); - astman_append(s, "Reg-Contact : %s\r\n", peer->fullcontact); - if (peer->chanvars) { - for (v = peer->chanvars ; v ; v = v->next) { - astman_append(s, "ChanVariable:\n"); - astman_append(s, " %s,%s\r\n", v->name, v->value); - } - } - - ASTOBJ_UNREF(peer,sip_destroy_peer); - - } else { - ast_cli(fd,"Peer %s not found.\n", argv[3]); - ast_cli(fd,"\n"); - } - - return RESULT_SUCCESS; -} - -/*! \brief Show one user in detail */ -static int sip_show_user(int fd, int argc, char *argv[]) -{ - char cbuf[256]; - struct sip_user *user; - struct ast_variable *v; - int load_realtime; - - if (argc < 4) - return RESULT_SHOWUSAGE; - - /* Load from realtime storage? */ - load_realtime = (argc == 5 && !strcmp(argv[4], "load")) ? TRUE : FALSE; - - user = find_user(argv[3], load_realtime); - if (user) { - ast_cli(fd,"\n\n"); - ast_cli(fd, " * Name : %s\n", user->name); - ast_cli(fd, " Secret : %s\n", ast_strlen_zero(user->secret)?"<Not set>":"<Set>"); - ast_cli(fd, " MD5Secret : %s\n", ast_strlen_zero(user->md5secret)?"<Not set>":"<Set>"); - ast_cli(fd, " Context : %s\n", user->context); - ast_cli(fd, " Language : %s\n", user->language); - if (!ast_strlen_zero(user->accountcode)) - ast_cli(fd, " Accountcode : %s\n", user->accountcode); - ast_cli(fd, " AMA flags : %s\n", ast_cdr_flags2str(user->amaflags)); - ast_cli(fd, " Transfer mode: %s\n", transfermode2str(user->allowtransfer)); - ast_cli(fd, " MaxCallBR : %d kbps\n", user->maxcallbitrate); - ast_cli(fd, " CallingPres : %s\n", ast_describe_caller_presentation(user->callingpres)); - ast_cli(fd, " Call limit : %d\n", user->call_limit); - ast_cli(fd, " Callgroup : "); - print_group(fd, user->callgroup, 0); - ast_cli(fd, " Pickupgroup : "); - print_group(fd, user->pickupgroup, 0); - ast_cli(fd, " Callerid : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), user->cid_name, user->cid_num, "<unspecified>")); - ast_cli(fd, " ACL : %s\n", (user->ha?"Yes":"No")); - ast_cli(fd, " Codec Order : ("); - print_codec_to_cli(fd, &user->prefs); - ast_cli(fd, ")\n"); - - ast_cli(fd, " Auto-Framing: %s \n", user->autoframing ? "Yes" : "No"); - if (user->chanvars) { - ast_cli(fd, " Variables :\n"); - for (v = user->chanvars ; v ; v = v->next) - ast_cli(fd, " %s = %s\n", v->name, v->value); - } - ast_cli(fd,"\n"); - ASTOBJ_UNREF(user,sip_destroy_user); - } else { - ast_cli(fd,"User %s not found.\n", argv[3]); - ast_cli(fd,"\n"); - } - - return RESULT_SUCCESS; -} - -/*! \brief Show SIP Registry (registrations with other SIP proxies */ -static int sip_show_registry(int fd, int argc, char *argv[]) -{ -#define FORMAT2 "%-30.30s %-12.12s %8.8s %-20.20s %-25.25s\n" -#define FORMAT "%-30.30s %-12.12s %8d %-20.20s %-25.25s\n" - char host[80]; - char tmpdat[256]; - struct tm tm; - - - if (argc != 3) - return RESULT_SHOWUSAGE; - ast_cli(fd, FORMAT2, "Host", "Username", "Refresh", "State", "Reg.Time"); - ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do { - ASTOBJ_RDLOCK(iterator); - snprintf(host, sizeof(host), "%s:%d", iterator->hostname, iterator->portno ? iterator->portno : STANDARD_SIP_PORT); - if (iterator->regtime) { - ast_localtime(&iterator->regtime, &tm, NULL); - strftime(tmpdat, sizeof(tmpdat), "%a, %d %b %Y %T", &tm); - } else { - tmpdat[0] = 0; - } - ast_cli(fd, FORMAT, host, iterator->username, iterator->refresh, regstate2str(iterator->regstate), tmpdat); - ASTOBJ_UNLOCK(iterator); - } while(0)); - return RESULT_SUCCESS; -#undef FORMAT -#undef FORMAT2 -} - -/*! \brief List global settings for the SIP channel */ -static int sip_show_settings(int fd, int argc, char *argv[]) -{ - int realtimepeers; - int realtimeusers; - char codec_buf[SIPBUFSIZE]; - - realtimepeers = ast_check_realtime("sippeers"); - realtimeusers = ast_check_realtime("sipusers"); - - if (argc != 3) - return RESULT_SHOWUSAGE; - ast_cli(fd, "\n\nGlobal Settings:\n"); - ast_cli(fd, "----------------\n"); - ast_cli(fd, " SIP Port: %d\n", ntohs(bindaddr.sin_port)); - ast_cli(fd, " Bindaddress: %s\n", ast_inet_ntoa(bindaddr.sin_addr)); - ast_cli(fd, " Videosupport: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "Yes" : "No"); - ast_cli(fd, " AutoCreatePeer: %s\n", autocreatepeer ? "Yes" : "No"); - ast_cli(fd, " Allow unknown access: %s\n", global_allowguest ? "Yes" : "No"); - ast_cli(fd, " Allow subscriptions: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE) ? "Yes" : "No"); - ast_cli(fd, " Allow overlap dialing: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP) ? "Yes" : "No"); - ast_cli(fd, " Promsic. redir: %s\n", ast_test_flag(&global_flags[0], SIP_PROMISCREDIR) ? "Yes" : "No"); - ast_cli(fd, " SIP domain support: %s\n", AST_LIST_EMPTY(&domain_list) ? "No" : "Yes"); - ast_cli(fd, " Call to non-local dom.: %s\n", allow_external_domains ? "Yes" : "No"); - ast_cli(fd, " URI user is phone no: %s\n", ast_test_flag(&global_flags[0], SIP_USEREQPHONE) ? "Yes" : "No"); - ast_cli(fd, " Our auth realm %s\n", global_realm); - ast_cli(fd, " Realm. auth: %s\n", authl ? "Yes": "No"); - ast_cli(fd, " Always auth rejects: %s\n", global_alwaysauthreject ? "Yes" : "No"); - ast_cli(fd, " Call limit peers only: %s\n", global_limitonpeers ? "Yes" : "No"); - ast_cli(fd, " Direct RTP setup: %s\n", global_directrtpsetup ? "Yes" : "No"); - ast_cli(fd, " User Agent: %s\n", global_useragent); - ast_cli(fd, " MWI checking interval: %d secs\n", global_mwitime); - ast_cli(fd, " Reg. context: %s\n", S_OR(global_regcontext, "(not set)")); - ast_cli(fd, " Caller ID: %s\n", default_callerid); - ast_cli(fd, " From: Domain: %s\n", default_fromdomain); - ast_cli(fd, " Record SIP history: %s\n", recordhistory ? "On" : "Off"); - ast_cli(fd, " Call Events: %s\n", global_callevents ? "On" : "Off"); - ast_cli(fd, " IP ToS SIP: %s\n", ast_tos2str(global_tos_sip)); - ast_cli(fd, " IP ToS RTP audio: %s\n", ast_tos2str(global_tos_audio)); - ast_cli(fd, " IP ToS RTP video: %s\n", ast_tos2str(global_tos_video)); - ast_cli(fd, " T38 fax pt UDPTL: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_UDPTL) ? "Yes" : "No"); -#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS - ast_cli(fd, " T38 fax pt RTP: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_RTP) ? "Yes" : "No"); - ast_cli(fd, " T38 fax pt TCP: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT_TCP) ? "Yes" : "No"); -#endif - ast_cli(fd, " RFC2833 Compensation: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_RFC2833_COMPENSATE) ? "Yes" : "No"); - if (!realtimepeers && !realtimeusers) - ast_cli(fd, " SIP realtime: Disabled\n" ); - else - ast_cli(fd, " SIP realtime: Enabled\n" ); - - ast_cli(fd, "\nGlobal Signalling Settings:\n"); - ast_cli(fd, "---------------------------\n"); - ast_cli(fd, " Codecs: "); - ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, global_capability); - ast_cli(fd, "%s\n", codec_buf); - ast_cli(fd, " Codec Order: "); - print_codec_to_cli(fd, &default_prefs); - ast_cli(fd, "\n"); - ast_cli(fd, " T1 minimum: %d\n", global_t1min); - ast_cli(fd, " Relax DTMF: %s\n", global_relaxdtmf ? "Yes" : "No"); - ast_cli(fd, " Compact SIP headers: %s\n", compactheaders ? "Yes" : "No"); - ast_cli(fd, " RTP Keepalive: %d %s\n", global_rtpkeepalive, global_rtpkeepalive ? "" : "(Disabled)" ); - ast_cli(fd, " RTP Timeout: %d %s\n", global_rtptimeout, global_rtptimeout ? "" : "(Disabled)" ); - ast_cli(fd, " RTP Hold Timeout: %d %s\n", global_rtpholdtimeout, global_rtpholdtimeout ? "" : "(Disabled)"); - ast_cli(fd, " MWI NOTIFY mime type: %s\n", default_notifymime); - ast_cli(fd, " DNS SRV lookup: %s\n", srvlookup ? "Yes" : "No"); - ast_cli(fd, " Pedantic SIP support: %s\n", pedanticsipchecking ? "Yes" : "No"); - ast_cli(fd, " Reg. min duration %d secs\n", min_expiry); - ast_cli(fd, " Reg. max duration: %d secs\n", max_expiry); - ast_cli(fd, " Reg. default duration: %d secs\n", default_expiry); - ast_cli(fd, " Outbound reg. timeout: %d secs\n", global_reg_timeout); - ast_cli(fd, " Outbound reg. attempts: %d\n", global_regattempts_max); - ast_cli(fd, " Notify ringing state: %s\n", global_notifyringing ? "Yes" : "No"); - ast_cli(fd, " Notify hold state: %s\n", global_notifyhold ? "Yes" : "No"); - ast_cli(fd, " SIP Transfer mode: %s\n", transfermode2str(global_allowtransfer)); - ast_cli(fd, " Max Call Bitrate: %d kbps\r\n", default_maxcallbitrate); - ast_cli(fd, " Auto-Framing: %s \r\n", global_autoframing ? "Yes" : "No"); - ast_cli(fd, "\nDefault Settings:\n"); - ast_cli(fd, "-----------------\n"); - ast_cli(fd, " Context: %s\n", default_context); - ast_cli(fd, " Nat: %s\n", nat2str(ast_test_flag(&global_flags[0], SIP_NAT))); - ast_cli(fd, " DTMF: %s\n", dtmfmode2str(ast_test_flag(&global_flags[0], SIP_DTMF))); - ast_cli(fd, " Qualify: %d\n", default_qualify); - ast_cli(fd, " Use ClientCode: %s\n", ast_test_flag(&global_flags[0], SIP_USECLIENTCODE) ? "Yes" : "No"); - ast_cli(fd, " Progress inband: %s\n", (ast_test_flag(&global_flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER) ? "Never" : (ast_test_flag(&global_flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NO) ? "No" : "Yes" ); - ast_cli(fd, " Language: %s\n", S_OR(default_language, "(Defaults to English)")); - ast_cli(fd, " MOH Interpret: %s\n", default_mohinterpret); - ast_cli(fd, " MOH Suggest: %s\n", default_mohsuggest); - ast_cli(fd, " Voice Mail Extension: %s\n", default_vmexten); - - - if (realtimepeers || realtimeusers) { - ast_cli(fd, "\nRealtime SIP Settings:\n"); - ast_cli(fd, "----------------------\n"); - ast_cli(fd, " Realtime Peers: %s\n", realtimepeers ? "Yes" : "No"); - ast_cli(fd, " Realtime Users: %s\n", realtimeusers ? "Yes" : "No"); - ast_cli(fd, " Cache Friends: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) ? "Yes" : "No"); - ast_cli(fd, " Update: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_RTUPDATE) ? "Yes" : "No"); - ast_cli(fd, " Ignore Reg. Expire: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_IGNOREREGEXPIRE) ? "Yes" : "No"); - ast_cli(fd, " Save sys. name: %s\n", ast_test_flag(&global_flags[1], SIP_PAGE2_RTSAVE_SYSNAME) ? "Yes" : "No"); - ast_cli(fd, " Auto Clear: %d\n", global_rtautoclear); - } - ast_cli(fd, "\n----\n"); - return RESULT_SUCCESS; -} - -/*! \brief Show subscription type in string format */ -static const char *subscription_type2str(enum subscriptiontype subtype) -{ - int i; - - for (i = 1; (i < (sizeof(subscription_types) / sizeof(subscription_types[0]))); i++) { - if (subscription_types[i].type == subtype) { - return subscription_types[i].text; - } - } - return subscription_types[0].text; -} - -/*! \brief Find subscription type in array */ -static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype) -{ - int i; - - for (i = 1; (i < (sizeof(subscription_types) / sizeof(subscription_types[0]))); i++) { - if (subscription_types[i].type == subtype) { - return &subscription_types[i]; - } - } - return &subscription_types[0]; -} - -/*! \brief Show active SIP channels */ -static int sip_show_channels(int fd, int argc, char *argv[]) -{ - return __sip_show_channels(fd, argc, argv, 0); -} - -/*! \brief Show active SIP subscriptions */ -static int sip_show_subscriptions(int fd, int argc, char *argv[]) -{ - return __sip_show_channels(fd, argc, argv, 1); -} - -/*! \brief SIP show channels CLI (main function) */ -static int __sip_show_channels(int fd, int argc, char *argv[], int subscriptions) -{ -#define FORMAT3 "%-15.15s %-10.10s %-11.11s %-15.15s %-13.13s %-15.15s %-10.10s\n" -#define FORMAT2 "%-15.15s %-10.10s %-11.11s %-11.11s %-15.15s %-7.7s %-15.15s\n" -#define FORMAT "%-15.15s %-10.10s %-11.11s %5.5d/%5.5d %-15.15s %-3.3s %-3.3s %-15.15s %-10.10s\n" - struct sip_pvt *cur; - int numchans = 0; - char *referstatus = NULL; - - if (argc != 3) - return RESULT_SHOWUSAGE; - ast_mutex_lock(&iflock); - cur = iflist; - if (!subscriptions) - ast_cli(fd, FORMAT2, "Peer", "User/ANR", "Call ID", "Seq (Tx/Rx)", "Format", "Hold", "Last Message"); - else - ast_cli(fd, FORMAT3, "Peer", "User", "Call ID", "Extension", "Last state", "Type", "Mailbox"); - for (; cur; cur = cur->next) { - referstatus = ""; - if (cur->refer) { /* SIP transfer in progress */ - referstatus = referstatus2str(cur->refer->status); - } - if (cur->subscribed == NONE && !subscriptions) { - char formatbuf[SIPBUFSIZE/2]; - ast_cli(fd, FORMAT, ast_inet_ntoa(cur->sa.sin_addr), - S_OR(cur->username, S_OR(cur->cid_num, "(None)")), - cur->callid, - cur->ocseq, cur->icseq, - ast_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->owner ? cur->owner->nativeformats : 0), - ast_test_flag(&cur->flags[1], SIP_PAGE2_CALL_ONHOLD) ? "Yes" : "No", - ast_test_flag(&cur->flags[0], SIP_NEEDDESTROY) ? "(d)" : "", - cur->lastmsg , - referstatus - ); - numchans++; - } - if (cur->subscribed != NONE && subscriptions) { - ast_cli(fd, FORMAT3, ast_inet_ntoa(cur->sa.sin_addr), - S_OR(cur->username, S_OR(cur->cid_num, "(None)")), - cur->callid, - /* the 'complete' exten/context is hidden in the refer_to field for subscriptions */ - cur->subscribed == MWI_NOTIFICATION ? "--" : cur->subscribeuri, - cur->subscribed == MWI_NOTIFICATION ? "<none>" : ast_extension_state2str(cur->laststate), - subscription_type2str(cur->subscribed), - cur->subscribed == MWI_NOTIFICATION ? (cur->relatedpeer ? cur->relatedpeer->mailbox : "<none>") : "<none>" -); - numchans++; - } - } - ast_mutex_unlock(&iflock); - if (!subscriptions) - ast_cli(fd, "%d active SIP channel%s\n", numchans, (numchans != 1) ? "s" : ""); - else - ast_cli(fd, "%d active SIP subscription%s\n", numchans, (numchans != 1) ? "s" : ""); - return RESULT_SUCCESS; -#undef FORMAT -#undef FORMAT2 -#undef FORMAT3 -} - -/*! \brief Support routine for 'sip show channel' CLI */ -static char *complete_sipch(const char *line, const char *word, int pos, int state) -{ - int which=0; - struct sip_pvt *cur; - char *c = NULL; - int wordlen = strlen(word); - - if (pos != 3) { - return NULL; - } - - ast_mutex_lock(&iflock); - for (cur = iflist; cur; cur = cur->next) { - if (!strncasecmp(word, cur->callid, wordlen) && ++which > state) { - c = ast_strdup(cur->callid); - break; - } - } - ast_mutex_unlock(&iflock); - return c; -} - -/*! \brief Do completion on peer name */ -static char *complete_sip_peer(const char *word, int state, int flags2) -{ - char *result = NULL; - int wordlen = strlen(word); - int which = 0; - - ASTOBJ_CONTAINER_TRAVERSE(&peerl, !result, do { - /* locking of the object is not required because only the name and flags are being compared */ - if (!strncasecmp(word, iterator->name, wordlen) && - (!flags2 || ast_test_flag(&iterator->flags[1], flags2)) && - ++which > state) - result = ast_strdup(iterator->name); - } while(0) ); - return result; -} - -/*! \brief Support routine for 'sip show peer' CLI */ -static char *complete_sip_show_peer(const char *line, const char *word, int pos, int state) -{ - if (pos == 3) - return complete_sip_peer(word, state, 0); - - return NULL; -} - -/*! \brief Support routine for 'sip debug peer' CLI */ -static char *complete_sip_debug_peer(const char *line, const char *word, int pos, int state) -{ - if (pos == 3) - return complete_sip_peer(word, state, 0); - - return NULL; -} - -/*! \brief Do completion on user name */ -static char *complete_sip_user(const char *word, int state, int flags2) -{ - char *result = NULL; - int wordlen = strlen(word); - int which = 0; - - ASTOBJ_CONTAINER_TRAVERSE(&userl, !result, do { - /* locking of the object is not required because only the name and flags are being compared */ - if (!strncasecmp(word, iterator->name, wordlen)) { - if (flags2 && !ast_test_flag(&iterator->flags[1], flags2)) - continue; - if (++which > state) { - result = ast_strdup(iterator->name); - } - } - } while(0) ); - return result; -} - -/*! \brief Support routine for 'sip show user' CLI */ -static char *complete_sip_show_user(const char *line, const char *word, int pos, int state) -{ - if (pos == 3) - return complete_sip_user(word, state, 0); - - return NULL; -} - -/*! \brief Support routine for 'sip notify' CLI */ -static char *complete_sipnotify(const char *line, const char *word, int pos, int state) -{ - char *c = NULL; - - if (pos == 2) { - int which = 0; - char *cat = NULL; - int wordlen = strlen(word); - - /* do completion for notify type */ - - if (!notify_types) - return NULL; - - while ( (cat = ast_category_browse(notify_types, cat)) ) { - if (!strncasecmp(word, cat, wordlen) && ++which > state) { - c = ast_strdup(cat); - break; - } - } - return c; - } - - if (pos > 2) - return complete_sip_peer(word, state, 0); - - return NULL; -} - -/*! \brief Support routine for 'sip prune realtime peer' CLI */ -static char *complete_sip_prune_realtime_peer(const char *line, const char *word, int pos, int state) -{ - if (pos == 4) - return complete_sip_peer(word, state, SIP_PAGE2_RTCACHEFRIENDS); - return NULL; -} - -/*! \brief Support routine for 'sip prune realtime user' CLI */ -static char *complete_sip_prune_realtime_user(const char *line, const char *word, int pos, int state) -{ - if (pos == 4) - return complete_sip_user(word, state, SIP_PAGE2_RTCACHEFRIENDS); - - return NULL; -} - -/*! \brief Show details of one active dialog */ -static int sip_show_channel(int fd, int argc, char *argv[]) -{ - struct sip_pvt *cur; - size_t len; - int found = 0; - - if (argc != 4) - return RESULT_SHOWUSAGE; - len = strlen(argv[3]); - ast_mutex_lock(&iflock); - for (cur = iflist; cur; cur = cur->next) { - if (!strncasecmp(cur->callid, argv[3], len)) { - char formatbuf[SIPBUFSIZE/2]; - ast_cli(fd,"\n"); - if (cur->subscribed != NONE) - ast_cli(fd, " * Subscription (type: %s)\n", subscription_type2str(cur->subscribed)); - else - ast_cli(fd, " * SIP Call\n"); - ast_cli(fd, " Curr. trans. direction: %s\n", ast_test_flag(&cur->flags[0], SIP_OUTGOING) ? "Outgoing" : "Incoming"); - ast_cli(fd, " Call-ID: %s\n", cur->callid); - ast_cli(fd, " Owner channel ID: %s\n", cur->owner ? cur->owner->name : "<none>"); - ast_cli(fd, " Our Codec Capability: %d\n", cur->capability); - ast_cli(fd, " Non-Codec Capability (DTMF): %d\n", cur->noncodeccapability); - ast_cli(fd, " Their Codec Capability: %d\n", cur->peercapability); - ast_cli(fd, " Joint Codec Capability: %d\n", cur->jointcapability); - ast_cli(fd, " Format: %s\n", ast_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->owner ? cur->owner->nativeformats : 0) ); - ast_cli(fd, " MaxCallBR: %d kbps\n", cur->maxcallbitrate); - ast_cli(fd, " Theoretical Address: %s:%d\n", ast_inet_ntoa(cur->sa.sin_addr), ntohs(cur->sa.sin_port)); - ast_cli(fd, " Received Address: %s:%d\n", ast_inet_ntoa(cur->recv.sin_addr), ntohs(cur->recv.sin_port)); - ast_cli(fd, " SIP Transfer mode: %s\n", transfermode2str(cur->allowtransfer)); - ast_cli(fd, " NAT Support: %s\n", nat2str(ast_test_flag(&cur->flags[0], SIP_NAT))); - ast_cli(fd, " Audio IP: %s %s\n", ast_inet_ntoa(cur->redirip.sin_addr.s_addr ? cur->redirip.sin_addr : cur->ourip), cur->redirip.sin_addr.s_addr ? "(Outside bridge)" : "(local)" ); - ast_cli(fd, " Our Tag: %s\n", cur->tag); - ast_cli(fd, " Their Tag: %s\n", cur->theirtag); - ast_cli(fd, " SIP User agent: %s\n", cur->useragent); - if (!ast_strlen_zero(cur->username)) - ast_cli(fd, " Username: %s\n", cur->username); - if (!ast_strlen_zero(cur->peername)) - ast_cli(fd, " Peername: %s\n", cur->peername); - if (!ast_strlen_zero(cur->uri)) - ast_cli(fd, " Original uri: %s\n", cur->uri); - if (!ast_strlen_zero(cur->cid_num)) - ast_cli(fd, " Caller-ID: %s\n", cur->cid_num); - ast_cli(fd, " Need Destroy: %d\n", ast_test_flag(&cur->flags[0], SIP_NEEDDESTROY)); - ast_cli(fd, " Last Message: %s\n", cur->lastmsg); - ast_cli(fd, " Promiscuous Redir: %s\n", ast_test_flag(&cur->flags[0], SIP_PROMISCREDIR) ? "Yes" : "No"); - ast_cli(fd, " Route: %s\n", cur->route ? cur->route->hop : "N/A"); - ast_cli(fd, " DTMF Mode: %s\n", dtmfmode2str(ast_test_flag(&cur->flags[0], SIP_DTMF))); - ast_cli(fd, " SIP Options: "); - if (cur->sipoptions) { - int x; - for (x=0 ; (x < (sizeof(sip_options) / sizeof(sip_options[0]))); x++) { - if (cur->sipoptions & sip_options[x].id) - ast_cli(fd, "%s ", sip_options[x].text); - } - } else - ast_cli(fd, "(none)\n"); - ast_cli(fd, "\n\n"); - found++; - } - } - ast_mutex_unlock(&iflock); - if (!found) - ast_cli(fd, "No such SIP Call ID starting with '%s'\n", argv[3]); - return RESULT_SUCCESS; -} - -/*! \brief Show history details of one dialog */ -static int sip_show_history(int fd, int argc, char *argv[]) -{ - struct sip_pvt *cur; - size_t len; - int found = 0; - - if (argc != 4) - return RESULT_SHOWUSAGE; - if (!recordhistory) - ast_cli(fd, "\n***Note: History recording is currently DISABLED. Use 'sip history' to ENABLE.\n"); - len = strlen(argv[3]); - ast_mutex_lock(&iflock); - for (cur = iflist; cur; cur = cur->next) { - if (!strncasecmp(cur->callid, argv[3], len)) { - struct sip_history *hist; - int x = 0; - - ast_cli(fd,"\n"); - if (cur->subscribed != NONE) - ast_cli(fd, " * Subscription\n"); - else - ast_cli(fd, " * SIP Call\n"); - if (cur->history) - AST_LIST_TRAVERSE(cur->history, hist, list) - ast_cli(fd, "%d. %s\n", ++x, hist->event); - if (x == 0) - ast_cli(fd, "Call '%s' has no history\n", cur->callid); - found++; - } - } - ast_mutex_unlock(&iflock); - if (!found) - ast_cli(fd, "No such SIP Call ID starting with '%s'\n", argv[3]); - return RESULT_SUCCESS; -} - -/*! \brief Dump SIP history to debug log file at end of lifespan for SIP dialog */ -static void sip_dump_history(struct sip_pvt *dialog) -{ - int x = 0; - struct sip_history *hist; - static int errmsg = 0; - - if (!dialog) - return; - - if (!option_debug && !sipdebug) { - if (!errmsg) { - ast_log(LOG_NOTICE, "You must have debugging enabled (SIP or Asterisk) in order to dump SIP history.\n"); - errmsg = 1; - } - return; - } - - ast_log(LOG_DEBUG, "\n---------- SIP HISTORY for '%s' \n", dialog->callid); - if (dialog->subscribed) - ast_log(LOG_DEBUG, " * Subscription\n"); - else - ast_log(LOG_DEBUG, " * SIP Call\n"); - if (dialog->history) - AST_LIST_TRAVERSE(dialog->history, hist, list) - ast_log(LOG_DEBUG, " %-3.3d. %s\n", ++x, hist->event); - if (!x) - ast_log(LOG_DEBUG, "Call '%s' has no history\n", dialog->callid); - ast_log(LOG_DEBUG, "\n---------- END SIP HISTORY for '%s' \n", dialog->callid); -} - - -/*! \brief Receive SIP INFO Message -\note Doesn't read the duration of the DTMF signal */ -static void handle_request_info(struct sip_pvt *p, struct sip_request *req) -{ - char buf[1024]; - unsigned int event; - const char *c = get_header(req, "Content-Type"); - - /* Need to check the media/type */ - if (!strcasecmp(c, "application/dtmf-relay") || - !strcasecmp(c, "application/vnd.nortelnetworks.digits")) { - unsigned int duration = 0; - - /* Try getting the "signal=" part */ - if (ast_strlen_zero(c = get_body(req, "Signal")) && ast_strlen_zero(c = get_body(req, "d"))) { - ast_log(LOG_WARNING, "Unable to retrieve DTMF signal from INFO message from %s\n", p->callid); - transmit_response(p, "200 OK", req); /* Should return error */ - return; - } else { - ast_copy_string(buf, c, sizeof(buf)); - } - - if (!ast_strlen_zero((c = get_body(req, "Duration")))) - duration = atoi(c); - if (!duration) - duration = 100; /* 100 ms */ - - if (!p->owner) { /* not a PBX call */ - transmit_response(p, "481 Call leg/transaction does not exist", req); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return; - } - - if (ast_strlen_zero(buf)) { - transmit_response(p, "200 OK", req); - return; - } - - if (buf[0] == '*') - event = 10; - else if (buf[0] == '#') - event = 11; - else if ((buf[0] >= 'A') && (buf[0] <= 'D')) - event = 12 + buf[0] - 'A'; - else - event = atoi(buf); - if (event == 16) { - /* send a FLASH event */ - struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_FLASH, }; - ast_queue_frame(p->owner, &f); - if (sipdebug) - ast_verbose("* DTMF-relay event received: FLASH\n"); - } else { - /* send a DTMF event */ - struct ast_frame f = { AST_FRAME_DTMF, }; - if (event < 10) { - f.subclass = '0' + event; - } else if (event < 11) { - f.subclass = '*'; - } else if (event < 12) { - f.subclass = '#'; - } else if (event < 16) { - f.subclass = 'A' + (event - 12); - } - f.len = duration; - ast_queue_frame(p->owner, &f); - if (sipdebug) - ast_verbose("* DTMF-relay event received: %c\n", f.subclass); - } - transmit_response(p, "200 OK", req); - return; - } else if (!strcasecmp(c, "application/media_control+xml")) { - /* Eh, we'll just assume it's a fast picture update for now */ - if (p->owner) - ast_queue_control(p->owner, AST_CONTROL_VIDUPDATE); - transmit_response(p, "200 OK", req); - return; - } else if (!ast_strlen_zero(c = get_header(req, "X-ClientCode"))) { - /* Client code (from SNOM phone) */ - if (ast_test_flag(&p->flags[0], SIP_USECLIENTCODE)) { - if (p->owner && p->owner->cdr) - ast_cdr_setuserfield(p->owner, c); - if (p->owner && ast_bridged_channel(p->owner) && ast_bridged_channel(p->owner)->cdr) - ast_cdr_setuserfield(ast_bridged_channel(p->owner), c); - transmit_response(p, "200 OK", req); - } else { - transmit_response(p, "403 Unauthorized", req); - } - return; - } else if (ast_strlen_zero(c = get_header(req, "Content-Length")) || !strcasecmp(c, "0")) { - /* This is probably just a packet making sure the signalling is still up, just send back a 200 OK */ - transmit_response(p, "200 OK", req); - return; - } - - /* Other type of INFO message, not really understood by Asterisk */ - /* if (get_msg_text(buf, sizeof(buf), req)) { */ - - ast_log(LOG_WARNING, "Unable to parse INFO message from %s. Content %s\n", p->callid, buf); - transmit_response(p, "415 Unsupported media type", req); - return; -} - -/*! \brief Enable SIP Debugging in CLI */ -static int sip_do_debug_ip(int fd, int argc, char *argv[]) -{ - struct hostent *hp; - struct ast_hostent ahp; - int port = 0; - char *p, *arg; - - /* sip set debug ip <ip> */ - if (argc != 5) - return RESULT_SHOWUSAGE; - p = arg = argv[4]; - strsep(&p, ":"); - if (p) - port = atoi(p); - hp = ast_gethostbyname(arg, &ahp); - if (hp == NULL) - return RESULT_SHOWUSAGE; - - debugaddr.sin_family = AF_INET; - memcpy(&debugaddr.sin_addr, hp->h_addr, sizeof(debugaddr.sin_addr)); - debugaddr.sin_port = htons(port); - if (port == 0) - ast_cli(fd, "SIP Debugging Enabled for IP: %s\n", ast_inet_ntoa(debugaddr.sin_addr)); - else - ast_cli(fd, "SIP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(debugaddr.sin_addr), port); - - ast_set_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONSOLE); - - return RESULT_SUCCESS; -} - -/*! \brief sip_do_debug_peer: Turn on SIP debugging with peer mask */ -static int sip_do_debug_peer(int fd, int argc, char *argv[]) -{ - struct sip_peer *peer; - if (argc != 5) - return RESULT_SHOWUSAGE; - peer = find_peer(argv[4], NULL, 1, 0); - if (peer) { - if (peer->addr.sin_addr.s_addr) { - debugaddr.sin_family = AF_INET; - debugaddr.sin_addr = peer->addr.sin_addr; - debugaddr.sin_port = peer->addr.sin_port; - ast_cli(fd, "SIP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(debugaddr.sin_addr), ntohs(debugaddr.sin_port)); - ast_set_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONSOLE); - } else - ast_cli(fd, "Unable to get IP address of peer '%s'\n", argv[4]); - ASTOBJ_UNREF(peer,sip_destroy_peer); - } else - ast_cli(fd, "No such peer '%s'\n", argv[4]); - return RESULT_SUCCESS; -} - -/*! \brief Turn on SIP debugging (CLI command) */ -static int sip_do_debug(int fd, int argc, char *argv[]) -{ - int oldsipdebug = sipdebug_console; - if (argc != 3) { - if (argc != 5) - return RESULT_SHOWUSAGE; - else if (strcmp(argv[3], "ip") == 0) - return sip_do_debug_ip(fd, argc, argv); - else if (strcmp(argv[3], "peer") == 0) - return sip_do_debug_peer(fd, argc, argv); - else - return RESULT_SHOWUSAGE; - } - ast_set_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONSOLE); - memset(&debugaddr, 0, sizeof(debugaddr)); - ast_cli(fd, "SIP Debugging %senabled\n", oldsipdebug ? "re-" : ""); - return RESULT_SUCCESS; -} - -static int sip_do_debug_deprecated(int fd, int argc, char *argv[]) -{ - int oldsipdebug = sipdebug_console; - char *newargv[6] = { "sip", "set", "debug", NULL }; - if (argc != 2) { - if (argc != 4) - return RESULT_SHOWUSAGE; - else if (strcmp(argv[2], "ip") == 0) { - newargv[3] = argv[2]; - newargv[4] = argv[3]; - return sip_do_debug_ip(fd, argc + 1, newargv); - } else if (strcmp(argv[2], "peer") == 0) { - newargv[3] = argv[2]; - newargv[4] = argv[3]; - return sip_do_debug_peer(fd, argc + 1, newargv); - } else - return RESULT_SHOWUSAGE; - } - ast_set_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONSOLE); - memset(&debugaddr, 0, sizeof(debugaddr)); - ast_cli(fd, "SIP Debugging %senabled\n", oldsipdebug ? "re-" : ""); - return RESULT_SUCCESS; -} - -/*! \brief Cli command to send SIP notify to peer */ -static int sip_notify(int fd, int argc, char *argv[]) -{ - struct ast_variable *varlist; - int i; - - if (argc < 4) - return RESULT_SHOWUSAGE; - - if (!notify_types) { - ast_cli(fd, "No %s file found, or no types listed there\n", notify_config); - return RESULT_FAILURE; - } - - varlist = ast_variable_browse(notify_types, argv[2]); - - if (!varlist) { - ast_cli(fd, "Unable to find notify type '%s'\n", argv[2]); - return RESULT_FAILURE; - } - - for (i = 3; i < argc; i++) { - struct sip_pvt *p; - struct sip_request req; - struct ast_variable *var; - - if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY))) { - ast_log(LOG_WARNING, "Unable to build sip pvt data for notify (memory/socket error)\n"); - return RESULT_FAILURE; - } - - if (create_addr(p, argv[i])) { - /* Maybe they're not registered, etc. */ - sip_destroy(p); - ast_cli(fd, "Could not create address for '%s'\n", argv[i]); - continue; - } - - initreqprep(&req, p, SIP_NOTIFY); - - for (var = varlist; var; var = var->next) - add_header(&req, var->name, ast_unescape_semicolon(var->value)); - - /* Recalculate our side, and recalculate Call ID */ - if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip)) - p->ourip = __ourip; - build_via(p); - build_callid_pvt(p); - ast_cli(fd, "Sending NOTIFY of type '%s' to '%s'\n", argv[2], argv[i]); - transmit_sip_request(p, &req); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - - return RESULT_SUCCESS; -} - -/*! \brief Disable SIP Debugging in CLI */ -static int sip_no_debug(int fd, int argc, char *argv[]) -{ - if (argc != 4) - return RESULT_SHOWUSAGE; - ast_clear_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONSOLE); - ast_cli(fd, "SIP Debugging Disabled\n"); - return RESULT_SUCCESS; -} - -static int sip_no_debug_deprecated(int fd, int argc, char *argv[]) -{ - if (argc != 3) - return RESULT_SHOWUSAGE; - ast_clear_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONSOLE); - ast_cli(fd, "SIP Debugging Disabled\n"); - return RESULT_SUCCESS; -} - -/*! \brief Enable SIP History logging (CLI) */ -static int sip_do_history(int fd, int argc, char *argv[]) -{ - if (argc != 2) { - return RESULT_SHOWUSAGE; - } - recordhistory = TRUE; - ast_cli(fd, "SIP History Recording Enabled (use 'sip show history')\n"); - return RESULT_SUCCESS; -} - -/*! \brief Disable SIP History logging (CLI) */ -static int sip_no_history(int fd, int argc, char *argv[]) -{ - if (argc != 3) { - return RESULT_SHOWUSAGE; - } - recordhistory = FALSE; - ast_cli(fd, "SIP History Recording Disabled\n"); - return RESULT_SUCCESS; -} - -/*! \brief Authenticate for outbound registration */ -static int do_register_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader) -{ - char digest[1024]; - p->authtries++; - memset(digest,0,sizeof(digest)); - if (reply_digest(p, req, header, SIP_REGISTER, digest, sizeof(digest))) { - /* There's nothing to use for authentication */ - /* No digest challenge in request */ - if (sip_debug_test_pvt(p) && p->registry) - ast_verbose("No authentication challenge, sending blank registration to domain/host name %s\n", p->registry->hostname); - /* No old challenge */ - return -1; - } - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) - append_history(p, "RegistryAuth", "Try: %d", p->authtries); - if (sip_debug_test_pvt(p) && p->registry) - ast_verbose("Responding to challenge, registration to domain/host name %s\n", p->registry->hostname); - return transmit_register(p->registry, SIP_REGISTER, digest, respheader); -} - -/*! \brief Add authentication on outbound SIP packet */ -static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, int sipmethod, int init) -{ - char digest[1024]; - - if (!p->options && !(p->options = ast_calloc(1, sizeof(*p->options)))) - return -2; - - p->authtries++; - if (option_debug > 1) - ast_log(LOG_DEBUG, "Auth attempt %d on %s\n", p->authtries, sip_methods[sipmethod].text); - memset(digest, 0, sizeof(digest)); - if (reply_digest(p, req, header, sipmethod, digest, sizeof(digest) )) { - /* No way to authenticate */ - return -1; - } - /* Now we have a reply digest */ - p->options->auth = digest; - p->options->authheader = respheader; - return transmit_invite(p, sipmethod, sipmethod == SIP_INVITE, init); -} - -/*! \brief reply to authentication for outbound registrations -\return Returns -1 if we have no auth -\note This is used for register= servers in sip.conf, SIP proxies we register - with for receiving calls from. */ -static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len) -{ - char tmp[512]; - char *c; - char oldnonce[256]; - - /* table of recognised keywords, and places where they should be copied */ - const struct x { - const char *key; - int field_index; - } *i, keys[] = { - { "realm=", ast_string_field_index(p, realm) }, - { "nonce=", ast_string_field_index(p, nonce) }, - { "opaque=", ast_string_field_index(p, opaque) }, - { "qop=", ast_string_field_index(p, qop) }, - { "domain=", ast_string_field_index(p, domain) }, - { NULL, 0 }, - }; - - ast_copy_string(tmp, get_header(req, header), sizeof(tmp)); - if (ast_strlen_zero(tmp)) - return -1; - if (strncasecmp(tmp, "Digest ", strlen("Digest "))) { - ast_log(LOG_WARNING, "missing Digest.\n"); - return -1; - } - c = tmp + strlen("Digest "); - ast_copy_string(oldnonce, p->nonce, sizeof(oldnonce)); - while (c && *(c = ast_skip_blanks(c))) { /* lookup for keys */ - for (i = keys; i->key != NULL; i++) { - char *src, *separator; - if (strncasecmp(c, i->key, strlen(i->key)) != 0) - continue; - /* Found. Skip keyword, take text in quotes or up to the separator. */ - c += strlen(i->key); - if (*c == '"') { - src = ++c; - separator = "\""; - } else { - src = c; - separator = ","; - } - strsep(&c, separator); /* clear separator and move ptr */ - ast_string_field_index_set(p, i->field_index, src); - break; - } - if (i->key == NULL) /* not found, try ',' */ - strsep(&c, ","); - } - /* Reset nonce count */ - if (strcmp(p->nonce, oldnonce)) - p->noncecount = 0; - - /* Save auth data for following registrations */ - if (p->registry) { - struct sip_registry *r = p->registry; - - if (strcmp(r->nonce, p->nonce)) { - ast_string_field_set(r, realm, p->realm); - ast_string_field_set(r, nonce, p->nonce); - ast_string_field_set(r, domain, p->domain); - ast_string_field_set(r, opaque, p->opaque); - ast_string_field_set(r, qop, p->qop); - r->noncecount = 0; - } - } - return build_reply_digest(p, sipmethod, digest, digest_len); -} - -/*! \brief Build reply digest -\return Returns -1 if we have no auth -\note Build digest challenge for authentication of peers (for registration) - and users (for calls). Also used for authentication of CANCEL and BYE -*/ -static int build_reply_digest(struct sip_pvt *p, int method, char* digest, int digest_len) -{ - char a1[256]; - char a2[256]; - char a1_hash[256]; - char a2_hash[256]; - char resp[256]; - char resp_hash[256]; - char uri[256]; - char opaque[256] = ""; - char cnonce[80]; - const char *username; - const char *secret; - const char *md5secret; - struct sip_auth *auth = NULL; /* Realm authentication */ - - if (!ast_strlen_zero(p->domain)) - ast_copy_string(uri, p->domain, sizeof(uri)); - else if (!ast_strlen_zero(p->uri)) - ast_copy_string(uri, p->uri, sizeof(uri)); - else - snprintf(uri, sizeof(uri), "sip:%s@%s",p->username, ast_inet_ntoa(p->sa.sin_addr)); - - snprintf(cnonce, sizeof(cnonce), "%08lx", ast_random()); - - /* Check if we have separate auth credentials */ - if(!(auth = find_realm_authentication(p->peerauth, p->realm))) /* Start with peer list */ - auth = find_realm_authentication(authl, p->realm); /* If not, global list */ - - if (auth) { - ast_log(LOG_DEBUG, "use realm [%s] from peer [%s][%s]\n", auth->username, p->peername, p->username); - username = auth->username; - secret = auth->secret; - md5secret = auth->md5secret; - if (sipdebug) - ast_log(LOG_DEBUG,"Using realm %s authentication for call %s\n", p->realm, p->callid); - } else { - /* No authentication, use peer or register= config */ - username = p->authname; - secret = p->peersecret; - md5secret = p->peermd5secret; - } - if (ast_strlen_zero(username)) /* We have no authentication */ - return -1; - - /* Calculate SIP digest response */ - snprintf(a1,sizeof(a1),"%s:%s:%s", username, p->realm, secret); - snprintf(a2,sizeof(a2),"%s:%s", sip_methods[method].text, uri); - if (!ast_strlen_zero(md5secret)) - ast_copy_string(a1_hash, md5secret, sizeof(a1_hash)); - else - ast_md5_hash(a1_hash,a1); - ast_md5_hash(a2_hash,a2); - - p->noncecount++; - if (!ast_strlen_zero(p->qop)) - snprintf(resp,sizeof(resp),"%s:%s:%08x:%s:%s:%s", a1_hash, p->nonce, p->noncecount, cnonce, "auth", a2_hash); - else - snprintf(resp,sizeof(resp),"%s:%s:%s", a1_hash, p->nonce, a2_hash); - ast_md5_hash(resp_hash, resp); - - /* only include the opaque string if it's set */ - if (!ast_strlen_zero(p->opaque)) { - snprintf(opaque, sizeof(opaque), ", opaque=\"%s\"", p->opaque); - } - - /* XXX We hard code our qop to "auth" for now. XXX */ - if (!ast_strlen_zero(p->qop)) - snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\"%s, qop=auth, cnonce=\"%s\", nc=%08x", username, p->realm, uri, p->nonce, resp_hash, opaque, cnonce, p->noncecount); - else - snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\"%s", username, p->realm, uri, p->nonce, resp_hash, opaque); - - append_history(p, "AuthResp", "Auth response sent for %s in realm %s - nc %d", username, p->realm, p->noncecount); - - return 0; -} - -static char show_domains_usage[] = -"Usage: sip show domains\n" -" Lists all configured SIP local domains.\n" -" Asterisk only responds to SIP messages to local domains.\n"; - -static char notify_usage[] = -"Usage: sip notify <type> <peer> [<peer>...]\n" -" Send a NOTIFY message to a SIP peer or peers\n" -" Message types are defined in sip_notify.conf\n"; - -static char show_users_usage[] = -"Usage: sip show users [like <pattern>]\n" -" Lists all known SIP users.\n" -" Optional regular expression pattern is used to filter the user list.\n"; - -static char show_user_usage[] = -"Usage: sip show user <name> [load]\n" -" Shows all details on one SIP user and the current status.\n" -" Option \"load\" forces lookup of peer in realtime storage.\n"; - -static char show_inuse_usage[] = -"Usage: sip show inuse [all]\n" -" List all SIP users and peers usage counters and limits.\n" -" Add option \"all\" to show all devices, not only those with a limit.\n"; - -static char show_channels_usage[] = -"Usage: sip show channels\n" -" Lists all currently active SIP channels.\n"; - -static char show_channel_usage[] = -"Usage: sip show channel <channel>\n" -" Provides detailed status on a given SIP channel.\n"; - -static char show_history_usage[] = -"Usage: sip show history <channel>\n" -" Provides detailed dialog history on a given SIP channel.\n"; - -static char show_peers_usage[] = -"Usage: sip show peers [like <pattern>]\n" -" Lists all known SIP peers.\n" -" Optional regular expression pattern is used to filter the peer list.\n"; - -static char show_peer_usage[] = -"Usage: sip show peer <name> [load]\n" -" Shows all details on one SIP peer and the current status.\n" -" Option \"load\" forces lookup of peer in realtime storage.\n"; - -static char prune_realtime_usage[] = -"Usage: sip prune realtime [peer|user] [<name>|all|like <pattern>]\n" -" Prunes object(s) from the cache.\n" -" Optional regular expression pattern is used to filter the objects.\n"; - -static char show_reg_usage[] = -"Usage: sip show registry\n" -" Lists all registration requests and status.\n"; - -static char debug_usage[] = -"Usage: sip set debug\n" -" Enables dumping of SIP packets for debugging purposes\n\n" -" sip set debug ip <host[:PORT]>\n" -" Enables dumping of SIP packets to and from host.\n\n" -" sip set debug peer <peername>\n" -" Enables dumping of SIP packets to and from host.\n" -" Require peer to be registered.\n"; - -static char no_debug_usage[] = -"Usage: sip set debug off\n" -" Disables dumping of SIP packets for debugging purposes\n"; - -static char no_history_usage[] = -"Usage: sip history off\n" -" Disables recording of SIP dialog history for debugging purposes\n"; - -static char history_usage[] = -"Usage: sip history\n" -" Enables recording of SIP dialog history for debugging purposes.\n" -"Use 'sip show history' to view the history of a call number.\n"; - -static char sip_reload_usage[] = -"Usage: sip reload\n" -" Reloads SIP configuration from sip.conf\n"; - -static char show_subscriptions_usage[] = -"Usage: sip show subscriptions\n" -" Lists active SIP subscriptions for extension states\n"; - -static char show_objects_usage[] = -"Usage: sip show objects\n" -" Lists status of known SIP objects\n"; - -static char show_settings_usage[] = -"Usage: sip show settings\n" -" Provides detailed list of the configuration of the SIP channel.\n"; - -/*! \brief Read SIP header (dialplan function) */ -static int func_header_read(struct ast_channel *chan, char *function, char *data, char *buf, size_t len) -{ - struct sip_pvt *p; - const char *content = NULL; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(header); - AST_APP_ARG(number); - ); - int i, number, start = 0; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "This function requires a header name.\n"); - return -1; - } - - ast_channel_lock(chan); - if (chan->tech != &sip_tech && chan->tech != &sip_tech_info) { - ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n"); - ast_channel_unlock(chan); - return -1; - } - - AST_STANDARD_APP_ARGS(args, data); - if (!args.number) { - number = 1; - } else { - sscanf(args.number, "%d", &number); - if (number < 1) - number = 1; - } - - p = chan->tech_pvt; - - /* If there is no private structure, this channel is no longer alive */ - if (!p) { - ast_channel_unlock(chan); - return -1; - } - - for (i = 0; i < number; i++) - content = __get_header(&p->initreq, args.header, &start); - - if (ast_strlen_zero(content)) { - ast_channel_unlock(chan); - return -1; - } - - ast_copy_string(buf, content, len); - ast_channel_unlock(chan); - - return 0; -} - -static struct ast_custom_function sip_header_function = { - .name = "SIP_HEADER", - .synopsis = "Gets the specified SIP header", - .syntax = "SIP_HEADER(<name>[,<number>])", - .desc = "Since there are several headers (such as Via) which can occur multiple\n" - "times, SIP_HEADER takes an optional second argument to specify which header with\n" - "that name to retrieve. Headers start at offset 1.\n", - .read = func_header_read, -}; - -/*! \brief Dial plan function to check if domain is local */ -static int func_check_sipdomain(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) -{ - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "CHECKSIPDOMAIN requires an argument - A domain name\n"); - return -1; - } - if (check_sip_domain(data, NULL, 0)) - ast_copy_string(buf, data, len); - else - buf[0] = '\0'; - return 0; -} - -static struct ast_custom_function checksipdomain_function = { - .name = "CHECKSIPDOMAIN", - .synopsis = "Checks if domain is a local domain", - .syntax = "CHECKSIPDOMAIN(<domain|IP>)", - .read = func_check_sipdomain, - .desc = "This function checks if the domain in the argument is configured\n" - "as a local SIP domain that this Asterisk server is configured to handle.\n" - "Returns the domain name if it is locally handled, otherwise an empty string.\n" - "Check the domain= configuration in sip.conf\n", -}; - -/*! \brief ${SIPPEER()} Dialplan function - reads peer data */ -static int function_sippeer(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) -{ - struct sip_peer *peer; - char *colname; - - if ((colname = strchr(data, ':'))) /*! \todo Will be deprecated after 1.4 */ - *colname++ = '\0'; - else if ((colname = strchr(data, '|'))) - *colname++ = '\0'; - else - colname = "ip"; - - if (!(peer = find_peer(data, NULL, 1, 0))) - return -1; - - if (!strcasecmp(colname, "ip")) { - ast_copy_string(buf, peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "", len); - } else if (!strcasecmp(colname, "status")) { - peer_status(peer, buf, len); - } else if (!strcasecmp(colname, "language")) { - ast_copy_string(buf, peer->language, len); - } else if (!strcasecmp(colname, "regexten")) { - ast_copy_string(buf, peer->regexten, len); - } else if (!strcasecmp(colname, "limit")) { - snprintf(buf, len, "%d", peer->call_limit); - } else if (!strcasecmp(colname, "curcalls")) { - snprintf(buf, len, "%d", peer->inUse); - } else if (!strcasecmp(colname, "accountcode")) { - ast_copy_string(buf, peer->accountcode, len); - } else if (!strcasecmp(colname, "useragent")) { - ast_copy_string(buf, peer->useragent, len); - } else if (!strcasecmp(colname, "mailbox")) { - ast_copy_string(buf, peer->mailbox, len); - } else if (!strcasecmp(colname, "context")) { - ast_copy_string(buf, peer->context, len); - } else if (!strcasecmp(colname, "expire")) { - snprintf(buf, len, "%d", peer->expire); - } else if (!strcasecmp(colname, "dynamic")) { - ast_copy_string(buf, (ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC) ? "yes" : "no"), len); - } else if (!strcasecmp(colname, "callerid_name")) { - ast_copy_string(buf, peer->cid_name, len); - } else if (!strcasecmp(colname, "callerid_num")) { - ast_copy_string(buf, peer->cid_num, len); - } else if (!strcasecmp(colname, "codecs")) { - ast_getformatname_multiple(buf, len -1, peer->capability); - } else if (!strncasecmp(colname, "codec[", 6)) { - char *codecnum; - int index = 0, codec = 0; - - codecnum = colname + 6; /* move past the '[' */ - codecnum = strsep(&codecnum, "]"); /* trim trailing ']' if any */ - index = atoi(codecnum); - if((codec = ast_codec_pref_index(&peer->prefs, index))) { - ast_copy_string(buf, ast_getformatname(codec), len); - } else { - buf[0] = '\0'; - } - } else { - buf[0] = '\0'; - } - - ASTOBJ_UNREF(peer, sip_destroy_peer); - - return 0; -} - -/*! \brief Structure to declare a dialplan function: SIPPEER */ -struct ast_custom_function sippeer_function = { - .name = "SIPPEER", - .synopsis = "Gets SIP peer information", - .syntax = "SIPPEER(<peername>[|item])", - .read = function_sippeer, - .desc = "Valid items are:\n" - "- ip (default) The IP address.\n" - "- mailbox The configured mailbox.\n" - "- context The configured context.\n" - "- expire The epoch time of the next expire.\n" - "- dynamic Is it dynamic? (yes/no).\n" - "- callerid_name The configured Caller ID name.\n" - "- callerid_num The configured Caller ID number.\n" - "- codecs The configured codecs.\n" - "- status Status (if qualify=yes).\n" - "- regexten Registration extension\n" - "- limit Call limit (call-limit)\n" - "- curcalls Current amount of calls \n" - " Only available if call-limit is set\n" - "- language Default language for peer\n" - "- accountcode Account code for this peer\n" - "- useragent Current user agent id for peer\n" - "- codec[x] Preferred codec index number 'x' (beginning with zero).\n" - "\n" -}; - -/*! \brief ${SIPCHANINFO()} Dialplan function - reads sip channel data */ -static int function_sipchaninfo_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) -{ - struct sip_pvt *p; - - *buf = 0; - - if (!data) { - ast_log(LOG_WARNING, "This function requires a parameter name.\n"); - return -1; - } - - ast_channel_lock(chan); - if (chan->tech != &sip_tech && chan->tech != &sip_tech_info) { - ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n"); - ast_channel_unlock(chan); - return -1; - } - - p = chan->tech_pvt; - - /* If there is no private structure, this channel is no longer alive */ - if (!p) { - ast_channel_unlock(chan); - return -1; - } - - if (!strcasecmp(data, "peerip")) { - ast_copy_string(buf, p->sa.sin_addr.s_addr ? ast_inet_ntoa(p->sa.sin_addr) : "", len); - } else if (!strcasecmp(data, "recvip")) { - ast_copy_string(buf, p->recv.sin_addr.s_addr ? ast_inet_ntoa(p->recv.sin_addr) : "", len); - } else if (!strcasecmp(data, "from")) { - ast_copy_string(buf, p->from, len); - } else if (!strcasecmp(data, "uri")) { - ast_copy_string(buf, p->uri, len); - } else if (!strcasecmp(data, "useragent")) { - ast_copy_string(buf, p->useragent, len); - } else if (!strcasecmp(data, "peername")) { - ast_copy_string(buf, p->peername, len); - } else if (!strcasecmp(data, "t38passthrough")) { - if (p->t38.state == T38_DISABLED) - ast_copy_string(buf, "0", sizeof("0")); - else /* T38 is offered or enabled in this call */ - ast_copy_string(buf, "1", sizeof("1")); - } else { - ast_channel_unlock(chan); - return -1; - } - ast_channel_unlock(chan); - - return 0; -} - -/*! \brief Structure to declare a dialplan function: SIPCHANINFO */ -static struct ast_custom_function sipchaninfo_function = { - .name = "SIPCHANINFO", - .synopsis = "Gets the specified SIP parameter from the current channel", - .syntax = "SIPCHANINFO(item)", - .read = function_sipchaninfo_read, - .desc = "Valid items are:\n" - "- peerip The IP address of the peer.\n" - "- recvip The source IP address of the peer.\n" - "- from The URI from the From: header.\n" - "- uri The URI from the Contact: header.\n" - "- useragent The useragent.\n" - "- peername The name of the peer.\n" - "- t38passthrough 1 if T38 is offered or enabled in this channel, otherwise 0\n" -}; - -/*! \brief Parse 302 Moved temporalily response */ -static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) -{ - char tmp[SIPBUFSIZE]; - char *s, *e, *uri, *t; - char *domain; - - ast_copy_string(tmp, get_header(req, "Contact"), sizeof(tmp)); - if ((t = strchr(tmp, ','))) - *t = '\0'; - s = get_in_brackets(tmp); - uri = ast_strdupa(s); - if (ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) { - if (!strncasecmp(s, "sip:", 4)) - s += 4; - e = strchr(s, ';'); - if (e) - *e = '\0'; - if (option_debug) - ast_log(LOG_DEBUG, "Found promiscuous redirection to 'SIP/%s'\n", s); - if (p->owner) - ast_string_field_build(p->owner, call_forward, "SIP/%s", s); - } else { - e = strchr(tmp, '@'); - if (e) { - *e++ = '\0'; - domain = e; - } else { - /* No username part */ - domain = tmp; - } - e = strchr(s, ';'); /* Strip of parameters in the username part */ - if (e) - *e = '\0'; - e = strchr(domain, ';'); /* Strip of parameters in the domain part */ - if (e) - *e = '\0'; - - if (!strncasecmp(s, "sip:", 4)) - s += 4; - if (option_debug > 1) - ast_log(LOG_DEBUG, "Received 302 Redirect to extension '%s' (domain %s)\n", s, domain); - if (p->owner) { - pbx_builtin_setvar_helper(p->owner, "SIPREDIRECTURI", uri); - pbx_builtin_setvar_helper(p->owner, "SIPDOMAIN", domain); - ast_string_field_set(p->owner, call_forward, s); - } - } -} - -/*! \brief Check pending actions on SIP call */ -static void check_pendings(struct sip_pvt *p) -{ - if (ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { - /* if we can't BYE, then this is really a pending CANCEL */ - if (p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA) - transmit_request(p, SIP_CANCEL, p->lastinvite, XMIT_RELIABLE, FALSE); - /* Actually don't destroy us yet, wait for the 487 on our original - INVITE, but do set an autodestruct just in case we never get it. */ - else { - /* We have a pending outbound invite, don't send someting - new in-transaction */ - if (p->pendinginvite) - return; - - /* Perhaps there is an SD change INVITE outstanding */ - transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, TRUE); - } - ast_clear_flag(&p->flags[0], SIP_PENDINGBYE); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } else if (ast_test_flag(&p->flags[0], SIP_NEEDREINVITE)) { - /* if we can't REINVITE, hold it for later */ - if (p->pendinginvite || p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA || p->waitid > 0) { - if (option_debug) - ast_log(LOG_DEBUG, "NOT Sending pending reinvite (yet) on '%s'\n", p->callid); - } else { - if (option_debug) - ast_log(LOG_DEBUG, "Sending pending reinvite on '%s'\n", p->callid); - /* Didn't get to reinvite yet, so do it now */ - transmit_reinvite_with_sdp(p); - ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE); - } - } -} - -/*! \brief Reset the NEEDREINVITE flag after waiting when we get 491 on a Re-invite - to avoid race conditions between asterisk servers. - Called from the scheduler. -*/ -static int sip_reinvite_retry(const void *data) -{ - struct sip_pvt *p = (struct sip_pvt *) data; - - ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); - p->waitid = -1; - return 0; -} - - -/*! \brief Handle SIP response to INVITE dialogue */ -static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno) -{ - int outgoing = ast_test_flag(&p->flags[0], SIP_OUTGOING); - int res = 0; - int xmitres = 0; - int reinvite = (p->owner && p->owner->_state == AST_STATE_UP); - struct ast_channel *bridgepeer = NULL; - - if (option_debug > 3) { - if (reinvite) - ast_log(LOG_DEBUG, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid); - else - ast_log(LOG_DEBUG, "SIP response %d to standard invite\n", resp); - } - - if (ast_test_flag(&p->flags[0], SIP_ALREADYGONE)) { /* This call is already gone */ - if (option_debug) - ast_log(LOG_DEBUG, "Got response on call that is already terminated: %s (ignoring)\n", p->callid); - return; - } - - /* Acknowledge sequence number - This only happens on INVITE from SIP-call */ - /* Don't auto congest anymore since we've gotten something useful back */ - AST_SCHED_DEL(sched, p->initid); - - /* RFC3261 says we must treat every 1xx response (but not 100) - that we don't recognize as if it was 183. - */ - if (resp > 100 && resp < 200 && resp!=101 && resp != 180 && resp != 182 && resp != 183) - resp = 183; - - /* Any response between 100 and 199 is PROCEEDING */ - if (resp >= 100 && resp < 200 && p->invitestate == INV_CALLING) - p->invitestate = INV_PROCEEDING; - - /* Final response, not 200 ? */ - if (resp >= 300 && (p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA )) - p->invitestate = INV_COMPLETED; - - - switch (resp) { - case 100: /* Trying */ - case 101: /* Dialog establishment */ - if (!ast_test_flag(req, SIP_PKT_IGNORE) && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - check_pendings(p); - break; - - case 180: /* 180 Ringing */ - case 182: /* 182 Queued */ - if (!ast_test_flag(req, SIP_PKT_IGNORE) && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) { - ast_queue_control(p->owner, AST_CONTROL_RINGING); - if (p->owner->_state != AST_STATE_UP) { - ast_setstate(p->owner, AST_STATE_RINGING); - } - } - if (find_sdp(req)) { - if (p->invitestate != INV_CANCELLED) - p->invitestate = INV_EARLY_MEDIA; - res = process_sdp(p, req); - if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) { - /* Queue a progress frame only if we have SDP in 180 or 182 */ - ast_queue_control(p->owner, AST_CONTROL_PROGRESS); - } - } - check_pendings(p); - break; - - case 183: /* Session progress */ - if (!ast_test_flag(req, SIP_PKT_IGNORE) && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - /* Ignore 183 Session progress without SDP */ - if (find_sdp(req)) { - if (p->invitestate != INV_CANCELLED) - p->invitestate = INV_EARLY_MEDIA; - res = process_sdp(p, req); - if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) { - /* Queue a progress frame */ - ast_queue_control(p->owner, AST_CONTROL_PROGRESS); - } - } - check_pendings(p); - break; - - case 200: /* 200 OK on invite - someone's answering our call */ - if (!ast_test_flag(req, SIP_PKT_IGNORE) && (p->invitestate != INV_CANCELLED) && sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - p->authtries = 0; - if (find_sdp(req)) { - if ((res = process_sdp(p, req)) && !ast_test_flag(req, SIP_PKT_IGNORE)) - if (!reinvite) - /* This 200 OK's SDP is not acceptable, so we need to ack, then hangup */ - /* For re-invites, we try to recover */ - ast_set_flag(&p->flags[0], SIP_PENDINGBYE); - } - - /* Parse contact header for continued conversation */ - /* When we get 200 OK, we know which device (and IP) to contact for this call */ - /* This is important when we have a SIP proxy between us and the phone */ - if (outgoing) { - update_call_counter(p, DEC_CALL_RINGING); - parse_ok_contact(p, req); - /* Save Record-Route for any later requests we make on this dialogue */ - if (!reinvite) - build_route(p, req, 1); - - if(set_address_from_contact(p)) { - /* Bad contact - we don't know how to reach this device */ - /* We need to ACK, but then send a bye */ - if (!p->route && !ast_test_flag(req, SIP_PKT_IGNORE)) - ast_set_flag(&p->flags[0], SIP_PENDINGBYE); - } - - } - - if (p->owner && (p->owner->_state == AST_STATE_UP) && (bridgepeer = ast_bridged_channel(p->owner))) { /* if this is a re-invite */ - struct sip_pvt *bridgepvt = NULL; - - if (!bridgepeer->tech) { - ast_log(LOG_WARNING, "Ooooh.. no tech! That's REALLY bad\n"); - break; - } - if (bridgepeer->tech == &sip_tech || bridgepeer->tech == &sip_tech_info) { - bridgepvt = (struct sip_pvt*)(bridgepeer->tech_pvt); - if (bridgepvt->udptl) { - if (p->t38.state == T38_PEER_REINVITE) { - sip_handle_t38_reinvite(bridgepeer, p, 0); - ast_rtp_set_rtptimers_onhold(p->rtp); - if (p->vrtp) - ast_rtp_set_rtptimers_onhold(p->vrtp); /* Turn off RTP timers while we send fax */ - } else if (p->t38.state == T38_DISABLED && bridgepeer && (bridgepvt->t38.state == T38_ENABLED)) { - ast_log(LOG_WARNING, "RTP re-invite after T38 session not handled yet !\n"); - /* Insted of this we should somehow re-invite the other side of the bridge to RTP */ - /* XXXX Should we really destroy this session here, without any response at all??? */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - } else { - if (option_debug > 1) - ast_log(LOG_DEBUG, "Strange... The other side of the bridge does not have a udptl struct\n"); - ast_mutex_lock(&bridgepvt->lock); - bridgepvt->t38.state = T38_DISABLED; - ast_mutex_unlock(&bridgepvt->lock); - if (option_debug) - ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n", bridgepvt->t38.state, bridgepeer->tech->type); - p->t38.state = T38_DISABLED; - if (option_debug > 1) - ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); - } - } else { - /* Other side is not a SIP channel */ - if (option_debug > 1) - ast_log(LOG_DEBUG, "Strange... The other side of the bridge is not a SIP channel\n"); - p->t38.state = T38_DISABLED; - if (option_debug > 1) - ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); - } - } - if ((p->t38.state == T38_LOCAL_REINVITE) || (p->t38.state == T38_LOCAL_DIRECT)) { - /* If there was T38 reinvite and we are supposed to answer with 200 OK than this should set us to T38 negotiated mode */ - p->t38.state = T38_ENABLED; - if (option_debug) - ast_log(LOG_DEBUG, "T38 changed state to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); - } - - if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) { - if (!reinvite) { - ast_queue_control(p->owner, AST_CONTROL_ANSWER); - } else { /* RE-invite */ - ast_queue_frame(p->owner, &ast_null_frame); - } - } else { - /* It's possible we're getting an 200 OK after we've tried to disconnect - by sending CANCEL */ - /* First send ACK, then send bye */ - if (!ast_test_flag(req, SIP_PKT_IGNORE)) - ast_set_flag(&p->flags[0], SIP_PENDINGBYE); - } - /* If I understand this right, the branch is different for a non-200 ACK only */ - p->invitestate = INV_TERMINATED; - ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE); - check_pendings(p); - break; - case 407: /* Proxy authentication */ - case 401: /* Www auth */ - /* First we ACK */ - xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->options) - p->options->auth_type = (resp == 401 ? WWW_AUTH : PROXY_AUTH); - - /* Then we AUTH */ - ast_string_field_free(p, theirtag); /* forget their old tag, so we don't match tags when getting response */ - if (!ast_test_flag(req, SIP_PKT_IGNORE)) { - char *authenticate = (resp == 401 ? "WWW-Authenticate" : "Proxy-Authenticate"); - char *authorization = (resp == 401 ? "Authorization" : "Proxy-Authorization"); - if (p->authtries < MAX_AUTHTRIES) - p->invitestate = INV_CALLING; - if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, authenticate, authorization, SIP_INVITE, 1)) { - ast_log(LOG_NOTICE, "Failed to authenticate on INVITE to '%s'\n", get_header(&p->initreq, "From")); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - sip_alreadygone(p); - if (p->owner) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - } - } - break; - - case 403: /* Forbidden */ - /* First we ACK */ - xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - ast_log(LOG_WARNING, "Received response: \"Forbidden\" from '%s'\n", get_header(&p->initreq, "From")); - if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - sip_alreadygone(p); - break; - - case 404: /* Not found */ - xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE)) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - sip_alreadygone(p); - break; - - case 408: /* Request timeout */ - case 481: /* Call leg does not exist */ - /* Could be REFER caused INVITE with replaces */ - ast_log(LOG_WARNING, "Re-invite to non-existing call leg on other UA. SIP dialog '%s'. Giving up.\n", p->callid); - xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->owner) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - break; - case 487: /* Cancelled transaction */ - /* We have sent CANCEL on an outbound INVITE - This transaction is already scheduled to be killed by sip_hangup(). - */ - xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE)) { - ast_queue_hangup(p->owner); - append_history(p, "Hangup", "Got 487 on CANCEL request from us. Queued AST hangup request"); - } else if (!ast_test_flag(req, SIP_PKT_IGNORE)) { - update_call_counter(p, DEC_CALL_LIMIT); - append_history(p, "Hangup", "Got 487 on CANCEL request from us on call without owner. Killing this dialog."); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - sip_alreadygone(p); - } - break; - case 488: /* Not acceptable here */ - xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (reinvite && p->udptl) { - /* If this is a T.38 call, we should go back to - audio. If this is an audio call - something went - terribly wrong since we don't renegotiate codecs, - only IP/port . - */ - p->t38.state = T38_DISABLED; - /* Try to reset RTP timers */ - ast_rtp_set_rtptimers_onhold(p->rtp); - ast_log(LOG_ERROR, "Got error on T.38 re-invite. Bad configuration. Peer needs to have T.38 disabled.\n"); - - /*! \bug Is there any way we can go back to the audio call on both - sides here? - */ - /* While figuring that out, hangup the call */ - if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE)) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } else if (p->udptl && p->t38.state == T38_LOCAL_DIRECT) { - /* We tried to send T.38 out in an initial INVITE and the remote side rejected it, - right now we can't fall back to audio so totally abort. - */ - p->t38.state = T38_DISABLED; - /* Try to reset RTP timers */ - ast_rtp_set_rtptimers_onhold(p->rtp); - ast_log(LOG_ERROR, "Got error on T.38 initial invite. Bailing out.\n"); - - /* The dialog is now terminated */ - if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE)) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - sip_alreadygone(p); - } else { - /* We can't set up this call, so give up */ - if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE)) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - /* If there's no dialog to end, then mark p as already gone */ - if (!reinvite) - sip_alreadygone(p); - } - break; - case 491: /* Pending */ - /* we really should have to wait a while, then retransmit - * We should support the retry-after at some point - * At this point, we treat this as a congestion if the call is not in UP state - */ - xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE)) { - if (p->owner->_state != AST_STATE_UP) { - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } else { - /* This is a re-invite that failed. - * Reset the flag after a while - */ - int wait = 3 + ast_random() % 5; - p->waitid = ast_sched_add(sched, wait, sip_reinvite_retry, p); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Reinvite race. Waiting %d secs before retry\n", wait); - } - } - break; - - case 501: /* Not implemented */ - xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (p->owner) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - break; - } - if (xmitres == XMIT_ERROR) - ast_log(LOG_WARNING, "Could not transmit message in dialog %s\n", p->callid); -} - -/* \brief Handle SIP response in REFER transaction - We've sent a REFER, now handle responses to it - */ -static void handle_response_refer(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno) -{ - char *auth = "Proxy-Authenticate"; - char *auth2 = "Proxy-Authorization"; - - /* If no refer structure exists, then do nothing */ - if (!p->refer) - return; - - switch (resp) { - case 202: /* Transfer accepted */ - /* We need to do something here */ - /* The transferee is now sending INVITE to target */ - p->refer->status = REFER_ACCEPTED; - /* Now wait for next message */ - if (option_debug > 2) - ast_log(LOG_DEBUG, "Got 202 accepted on transfer\n"); - /* We should hang along, waiting for NOTIFY's here */ - break; - - case 401: /* Not www-authorized on SIP method */ - case 407: /* Proxy auth */ - if (ast_strlen_zero(p->authname)) { - ast_log(LOG_WARNING, "Asked to authenticate REFER to %s:%d but we have no matching peer or realm auth!\n", - ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port)); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - if (resp == 401) { - auth = "WWW-Authenticate"; - auth2 = "Authorization"; - } - if ((p->authtries > 1) || do_proxy_auth(p, req, auth, auth2, SIP_REFER, 0)) { - ast_log(LOG_NOTICE, "Failed to authenticate on REFER to '%s'\n", get_header(&p->initreq, "From")); - p->refer->status = REFER_NOAUTH; - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - break; - case 481: /* Call leg does not exist */ - - /* A transfer with Replaces did not work */ - /* OEJ: We should Set flag, cancel the REFER, go back - to original call - but right now we can't */ - ast_log(LOG_WARNING, "Remote host can't match REFER request to call '%s'. Giving up.\n", p->callid); - if (p->owner) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - break; - - case 500: /* Server error */ - case 501: /* Method not implemented */ - /* Return to the current call onhold */ - /* Status flag needed to be reset */ - ast_log(LOG_NOTICE, "SIP transfer to %s failed, call miserably fails. \n", p->refer->refer_to); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - p->refer->status = REFER_FAILED; - break; - case 603: /* Transfer declined */ - ast_log(LOG_NOTICE, "SIP transfer to %s declined, call miserably fails. \n", p->refer->refer_to); - p->refer->status = REFER_FAILED; - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - break; - } -} - -/*! \brief Handle responses on REGISTER to services */ -static int handle_response_register(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int ignore, int seqno) -{ - int expires, expires_ms; - struct sip_registry *r; - r=p->registry; - - switch (resp) { - case 401: /* Unauthorized */ - if ((p->authtries == MAX_AUTHTRIES) || do_register_auth(p, req, "WWW-Authenticate", "Authorization")) { - ast_log(LOG_NOTICE, "Failed to authenticate on REGISTER to '%s@%s' (Tries %d)\n", p->registry->username, p->registry->hostname, p->authtries); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - break; - case 403: /* Forbidden */ - ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for REGISTER for '%s' to '%s'\n", p->registry->username, p->registry->hostname); - if (global_regattempts_max) - p->registry->regattempts = global_regattempts_max+1; - AST_SCHED_DEL(sched, r->timeout); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - break; - case 404: /* Not found */ - ast_log(LOG_WARNING, "Got 404 Not found on SIP register to service %s@%s, giving up\n", p->registry->username,p->registry->hostname); - if (global_regattempts_max) - p->registry->regattempts = global_regattempts_max+1; - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - r->call = NULL; - AST_SCHED_DEL(sched, r->timeout); - break; - case 407: /* Proxy auth */ - if ((p->authtries == MAX_AUTHTRIES) || do_register_auth(p, req, "Proxy-Authenticate", "Proxy-Authorization")) { - ast_log(LOG_NOTICE, "Failed to authenticate on REGISTER to '%s' (tries '%d')\n", get_header(&p->initreq, "From"), p->authtries); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - break; - case 408: /* Request timeout */ - /* Got a timeout response, so reset the counter of failed responses */ - r->regattempts = 0; - break; - case 479: /* SER: Not able to process the URI - address is wrong in register*/ - ast_log(LOG_WARNING, "Got error 479 on register to %s@%s, giving up (check config)\n", p->registry->username,p->registry->hostname); - if (global_regattempts_max) - p->registry->regattempts = global_regattempts_max+1; - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - r->call = NULL; - AST_SCHED_DEL(sched, r->timeout); - break; - case 200: /* 200 OK */ - if (!r) { - ast_log(LOG_WARNING, "Got 200 OK on REGISTER, but there isn't a registry entry for '%s' (we probably already got the OK)\n", S_OR(p->peername, p->username)); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - return 0; - } - - r->regstate = REG_STATE_REGISTERED; - r->regtime = time(NULL); /* Reset time of last succesful registration */ - manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelDriver: SIP\r\nDomain: %s\r\nStatus: %s\r\n", r->hostname, regstate2str(r->regstate)); - r->regattempts = 0; - if (option_debug) - ast_log(LOG_DEBUG, "Registration successful\n"); - if (r->timeout > -1) { - if (option_debug) - ast_log(LOG_DEBUG, "Cancelling timeout %d\n", r->timeout); - } - AST_SCHED_DEL(sched, r->timeout); - r->call = NULL; - p->registry = NULL; - /* Let this one hang around until we have all the responses */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - /* ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); */ - - /* set us up for re-registering */ - /* figure out how long we got registered for */ - AST_SCHED_DEL(sched, r->expire); - /* according to section 6.13 of RFC, contact headers override - expires headers, so check those first */ - expires = 0; - - /* XXX todo: try to save the extra call */ - if (!ast_strlen_zero(get_header(req, "Contact"))) { - const char *contact = NULL; - const char *tmptmp = NULL; - int start = 0; - for(;;) { - contact = __get_header(req, "Contact", &start); - /* this loop ensures we get a contact header about our register request */ - if(!ast_strlen_zero(contact)) { - if( (tmptmp=strstr(contact, p->our_contact))) { - contact=tmptmp; - break; - } - } else - break; - } - tmptmp = strcasestr(contact, "expires="); - if (tmptmp) { - if (sscanf(tmptmp + 8, "%d;", &expires) != 1) - expires = 0; - } - - } - if (!expires) - expires=atoi(get_header(req, "expires")); - if (!expires) - expires=default_expiry; - - expires_ms = expires * 1000; - if (expires <= EXPIRY_GUARD_LIMIT) - expires_ms -= MAX((expires_ms * EXPIRY_GUARD_PCT),EXPIRY_GUARD_MIN); - else - expires_ms -= EXPIRY_GUARD_SECS * 1000; - if (sipdebug) - ast_log(LOG_NOTICE, "Outbound Registration: Expiry for %s is %d sec (Scheduling reregistration in %d s)\n", r->hostname, expires, expires_ms/1000); - - r->refresh= (int) expires_ms / 1000; - - /* Schedule re-registration before we expire */ - AST_SCHED_DEL(sched, r->expire); - r->expire = ast_sched_add(sched, expires_ms, sip_reregister, r); - ASTOBJ_UNREF(r, sip_registry_destroy); - } - return 1; -} - -/*! \brief Handle qualification responses (OPTIONS) */ -static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_request *req) -{ - struct sip_peer *peer = p->relatedpeer; - int statechanged, is_reachable, was_reachable; - int pingtime = ast_tvdiff_ms(ast_tvnow(), peer->ps); - - /* - * Compute the response time to a ping (goes in peer->lastms.) - * -1 means did not respond, 0 means unknown, - * 1..maxms is a valid response, >maxms means late response. - */ - if (pingtime < 1) /* zero = unknown, so round up to 1 */ - pingtime = 1; - - /* Now determine new state and whether it has changed. - * Use some helper variables to simplify the writing - * of the expressions. - */ - was_reachable = peer->lastms > 0 && peer->lastms <= peer->maxms; - is_reachable = pingtime <= peer->maxms; - statechanged = peer->lastms == 0 /* yes, unknown before */ - || was_reachable != is_reachable; - - peer->lastms = pingtime; - peer->call = NULL; - if (statechanged) { - const char *s = is_reachable ? "Reachable" : "Lagged"; - - ast_log(LOG_NOTICE, "Peer '%s' is now %s. (%dms / %dms)\n", - peer->name, s, pingtime, peer->maxms); - ast_device_state_changed("SIP/%s", peer->name); - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", - "Peer: SIP/%s\r\nPeerStatus: %s\r\nTime: %d\r\n", - peer->name, s, pingtime); - } - - if (!AST_SCHED_DEL(sched, peer->pokeexpire)) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - - /* Try again eventually */ - peer->pokeexpire = ast_sched_add(sched, - is_reachable ? DEFAULT_FREQ_OK : DEFAULT_FREQ_NOTOK, - sip_poke_peer_s, ASTOBJ_REF(peer)); - - if (peer->pokeexpire == -1) { - ASTOBJ_UNREF(peer, sip_destroy_peer); - } -} - -/*! \brief Immediately stop RTP, VRTP and UDPTL as applicable */ -static void stop_media_flows(struct sip_pvt *p) -{ - /* Immediately stop RTP, VRTP and UDPTL as applicable */ - if (p->rtp) - ast_rtp_stop(p->rtp); - if (p->vrtp) - ast_rtp_stop(p->vrtp); - if (p->udptl) - ast_udptl_stop(p->udptl); -} - -/*! \brief Handle SIP response in dialogue */ -/* XXX only called by handle_request */ -static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int ignore, int seqno) -{ - struct ast_channel *owner; - int sipmethod; - int res = 1; - const char *c = get_header(req, "Cseq"); - /* GCC 4.2 complains if I try to cast c as a char * when passing it to ast_skip_nonblanks, so make a copy of it */ - char *c_copy = ast_strdupa(c); - /* Skip the Cseq and its subsequent spaces */ - const char *msg = ast_skip_blanks(ast_skip_nonblanks(c_copy)); - - if (!msg) - msg = ""; - - sipmethod = find_sip_method(msg); - - owner = p->owner; - if (owner) - owner->hangupcause = hangup_sip2cause(resp); - - /* Acknowledge whatever it is destined for */ - if ((resp >= 100) && (resp <= 199)) - __sip_semi_ack(p, seqno, 0, sipmethod); - else - __sip_ack(p, seqno, 0, sipmethod); - - /* If this is a NOTIFY for a subscription clear the flag that indicates that we have a NOTIFY pending */ - if (!p->owner && sipmethod == SIP_NOTIFY && p->pendinginvite) - p->pendinginvite = 0; - - /* Get their tag if we haven't already */ - if (ast_strlen_zero(p->theirtag) || (resp >= 200)) { - char tag[128]; - - gettag(req, "To", tag, sizeof(tag)); - ast_string_field_set(p, theirtag, tag); - } - - /* RFC 3261 Section 15 specifies that if we receive a 408 or 481 - * in response to a BYE, then we should end the current dialog - * and session. It is known that at least one phone manufacturer - * potentially will send a 404 in response to a BYE, so we'll be - * liberal in what we accept and end the dialog and session if we - * receive any of those responses to a BYE. - */ - if ((resp == 404 || resp == 408 || resp == 481) && sipmethod == SIP_BYE) { - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - return; - } - - if (p->relatedpeer && p->method == SIP_OPTIONS) { - /* We don't really care what the response is, just that it replied back. - Well, as long as it's not a 100 response... since we might - need to hang around for something more "definitive" */ - if (resp != 100) - handle_response_peerpoke(p, resp, req); - } else if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) { - switch(resp) { - case 100: /* 100 Trying */ - case 101: /* 101 Dialog establishment */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - break; - case 183: /* 183 Session Progress */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - break; - case 180: /* 180 Ringing */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - break; - case 182: /* 182 Queued */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - break; - case 200: /* 200 OK */ - p->authtries = 0; /* Reset authentication counter */ - if (sipmethod == SIP_MESSAGE || sipmethod == SIP_INFO) { - /* We successfully transmitted a message - or a video update request in INFO */ - /* Nothing happens here - the message is inside a dialog */ - } else if (sipmethod == SIP_INVITE) { - handle_response_invite(p, resp, rest, req, seqno); - } else if (sipmethod == SIP_NOTIFY) { - /* They got the notify, this is the end */ - if (p->owner) { - if (!p->refer) { - ast_log(LOG_WARNING, "Notify answer on an owned channel? - %s\n", p->owner->name); - ast_queue_hangup(p->owner); - } else if (option_debug > 3) - ast_log(LOG_DEBUG, "Got OK on REFER Notify message\n"); - } else { - if (p->subscribed == NONE) - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - if (ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE)) { - /* Ready to send the next state we have on queue */ - ast_clear_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE); - cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p); - } - } - } else if (sipmethod == SIP_REGISTER) - res = handle_response_register(p, resp, rest, req, ignore, seqno); - else if (sipmethod == SIP_BYE) { /* Ok, we're ready to go */ - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - } else if (sipmethod == SIP_SUBSCRIBE) - ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - break; - case 202: /* Transfer accepted */ - if (sipmethod == SIP_REFER) - handle_response_refer(p, resp, rest, req, seqno); - break; - case 401: /* Not www-authorized on SIP method */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - else if (sipmethod == SIP_REFER) - handle_response_refer(p, resp, rest, req, seqno); - else if (p->registry && sipmethod == SIP_REGISTER) - res = handle_response_register(p, resp, rest, req, ignore, seqno); - else if (sipmethod == SIP_BYE) { - if (ast_strlen_zero(p->authname)) { - ast_log(LOG_WARNING, "Asked to authenticate %s, to %s:%d but we have no matching peer!\n", - msg, ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port)); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } else if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, "WWW-Authenticate", "Authorization", sipmethod, 0)) { - ast_log(LOG_NOTICE, "Failed to authenticate on %s to '%s'\n", msg, get_header(&p->initreq, "From")); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - /* We fail to auth bye on our own call, but still needs to tear down the call. - Life, they call it. */ - } - } else { - ast_log(LOG_WARNING, "Got authentication request (401) on unknown %s to '%s'\n", sip_methods[sipmethod].text, get_header(req, "To")); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - break; - case 403: /* Forbidden - we failed authentication */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - else if (p->registry && sipmethod == SIP_REGISTER) - res = handle_response_register(p, resp, rest, req, ignore, seqno); - else { - ast_log(LOG_WARNING, "Forbidden - maybe wrong password on authentication for %s\n", msg); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - break; - case 404: /* Not found */ - if (p->registry && sipmethod == SIP_REGISTER) - res = handle_response_register(p, resp, rest, req, ignore, seqno); - else if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - else if (owner) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - break; - case 407: /* Proxy auth required */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - else if (sipmethod == SIP_REFER) - handle_response_refer(p, resp, rest, req, seqno); - else if (p->registry && sipmethod == SIP_REGISTER) - res = handle_response_register(p, resp, rest, req, ignore, seqno); - else if (sipmethod == SIP_BYE) { - if (ast_strlen_zero(p->authname)) { - ast_log(LOG_WARNING, "Asked to authenticate %s, to %s:%d but we have no matching peer!\n", - msg, ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port)); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } else if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, "Proxy-Authenticate", "Proxy-Authorization", sipmethod, 0)) { - ast_log(LOG_NOTICE, "Failed to authenticate on %s to '%s'\n", msg, get_header(&p->initreq, "From")); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - } else /* We can't handle this, giving up in a bad way */ - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - - break; - case 408: /* Request timeout - terminate dialog */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - else if (sipmethod == SIP_REGISTER) - res = handle_response_register(p, resp, rest, req, ignore, seqno); - else if (sipmethod == SIP_BYE) { - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - if (option_debug) - ast_log(LOG_DEBUG, "Got timeout on bye. Thanks for the answer. Now, kill this call\n"); - } else { - if (owner) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - break; - case 481: /* Call leg does not exist */ - if (sipmethod == SIP_INVITE) { - handle_response_invite(p, resp, rest, req, seqno); - } else if (sipmethod == SIP_REFER) { - handle_response_refer(p, resp, rest, req, seqno); - } else if (sipmethod == SIP_BYE) { - /* The other side has no transaction to bye, - just assume it's all right then */ - ast_log(LOG_WARNING, "Remote host can't match request %s to call '%s'. Giving up.\n", sip_methods[sipmethod].text, p->callid); - } else if (sipmethod == SIP_CANCEL) { - /* The other side has no transaction to cancel, - just assume it's all right then */ - ast_log(LOG_WARNING, "Remote host can't match request %s to call '%s'. Giving up.\n", sip_methods[sipmethod].text, p->callid); - } else { - ast_log(LOG_WARNING, "Remote host can't match request %s to call '%s'. Giving up.\n", sip_methods[sipmethod].text, p->callid); - /* Guessing that this is not an important request */ - } - break; - case 487: - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - break; - case 488: /* Not acceptable here - codec error */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - break; - case 491: /* Pending */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - else { - if (option_debug) - ast_log(LOG_DEBUG, "Got 491 on %s, unspported. Call ID %s\n", sip_methods[sipmethod].text, p->callid); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - break; - case 501: /* Not Implemented */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - else if (sipmethod == SIP_REFER) - handle_response_refer(p, resp, rest, req, seqno); - else - ast_log(LOG_WARNING, "Host '%s' does not implement '%s'\n", ast_inet_ntoa(p->sa.sin_addr), msg); - break; - case 603: /* Declined transfer */ - if (sipmethod == SIP_REFER) { - handle_response_refer(p, resp, rest, req, seqno); - break; - } - /* Fallthrough */ - default: - if ((resp >= 300) && (resp < 700)) { - /* Fatal response */ - if ((option_verbose > 2) && (resp != 487)) - ast_verbose(VERBOSE_PREFIX_3 "Got SIP response %d \"%s\" back from %s\n", resp, rest, ast_inet_ntoa(p->sa.sin_addr)); - - if (sipmethod == SIP_INVITE) - stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */ - - /* XXX Locking issues?? XXX */ - switch(resp) { - case 300: /* Multiple Choices */ - case 301: /* Moved permenantly */ - case 302: /* Moved temporarily */ - case 305: /* Use Proxy */ - parse_moved_contact(p, req); - /* Fall through */ - case 486: /* Busy here */ - case 600: /* Busy everywhere */ - case 603: /* Decline */ - if (p->owner) - ast_queue_control(p->owner, AST_CONTROL_BUSY); - break; - case 482: /* - \note SIP is incapable of performing a hairpin call, which - is yet another failure of not having a layer 2 (again, YAY - IETF for thinking ahead). So we treat this as a call - forward and hope we end up at the right place... */ - if (option_debug) - ast_log(LOG_DEBUG, "Hairpin detected, setting up call forward for what it's worth\n"); - if (p->owner) - ast_string_field_build(p->owner, call_forward, - "Local/%s@%s", p->username, p->context); - /* Fall through */ - case 480: /* Temporarily Unavailable */ - case 404: /* Not Found */ - case 410: /* Gone */ - case 400: /* Bad Request */ - case 500: /* Server error */ - if (sipmethod == SIP_REFER) { - handle_response_refer(p, resp, rest, req, seqno); - break; - } - /* Fall through */ - case 502: /* Bad gateway */ - case 503: /* Service Unavailable */ - case 504: /* Server Timeout */ - if (owner) - ast_queue_control(p->owner, AST_CONTROL_CONGESTION); - break; - default: - /* Send hangup */ - if (owner && sipmethod != SIP_MESSAGE && sipmethod != SIP_INFO && sipmethod != SIP_BYE) - ast_queue_hangup(p->owner); - break; - } - /* ACK on invite */ - if (sipmethod == SIP_INVITE) - transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE); - if (sipmethod != SIP_MESSAGE && sipmethod != SIP_INFO) - sip_alreadygone(p); - if (!p->owner) - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } else if ((resp >= 100) && (resp < 200)) { - if (sipmethod == SIP_INVITE) { - if (!ast_test_flag(req, SIP_PKT_IGNORE) && sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - if (find_sdp(req)) - process_sdp(p, req); - if (p->owner) { - /* Queue a progress frame */ - ast_queue_control(p->owner, AST_CONTROL_PROGRESS); - } - } - } else - ast_log(LOG_NOTICE, "Dont know how to handle a %d %s response from %s\n", resp, rest, p->owner ? p->owner->name : ast_inet_ntoa(p->sa.sin_addr)); - } - } else { - /* Responses to OUTGOING SIP requests on INCOMING calls - get handled here. As well as out-of-call message responses */ - if (ast_test_flag(req, SIP_PKT_DEBUG)) - ast_verbose("SIP Response message for INCOMING dialog %s arrived\n", msg); - - if (sipmethod == SIP_INVITE && resp == 200) { - /* Tags in early session is replaced by the tag in 200 OK, which is - the final reply to our INVITE */ - char tag[128]; - - gettag(req, "To", tag, sizeof(tag)); - ast_string_field_set(p, theirtag, tag); - } - - switch(resp) { - case 200: - if (sipmethod == SIP_INVITE) { - handle_response_invite(p, resp, rest, req, seqno); - } else if (sipmethod == SIP_CANCEL) { - if (option_debug) - ast_log(LOG_DEBUG, "Got 200 OK on CANCEL\n"); - - /* Wait for 487, then destroy */ - } else if (sipmethod == SIP_NOTIFY) { - /* They got the notify, this is the end */ - if (p->owner) { - if (p->refer) { - if (option_debug) - ast_log(LOG_DEBUG, "Got 200 OK on NOTIFY for transfer\n"); - } else - ast_log(LOG_WARNING, "Notify answer on an owned channel?\n"); - /* ast_queue_hangup(p->owner); Disabled */ - } else { - if (!p->subscribed && !p->refer) - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - if (ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE)) { - /* Ready to send the next state we have on queue */ - ast_clear_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE); - cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p); - } - } - } else if (sipmethod == SIP_BYE) - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - else if (sipmethod == SIP_MESSAGE || sipmethod == SIP_INFO) - /* We successfully transmitted a message or - a video update request in INFO */ - ; - else if (sipmethod == SIP_BYE) - /* Ok, we're ready to go */ - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - break; - case 202: /* Transfer accepted */ - if (sipmethod == SIP_REFER) - handle_response_refer(p, resp, rest, req, seqno); - break; - case 401: /* www-auth */ - case 407: - if (sipmethod == SIP_REFER) - handle_response_refer(p, resp, rest, req, seqno); - else if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - else if (sipmethod == SIP_BYE) { - char *auth, *auth2; - - auth = (resp == 407 ? "Proxy-Authenticate" : "WWW-Authenticate"); - auth2 = (resp == 407 ? "Proxy-Authorization" : "Authorization"); - if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, auth, auth2, sipmethod, 0)) { - ast_log(LOG_NOTICE, "Failed to authenticate on %s to '%s'\n", msg, get_header(&p->initreq, "From")); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - } - break; - case 481: /* Call leg does not exist */ - if (sipmethod == SIP_INVITE) { - /* Re-invite failed */ - handle_response_invite(p, resp, rest, req, seqno); - } else if (sipmethod == SIP_BYE) { - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } else if (sipdebug) { - ast_log (LOG_DEBUG, "Remote host can't match request %s to call '%s'. Giving up\n", sip_methods[sipmethod].text, p->callid); - } - break; - case 501: /* Not Implemented */ - if (sipmethod == SIP_INVITE) - handle_response_invite(p, resp, rest, req, seqno); - else if (sipmethod == SIP_REFER) - handle_response_refer(p, resp, rest, req, seqno); - break; - case 603: /* Declined transfer */ - if (sipmethod == SIP_REFER) { - handle_response_refer(p, resp, rest, req, seqno); - break; - } - /* Fallthrough */ - default: /* Errors without handlers */ - if ((resp >= 100) && (resp < 200)) { - if (sipmethod == SIP_INVITE) { /* re-invite */ - if (!ast_test_flag(req, SIP_PKT_IGNORE) && sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - } - } - if ((resp >= 300) && (resp < 700)) { - if ((option_verbose > 2) && (resp != 487)) - ast_verbose(VERBOSE_PREFIX_3 "Incoming call: Got SIP response %d \"%s\" back from %s\n", resp, rest, ast_inet_ntoa(p->sa.sin_addr)); - switch(resp) { - case 488: /* Not acceptable here - codec error */ - case 603: /* Decline */ - case 500: /* Server error */ - case 502: /* Bad gateway */ - case 503: /* Service Unavailable */ - case 504: /* Server timeout */ - - /* re-invite failed */ - if (sipmethod == SIP_INVITE && sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - break; - } - } - break; - } - } -} - - -/*! \brief Park SIP call support function - Starts in a new thread, then parks the call - XXX Should we add a wait period after streaming audio and before hangup?? Sometimes the - audio can't be heard before hangup -*/ -static void *sip_park_thread(void *stuff) -{ - struct ast_channel *transferee, *transferer; /* Chan1: The transferee, Chan2: The transferer */ - struct sip_dual *d; - struct sip_request req; - int ext; - int res; - - d = stuff; - transferee = d->chan1; - transferer = d->chan2; - copy_request(&req, &d->req); - - if (!transferee || !transferer) { - ast_log(LOG_ERROR, "Missing channels for parking! Transferer %s Transferee %s\n", transferer ? "<available>" : "<missing>", transferee ? "<available>" : "<missing>" ); - return NULL; - } - if (option_debug > 3) - ast_log(LOG_DEBUG, "SIP Park: Transferer channel %s, Transferee %s\n", transferer->name, transferee->name); - - ast_channel_lock(transferee); - if (ast_do_masquerade(transferee)) { - ast_log(LOG_WARNING, "Masquerade failed.\n"); - transmit_response(transferer->tech_pvt, "503 Internal error", &req); - ast_channel_unlock(transferee); - return NULL; - } - ast_channel_unlock(transferee); - - res = ast_park_call(transferee, transferer, 0, &ext); - - -#ifdef WHEN_WE_KNOW_THAT_THE_CLIENT_SUPPORTS_MESSAGE - if (!res) { - transmit_message_with_text(transferer->tech_pvt, "Unable to park call.\n"); - } else { - /* Then tell the transferer what happened */ - sprintf(buf, "Call parked on extension '%d'", ext); - transmit_message_with_text(transferer->tech_pvt, buf); - } -#endif - - /* Any way back to the current call??? */ - /* Transmit response to the REFER request */ - transmit_response(transferer->tech_pvt, "202 Accepted", &req); - if (!res) { - /* Transfer succeeded */ - append_history(transferer->tech_pvt, "SIPpark","Parked call on %d", ext); - transmit_notify_with_sipfrag(transferer->tech_pvt, d->seqno, "200 OK", TRUE); - transferer->hangupcause = AST_CAUSE_NORMAL_CLEARING; - ast_hangup(transferer); /* This will cause a BYE */ - if (option_debug) - ast_log(LOG_DEBUG, "SIP Call parked on extension '%d'\n", ext); - } else { - transmit_notify_with_sipfrag(transferer->tech_pvt, d->seqno, "503 Service Unavailable", TRUE); - append_history(transferer->tech_pvt, "SIPpark","Parking failed\n"); - if (option_debug) - ast_log(LOG_DEBUG, "SIP Call parked failed \n"); - /* Do not hangup call */ - } - free(d); - return NULL; -} - -/*! \brief Park a call using the subsystem in res_features.c - This is executed in a separate thread -*/ -static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno) -{ - struct sip_dual *d; - struct ast_channel *transferee, *transferer; - /* Chan2m: The transferer, chan1m: The transferee */ - pthread_t th; - - transferee = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan1->accountcode, chan1->exten, chan1->context, chan1->amaflags, "Parking/%s", chan1->name); - transferer = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, chan2->accountcode, chan2->exten, chan2->context, chan2->amaflags, "SIPPeer/%s", chan2->name); - if ((!transferer) || (!transferee)) { - if (transferee) { - transferee->hangupcause = AST_CAUSE_SWITCH_CONGESTION; - ast_hangup(transferee); - } - if (transferer) { - transferer->hangupcause = AST_CAUSE_SWITCH_CONGESTION; - ast_hangup(transferer); - } - return -1; - } - - /* Make formats okay */ - transferee->readformat = chan1->readformat; - transferee->writeformat = chan1->writeformat; - - /* Prepare for taking over the channel */ - ast_channel_masquerade(transferee, chan1); - - /* Setup the extensions and such */ - ast_copy_string(transferee->context, chan1->context, sizeof(transferee->context)); - ast_copy_string(transferee->exten, chan1->exten, sizeof(transferee->exten)); - transferee->priority = chan1->priority; - - /* We make a clone of the peer channel too, so we can play - back the announcement */ - - /* Make formats okay */ - transferer->readformat = chan2->readformat; - transferer->writeformat = chan2->writeformat; - - /* Prepare for taking over the channel. Go ahead and grab this channel - * lock here to avoid a deadlock with callbacks into the channel driver - * that hold the channel lock and want the pvt lock. */ - while (ast_channel_trylock(chan2)) { - struct sip_pvt *pvt = chan2->tech_pvt; - DEADLOCK_AVOIDANCE(&pvt->lock); - } - ast_channel_masquerade(transferer, chan2); - ast_channel_unlock(chan2); - - /* Setup the extensions and such */ - ast_copy_string(transferer->context, chan2->context, sizeof(transferer->context)); - ast_copy_string(transferer->exten, chan2->exten, sizeof(transferer->exten)); - transferer->priority = chan2->priority; - - ast_channel_lock(transferer); - if (ast_do_masquerade(transferer)) { - ast_log(LOG_WARNING, "Masquerade failed :(\n"); - ast_channel_unlock(transferer); - transferer->hangupcause = AST_CAUSE_SWITCH_CONGESTION; - ast_hangup(transferer); - return -1; - } - ast_channel_unlock(transferer); - if (!transferer || !transferee) { - if (!transferer) { - if (option_debug) - ast_log(LOG_DEBUG, "No transferer channel, giving up parking\n"); - } - if (!transferee) { - if (option_debug) - ast_log(LOG_DEBUG, "No transferee channel, giving up parking\n"); - } - return -1; - } - if ((d = ast_calloc(1, sizeof(*d)))) { - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - /* Save original request for followup */ - copy_request(&d->req, req); - d->chan1 = transferee; /* Transferee */ - d->chan2 = transferer; /* Transferer */ - d->seqno = seqno; - if (ast_pthread_create_background(&th, &attr, sip_park_thread, d) < 0) { - /* Could not start thread */ - free(d); /* We don't need it anymore. If thread is created, d will be free'd - by sip_park_thread() */ - pthread_attr_destroy(&attr); - return 0; - } - pthread_attr_destroy(&attr); - } - return -1; -} - -/*! \brief Turn off generator data - XXX Does this function belong in the SIP channel? -*/ -static void ast_quiet_chan(struct ast_channel *chan) -{ - if (chan && chan->_state == AST_STATE_UP) { - if (ast_test_flag(chan, AST_FLAG_MOH)) - ast_moh_stop(chan); - else if (chan->generatordata) - ast_deactivate_generator(chan); - } -} - -/*! \brief Attempt transfer of SIP call - This fix for attended transfers on a local PBX */ -static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target) -{ - int res = 0; - struct ast_channel *peera = NULL, - *peerb = NULL, - *peerc = NULL, - *peerd = NULL; - - - /* We will try to connect the transferee with the target and hangup - all channels to the transferer */ - if (option_debug > 3) { - ast_log(LOG_DEBUG, "Sip transfer:--------------------\n"); - if (transferer->chan1) - ast_log(LOG_DEBUG, "-- Transferer to PBX channel: %s State %s\n", transferer->chan1->name, ast_state2str(transferer->chan1->_state)); - else - ast_log(LOG_DEBUG, "-- No transferer first channel - odd??? \n"); - if (target->chan1) - ast_log(LOG_DEBUG, "-- Transferer to PBX second channel (target): %s State %s\n", target->chan1->name, ast_state2str(target->chan1->_state)); - else - ast_log(LOG_DEBUG, "-- No target first channel ---\n"); - if (transferer->chan2) - ast_log(LOG_DEBUG, "-- Bridged call to transferee: %s State %s\n", transferer->chan2->name, ast_state2str(transferer->chan2->_state)); - else - ast_log(LOG_DEBUG, "-- No bridged call to transferee\n"); - if (target->chan2) - ast_log(LOG_DEBUG, "-- Bridged call to transfer target: %s State %s\n", target->chan2 ? target->chan2->name : "<none>", target->chan2 ? ast_state2str(target->chan2->_state) : "(none)"); - else - ast_log(LOG_DEBUG, "-- No target second channel ---\n"); - ast_log(LOG_DEBUG, "-- END Sip transfer:--------------------\n"); - } - if (transferer->chan2) { /* We have a bridge on the transferer's channel */ - peera = transferer->chan1; /* Transferer - PBX -> transferee channel * the one we hangup */ - peerb = target->chan1; /* Transferer - PBX -> target channel - This will get lost in masq */ - peerc = transferer->chan2; /* Asterisk to Transferee */ - peerd = target->chan2; /* Asterisk to Target */ - if (option_debug > 2) - ast_log(LOG_DEBUG, "SIP transfer: Four channels to handle\n"); - } else if (target->chan2) { /* Transferer has no bridge (IVR), but transferee */ - peera = target->chan1; /* Transferer to PBX -> target channel */ - peerb = transferer->chan1; /* Transferer to IVR*/ - peerc = target->chan2; /* Asterisk to Target */ - peerd = transferer->chan2; /* Nothing */ - if (option_debug > 2) - ast_log(LOG_DEBUG, "SIP transfer: Three channels to handle\n"); - } - - if (peera && peerb && peerc && (peerb != peerc)) { - ast_quiet_chan(peera); /* Stop generators */ - ast_quiet_chan(peerb); - ast_quiet_chan(peerc); - if (peerd) - ast_quiet_chan(peerd); - - if (option_debug > 3) - ast_log(LOG_DEBUG, "SIP transfer: trying to masquerade %s into %s\n", peerc->name, peerb->name); - if (ast_channel_masquerade(peerb, peerc)) { - ast_log(LOG_WARNING, "Failed to masquerade %s into %s\n", peerb->name, peerc->name); - res = -1; - } else - ast_log(LOG_DEBUG, "SIP transfer: Succeeded to masquerade channels.\n"); - return res; - } else { - ast_log(LOG_NOTICE, "SIP Transfer attempted with no appropriate bridged calls to transfer\n"); - if (transferer->chan1) - ast_softhangup_nolock(transferer->chan1, AST_SOFTHANGUP_DEV); - if (target->chan1) - ast_softhangup_nolock(target->chan1, AST_SOFTHANGUP_DEV); - return -2; - } - return 0; -} - -/*! \brief Get tag from packet - * - * \return Returns the pointer to the provided tag buffer, - * or NULL if the tag was not found. - */ -static const char *gettag(const struct sip_request *req, const char *header, char *tagbuf, int tagbufsize) -{ - const char *thetag; - - if (!tagbuf) - return NULL; - tagbuf[0] = '\0'; /* reset the buffer */ - thetag = get_header(req, header); - thetag = strcasestr(thetag, ";tag="); - if (thetag) { - thetag += 5; - ast_copy_string(tagbuf, thetag, tagbufsize); - return strsep(&tagbuf, ";"); - } - return NULL; -} - -/*! \brief Handle incoming notifications */ -static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e) -{ - /* This is mostly a skeleton for future improvements */ - /* Mostly created to return proper answers on notifications on outbound REFER's */ - int res = 0; - const char *event = get_header(req, "Event"); - char *eventid = NULL; - char *sep; - - if( (sep = strchr(event, ';')) ) { /* XXX bug here - overwriting string ? */ - *sep++ = '\0'; - eventid = sep; - } - - if (option_debug > 1 && sipdebug) - ast_log(LOG_DEBUG, "Got NOTIFY Event: %s\n", event); - - if (strcmp(event, "refer")) { - /* We don't understand this event. */ - /* Here's room to implement incoming voicemail notifications :-) */ - transmit_response(p, "489 Bad event", req); - res = -1; - } else { - /* Save nesting depth for now, since there might be other events we will - support in the future */ - - /* Handle REFER notifications */ - - char buf[1024]; - char *cmd, *code; - int respcode; - int success = TRUE; - - /* EventID for each transfer... EventID is basically the REFER cseq - - We are getting notifications on a call that we transfered - We should hangup when we are getting a 200 OK in a sipfrag - Check if we have an owner of this event */ - - /* Check the content type */ - if (strncasecmp(get_header(req, "Content-Type"), "message/sipfrag", strlen("message/sipfrag"))) { - /* We need a sipfrag */ - transmit_response(p, "400 Bad request", req); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return -1; - } - - /* Get the text of the attachment */ - if (get_msg_text(buf, sizeof(buf), req)) { - ast_log(LOG_WARNING, "Unable to retrieve attachment from NOTIFY %s\n", p->callid); - transmit_response(p, "400 Bad request", req); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return -1; - } - - /* - From the RFC... - A minimal, but complete, implementation can respond with a single - NOTIFY containing either the body: - SIP/2.0 100 Trying - - if the subscription is pending, the body: - SIP/2.0 200 OK - if the reference was successful, the body: - SIP/2.0 503 Service Unavailable - if the reference failed, or the body: - SIP/2.0 603 Declined - - if the REFER request was accepted before approval to follow the - reference could be obtained and that approval was subsequently denied - (see Section 2.4.7). - - If there are several REFERs in the same dialog, we need to - match the ID of the event header... - */ - if (option_debug > 2) - ast_log(LOG_DEBUG, "* SIP Transfer NOTIFY Attachment: \n---%s\n---\n", buf); - cmd = ast_skip_blanks(buf); - code = cmd; - /* We are at SIP/2.0 */ - while(*code && (*code > 32)) { /* Search white space */ - code++; - } - *code++ = '\0'; - code = ast_skip_blanks(code); - sep = code; - sep++; - while(*sep && (*sep > 32)) { /* Search white space */ - sep++; - } - *sep++ = '\0'; /* Response string */ - respcode = atoi(code); - switch (respcode) { - case 100: /* Trying: */ - case 101: /* dialog establishment */ - /* Don't do anything yet */ - break; - case 183: /* Ringing: */ - /* Don't do anything yet */ - break; - case 200: /* OK: The new call is up, hangup this call */ - /* Hangup the call that we are replacing */ - break; - case 301: /* Moved permenantly */ - case 302: /* Moved temporarily */ - /* Do we get the header in the packet in this case? */ - success = FALSE; - break; - case 503: /* Service Unavailable: The new call failed */ - /* Cancel transfer, continue the call */ - success = FALSE; - break; - case 603: /* Declined: Not accepted */ - /* Cancel transfer, continue the current call */ - success = FALSE; - break; - } - if (!success) { - ast_log(LOG_NOTICE, "Transfer failed. Sorry. Nothing further to do with this call\n"); - } - - /* Confirm that we received this packet */ - transmit_response(p, "200 OK", req); - }; - - if (!p->lastinvite) - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - - return res; -} - -/*! \brief Handle incoming OPTIONS request */ -static int handle_request_options(struct sip_pvt *p, struct sip_request *req) -{ - int res; - - - /* XXX Should we authenticate OPTIONS? XXX */ - - if (p->lastinvite) { - /* if this is a request in an active dialog, just confirm that the dialog exists. */ - transmit_response_with_allow(p, "200 OK", req, 0); - return 0; - } - - res = get_destination(p, req); - build_contact(p); - - if (ast_strlen_zero(p->context)) - ast_string_field_set(p, context, default_context); - - if (ast_shutting_down()) - transmit_response_with_allow(p, "503 Unavailable", req, 0); - else if (res < 0) - transmit_response_with_allow(p, "404 Not Found", req, 0); - else - transmit_response_with_allow(p, "200 OK", req, 0); - - /* Destroy if this OPTIONS was the opening request, but not if - it's in the middle of a normal call flow. */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - - return res; -} - -/*! \brief Handle the transfer part of INVITE with a replaces: header, - meaning a target pickup or an attended transfer */ -static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int ignore, int seqno, struct sockaddr_in *sin) -{ - struct ast_frame *f; - int earlyreplace = 0; - int oneleggedreplace = 0; /* Call with no bridge, propably IVR or voice message */ - struct ast_channel *c = p->owner; /* Our incoming call */ - struct ast_channel *replacecall = p->refer->refer_call->owner; /* The channel we're about to take over */ - struct ast_channel *targetcall; /* The bridge to the take-over target */ - - /* Check if we're in ring state */ - if (replacecall->_state == AST_STATE_RING) - earlyreplace = 1; - - /* Check if we have a bridge */ - if (!(targetcall = ast_bridged_channel(replacecall))) { - /* We have no bridge */ - if (!earlyreplace) { - if (option_debug > 1) - ast_log(LOG_DEBUG, " Attended transfer attempted to replace call with no bridge (maybe ringing). Channel %s!\n", replacecall->name); - oneleggedreplace = 1; - } - } - if (option_debug > 3 && targetcall && targetcall->_state == AST_STATE_RINGING) - ast_log(LOG_DEBUG, "SIP transfer: Target channel is in ringing state\n"); - - if (option_debug > 3) { - if (targetcall) - ast_log(LOG_DEBUG, "SIP transfer: Invite Replace incoming channel should bridge to channel %s while hanging up channel %s\n", targetcall->name, replacecall->name); - else - ast_log(LOG_DEBUG, "SIP transfer: Invite Replace incoming channel should replace and hang up channel %s (one call leg)\n", replacecall->name); - } - - if (ignore) { - ast_log(LOG_NOTICE, "Ignoring this INVITE with replaces in a stupid way.\n"); - /* We should answer something here. If we are here, the - call we are replacing exists, so an accepted - can't harm */ - transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE); - /* Do something more clever here */ - ast_channel_unlock(c); - ast_mutex_unlock(&p->refer->refer_call->lock); - return 1; - } - if (!c) { - /* What to do if no channel ??? */ - ast_log(LOG_ERROR, "Unable to create new channel. Invite/replace failed.\n"); - transmit_response_reliable(p, "503 Service Unavailable", req); - append_history(p, "Xfer", "INVITE/Replace Failed. No new channel."); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - ast_mutex_unlock(&p->refer->refer_call->lock); - return 1; - } - append_history(p, "Xfer", "INVITE/Replace received"); - /* We have three channels to play with - channel c: New incoming call - targetcall: Call from PBX to target - p->refer->refer_call: SIP pvt dialog from transferer to pbx. - replacecall: The owner of the previous - We need to masq C into refer_call to connect to - targetcall; - If we are talking to internal audio stream, target call is null. - */ - - /* Fake call progress */ - transmit_response(p, "100 Trying", req); - ast_setstate(c, AST_STATE_RING); - - /* Masquerade the new call into the referred call to connect to target call - Targetcall is not touched by the masq */ - - /* Answer the incoming call and set channel to UP state */ - transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE); - - ast_setstate(c, AST_STATE_UP); - - /* Stop music on hold and other generators */ - ast_quiet_chan(replacecall); - ast_quiet_chan(targetcall); - if (option_debug > 3) - ast_log(LOG_DEBUG, "Invite/Replaces: preparing to masquerade %s into %s\n", c->name, replacecall->name); - /* Unlock clone, but not original (replacecall) */ - if (!oneleggedreplace) - ast_channel_unlock(c); - - /* Unlock PVT */ - ast_mutex_unlock(&p->refer->refer_call->lock); - - /* Make sure that the masq does not free our PVT for the old call */ - if (! earlyreplace && ! oneleggedreplace ) - ast_set_flag(&p->refer->refer_call->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */ - - /* Prepare the masquerade - if this does not happen, we will be gone */ - if(ast_channel_masquerade(replacecall, c)) - ast_log(LOG_ERROR, "Failed to masquerade C into Replacecall\n"); - else if (option_debug > 3) - ast_log(LOG_DEBUG, "Invite/Replaces: Going to masquerade %s into %s\n", c->name, replacecall->name); - - /* The masquerade will happen as soon as someone reads a frame from the channel */ - - /* C should now be in place of replacecall */ - /* ast_read needs to lock channel */ - ast_channel_unlock(c); - - if (earlyreplace || oneleggedreplace ) { - /* Force the masq to happen */ - if ((f = ast_read(replacecall))) { /* Force the masq to happen */ - ast_frfree(f); - f = NULL; - if (option_debug > 3) - ast_log(LOG_DEBUG, "Invite/Replace: Could successfully read frame from RING channel!\n"); - } else { - ast_log(LOG_WARNING, "Invite/Replace: Could not read frame from RING channel \n"); - } - c->hangupcause = AST_CAUSE_SWITCH_CONGESTION; - if (!oneleggedreplace) - ast_channel_unlock(replacecall); - } else { /* Bridged call, UP channel */ - if ((f = ast_read(replacecall))) { /* Force the masq to happen */ - /* Masq ok */ - ast_frfree(f); - f = NULL; - if (option_debug > 2) - ast_log(LOG_DEBUG, "Invite/Replace: Could successfully read frame from channel! Masq done.\n"); - } else { - ast_log(LOG_WARNING, "Invite/Replace: Could not read frame from channel. Transfer failed\n"); - } - ast_channel_unlock(replacecall); - } - ast_mutex_unlock(&p->refer->refer_call->lock); - - ast_setstate(c, AST_STATE_DOWN); - if (option_debug > 3) { - struct ast_channel *test; - ast_log(LOG_DEBUG, "After transfer:----------------------------\n"); - ast_log(LOG_DEBUG, " -- C: %s State %s\n", c->name, ast_state2str(c->_state)); - if (replacecall) - ast_log(LOG_DEBUG, " -- replacecall: %s State %s\n", replacecall->name, ast_state2str(replacecall->_state)); - if (p->owner) { - ast_log(LOG_DEBUG, " -- P->owner: %s State %s\n", p->owner->name, ast_state2str(p->owner->_state)); - test = ast_bridged_channel(p->owner); - if (test) - ast_log(LOG_DEBUG, " -- Call bridged to P->owner: %s State %s\n", test->name, ast_state2str(test->_state)); - else - ast_log(LOG_DEBUG, " -- No call bridged to C->owner \n"); - } else - ast_log(LOG_DEBUG, " -- No channel yet \n"); - ast_log(LOG_DEBUG, "End After transfer:----------------------------\n"); - } - - ast_channel_unlock(p->owner); /* Unlock new owner */ - if (!oneleggedreplace) - ast_mutex_unlock(&p->lock); /* Unlock SIP structure */ - - /* The call should be down with no ast_channel, so hang it up */ - c->tech_pvt = NULL; - ast_hangup(c); - return 0; -} - -/*! \brief helper routine for sip_uri_cmp - * - * This takes the parameters from two SIP URIs and determines - * if the URIs match. The rules for parameters *suck*. Here's a breakdown - * 1. If a parameter appears in both URIs, then they must have the same value - * in order for the URIs to match - * 2. If one URI has a user, maddr, ttl, or method parameter, then the other - * URI must also have that parameter and must have the same value - * in order for the URIs to match - * 3. All other headers appearing in only one URI are not considered when - * determining if URIs match - * - * \param input1 Parameters from URI 1 - * \param input2 Parameters from URI 2 - * \return Return 0 if the URIs' parameters match, 1 if they do not - */ -static int sip_uri_params_cmp(const char *input1, const char *input2) -{ - char *params1 = ast_strdupa(input1); - char *params2 = ast_strdupa(input2); - char *pos1; - char *pos2; - int maddrmatch = 0; - int ttlmatch = 0; - int usermatch = 0; - int methodmatch = 0; - - /*Quick optimization. If both params are zero-length, then - * they match - */ - if (ast_strlen_zero(params1) && ast_strlen_zero(params2)) { - return 0; - } - - pos1 = params1; - while (!ast_strlen_zero(pos1)) { - char *name1 = pos1; - char *value1 = strchr(pos1, '='); - char *semicolon1 = strchr(pos1, ';'); - int matched = 0; - if (semicolon1) { - *semicolon1++ = '\0'; - } - if (!value1) { - goto fail; - } - *value1++ = '\0'; - /* Checkpoint reached. We have the name and value parsed for param1 - * We have to duplicate params2 each time through the second loop - * or else we can't search and replace the semicolons with \0 each - * time - */ - pos2 = ast_strdupa(params2); - while (!ast_strlen_zero(pos2)) { - char *name2 = pos2; - char *value2 = strchr(pos2, '='); - char *semicolon2 = strchr(pos2, ';'); - if (semicolon2) { - *semicolon2++ = '\0'; - } - if (!value2) { - goto fail; - } - *value2++ = '\0'; - if (!strcasecmp(name1, name2)) { - if (strcasecmp(value1, value2)) { - goto fail; - } else { - matched = 1; - break; - } - } - pos2 = semicolon2; - } - /* Need to see if the parameter we're looking at is one of the 'must-match' parameters */ - if (!strcasecmp(name1, "maddr")) { - if (matched) { - maddrmatch = 1; - } else { - goto fail; - } - } else if (!strcasecmp(name1, "ttl")) { - if (matched) { - ttlmatch = 1; - } else { - goto fail; - } - } else if (!strcasecmp(name1, "user")) { - if (matched) { - usermatch = 1; - } else { - goto fail; - } - } else if (!strcasecmp(name1, "method")) { - if (matched) { - methodmatch = 1; - } else { - goto fail; - } - } - pos1 = semicolon1; - } - - /* We've made it out of that horrible O(m*n) construct and there are no - * failures yet. We're not done yet, though, because params2 could have - * an maddr, ttl, user, or method header and params1 did not. - */ - pos2 = params2; - while (!ast_strlen_zero(pos2)) { - char *name2 = pos2; - char *value2 = strchr(pos2, '='); - char *semicolon2 = strchr(pos2, ';'); - if (semicolon2) { - *semicolon2++ = '\0'; - } - if (!value2) { - goto fail; - } - *value2++ = '\0'; - if ((!strcasecmp(name2, "maddr") && !maddrmatch) || - (!strcasecmp(name2, "ttl") && !ttlmatch) || - (!strcasecmp(name2, "user") && !usermatch) || - (!strcasecmp(name2, "method") && !methodmatch)) { - goto fail; - } - } - return 0; - -fail: - return 1; -} - -/*! \brief helper routine for sip_uri_cmp - * - * This takes the "headers" from two SIP URIs and determines - * if the URIs match. The rules for headers is simple. If a header - * appears in one URI, then it must also appear in the other URI. The - * order in which the headers appear does not matter. - * - * \param input1 Headers from URI 1 - * \param input2 Headers from URI 2 - * \return Return 0 if the URIs' headers match, 1 if they do not - */ -static int sip_uri_headers_cmp(const char *input1, const char *input2) -{ - char *headers1 = ast_strdupa(input1); - char *headers2 = ast_strdupa(input2); - int zerolength1 = ast_strlen_zero(headers1); - int zerolength2 = ast_strlen_zero(headers2); - int different = 0; - char *header1; - - if ((zerolength1 && !zerolength2) || - (zerolength2 && !zerolength1)) - return 1; - - if (zerolength1 && zerolength2) - return 0; - - /* At this point, we can definitively state that both inputs are - * not zero-length. First, one more optimization. If the length - * of the headers is not equal, then we definitely have no match - */ - if (strlen(headers1) != strlen(headers2)) { - return 1; - } - - for (header1 = strsep(&headers1, "&"); header1; header1 = strsep(&headers1, "&")) { - if (!strcasestr(headers2, header1)) { - different = 1; - break; - } - } - - return different; -} - -static int sip_uri_cmp(const char *input1, const char *input2) -{ - char *uri1 = ast_strdupa(input1); - char *uri2 = ast_strdupa(input2); - char *host1; - char *host2; - char *params1; - char *params2; - char *headers1; - char *headers2; - - /* Strip off "sip:" from the URI. We know this is present - * because it was checked back in parse_request() - */ - strsep(&uri1, ":"); - strsep(&uri2, ":"); - - if ((host1 = strchr(uri1, '@'))) { - *host1++ = '\0'; - } - if ((host2 = strchr(uri2, '@'))) { - *host2++ = '\0'; - } - - /* Check for mismatched username and passwords. This is the - * only case-sensitive comparison of a SIP URI - */ - if ((host1 && !host2) || - (host2 && !host1) || - (host1 && host2 && strcmp(uri1, uri2))) { - return 1; - } - - if (!host1) - host1 = uri1; - if (!host2) - host2 = uri2; - - /* Strip off the parameters and headers so we can compare - * host and port - */ - - if ((params1 = strchr(host1, ';'))) { - *params1++ = '\0'; - } - if ((params2 = strchr(host2, ';'))) { - *params2++ = '\0'; - } - - /* Headers come after parameters, but there may be headers without - * parameters, thus the S_OR - */ - if ((headers1 = strchr(S_OR(params1, host1), '?'))) { - *headers1++ = '\0'; - } - if ((headers2 = strchr(S_OR(params2, host2), '?'))) { - *headers2++ = '\0'; - } - - /* Now the host/port are properly isolated. We can get by with a string comparison - * because the SIP URI checking rules have some interesting exceptions that make - * this possible. I will note 2 in particular - * 1. hostnames which resolve to the same IP address as well as a hostname and its - * IP address are not considered a match with SIP URI's. - * 2. If one URI specifies a port and the other does not, then the URIs do not match. - * This includes if one URI explicitly contains port 5060 and the other implies it - * by not having a port specified. - */ - - if (strcasecmp(host1, host2)) { - return 1; - } - - /* Headers have easier rules to follow, so do those first */ - if (sip_uri_headers_cmp(headers1, headers2)) { - return 1; - } - - /* And now the parameters. Ugh */ - return sip_uri_params_cmp(params1, params2); -} - - -/*! \brief Handle incoming INVITE request -\note If the INVITE has a Replaces header, it is part of an - * attended transfer. If so, we do not go through the dial - * plan but tries to find the active call and masquerade - * into it - */ -static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *recount, char *e, int *nounlock) -{ - int res = 1; - int gotdest; - const char *p_replaces; - char *replace_id = NULL; - const char *required; - unsigned int required_profile = 0; - struct ast_channel *c = NULL; /* New channel */ - int reinvite = 0; - - /* Find out what they support */ - if (!p->sipoptions) { - const char *supported = get_header(req, "Supported"); - if (!ast_strlen_zero(supported)) - parse_sip_options(p, supported); - } - - /* Find out what they require */ - required = get_header(req, "Require"); - if (!ast_strlen_zero(required)) { - required_profile = parse_sip_options(NULL, required); - if (required_profile && required_profile != SIP_OPT_REPLACES) { - /* At this point we only support REPLACES */ - transmit_response_with_unsupported(p, "420 Bad extension (unsupported)", req, required); - ast_log(LOG_WARNING,"Received SIP INVITE with unsupported required extension: %s\n", required); - p->invitestate = INV_COMPLETED; - if (!p->lastinvite) - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return -1; - } - } - - /* Check if this is a loop */ - if (ast_test_flag(&p->flags[0], SIP_OUTGOING) && p->owner && (p->owner->_state != AST_STATE_UP)) { - /* This is a call to ourself. Send ourselves an error code and stop - processing immediately, as SIP really has no good mechanism for - being able to call yourself */ - /* If pedantic is on, we need to check the tags. If they're different, this is - in fact a forked call through a SIP proxy somewhere. */ - int different; - if (pedanticsipchecking) - different = sip_uri_cmp(p->initreq.rlPart2, req->rlPart2); - else - different = strcmp(p->initreq.rlPart2, req->rlPart2); - if (!different) { - transmit_response(p, "482 Loop Detected", req); - p->invitestate = INV_COMPLETED; - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return 0; - } else { - /* This is a spiral. What we need to do is to just change the outgoing INVITE - * so that it now routes to the new Request URI. Since we created the INVITE ourselves - * that should be all we need to do. - */ - char *uri = ast_strdupa(req->rlPart2); - char *at = strchr(uri, '@'); - char *peerorhost; - if (option_debug > 2) { - ast_log(LOG_DEBUG, "Potential spiral detected. Original RURI was %s, new RURI is %s\n", p->initreq.rlPart2, req->rlPart2); - } - if (at) { - *at = '\0'; - } - /* Parse out "sip:" */ - if ((peerorhost = strchr(uri, ':'))) { - *peerorhost++ = '\0'; - } - ast_string_field_free(p, theirtag); - /* Treat this as if there were a call forward instead... - */ - ast_string_field_set(p->owner, call_forward, peerorhost); - ast_queue_control(p->owner, AST_CONTROL_BUSY); - return 0; - } - } - - if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->pendinginvite) { - /* We already have a pending invite. Sorry. You are on hold. */ - transmit_response_reliable(p, "491 Request Pending", req); - if (option_debug) - ast_log(LOG_DEBUG, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid); - /* Don't destroy dialog here */ - return 0; - } - - p_replaces = get_header(req, "Replaces"); - if (!ast_strlen_zero(p_replaces)) { - /* We have a replaces header */ - char *ptr; - char *fromtag = NULL; - char *totag = NULL; - char *start, *to; - int error = 0; - - if (p->owner) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "INVITE w Replaces on existing call? Refusing action. [%s]\n", p->callid); - transmit_response_reliable(p, "400 Bad request", req); /* The best way to not not accept the transfer */ - /* Do not destroy existing call */ - return -1; - } - - if (sipdebug && option_debug > 2) - ast_log(LOG_DEBUG, "INVITE part of call transfer. Replaces [%s]\n", p_replaces); - /* Create a buffer we can manipulate */ - replace_id = ast_strdupa(p_replaces); - ast_uri_decode(replace_id); - - if (!p->refer && !sip_refer_allocate(p)) { - transmit_response_reliable(p, "500 Server Internal Error", req); - append_history(p, "Xfer", "INVITE/Replace Failed. Out of memory."); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - p->invitestate = INV_COMPLETED; - return -1; - } - - /* Todo: (When we find phones that support this) - if the replaces header contains ";early-only" - we can only replace the call in early - stage, not after it's up. - - If it's not in early mode, 486 Busy. - */ - - /* Skip leading whitespace */ - replace_id = ast_skip_blanks(replace_id); - - start = replace_id; - while ( (ptr = strsep(&start, ";")) ) { - ptr = ast_skip_blanks(ptr); /* XXX maybe unnecessary ? */ - if ( (to = strcasestr(ptr, "to-tag=") ) ) - totag = to + 7; /* skip the keyword */ - else if ( (to = strcasestr(ptr, "from-tag=") ) ) { - fromtag = to + 9; /* skip the keyword */ - fromtag = strsep(&fromtag, "&"); /* trim what ? */ - } - } - - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG,"Invite/replaces: Will use Replace-Call-ID : %s Fromtag: %s Totag: %s\n", replace_id, fromtag ? fromtag : "<no from tag>", totag ? totag : "<no to tag>"); - - - /* Try to find call that we are replacing - If we have a Replaces header, we need to cancel that call if we succeed with this call - */ - if ((p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) { - ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id); - transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req); - error = 1; - } - - /* At this point, bot the pvt and the owner of the call to be replaced is locked */ - - /* The matched call is the call from the transferer to Asterisk . - We want to bridge the bridged part of the call to the - incoming invite, thus taking over the refered call */ - - if (p->refer->refer_call == p) { - ast_log(LOG_NOTICE, "INVITE with replaces into it's own call id (%s == %s)!\n", replace_id, p->callid); - p->refer->refer_call = NULL; - transmit_response_reliable(p, "400 Bad request", req); /* The best way to not not accept the transfer */ - error = 1; - } - - if (!error && !p->refer->refer_call->owner) { - /* Oops, someting wrong anyway, no owner, no call */ - ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existing call id (%s)!\n", replace_id); - /* Check for better return code */ - transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replace)", req); - error = 1; - } - - if (!error && p->refer->refer_call->owner->_state != AST_STATE_RINGING && p->refer->refer_call->owner->_state != AST_STATE_RING && p->refer->refer_call->owner->_state != AST_STATE_UP ) { - ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id); - transmit_response_reliable(p, "603 Declined (Replaces)", req); - error = 1; - } - - if (error) { /* Give up this dialog */ - append_history(p, "Xfer", "INVITE/Replace Failed."); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - ast_mutex_unlock(&p->lock); - if (p->refer->refer_call) { - ast_mutex_unlock(&p->refer->refer_call->lock); - if (p->refer->refer_call->owner) { - ast_channel_unlock(p->refer->refer_call->owner); - } - } - p->invitestate = INV_COMPLETED; - return -1; - } - } - - - /* Check if this is an INVITE that sets up a new dialog or - a re-invite in an existing dialog */ - - if (!ast_test_flag(req, SIP_PKT_IGNORE)) { - int newcall = (p->initreq.headers ? TRUE : FALSE); - - if (sip_cancel_destroy(p)) - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - /* This also counts as a pending invite */ - p->pendinginvite = seqno; - check_via(p, req); - - copy_request(&p->initreq, req); /* Save this INVITE as the transaction basis */ - if (!p->owner) { /* Not a re-invite */ - if (debug) - ast_verbose("Using INVITE request as basis request - %s\n", p->callid); - if (newcall) - append_history(p, "Invite", "New call: %s", p->callid); - parse_ok_contact(p, req); - } else { /* Re-invite on existing call */ - ast_clear_flag(&p->flags[0], SIP_OUTGOING); /* This is now an inbound dialog */ - /* Handle SDP here if we already have an owner */ - if (find_sdp(req)) { - if (process_sdp(p, req)) { - transmit_response_reliable(p, "488 Not acceptable here", req); - if (!p->lastinvite) - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return -1; - } - } else { - p->jointcapability = p->capability; - if (option_debug > 2) - ast_log(LOG_DEBUG, "Hm.... No sdp for the moment\n"); - /* Some devices signal they want to be put off hold by sending a re-invite - *without* an SDP, which is supposed to mean "Go back to your state" - and since they put os on remote hold, we go back to off hold */ - if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) - change_hold_state(p, req, FALSE, 0); - } - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) /* This is a response, note what it was for */ - append_history(p, "ReInv", "Re-invite received"); - } - } else if (debug) - ast_verbose("Ignoring this INVITE request\n"); - - - if (!p->lastinvite && !ast_test_flag(req, SIP_PKT_IGNORE) && !p->owner) { - /* This is a new invite */ - /* Handle authentication if this is our first invite */ - res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin); - if (res == AUTH_CHALLENGE_SENT) { - p->invitestate = INV_COMPLETED; /* Needs to restart in another INVITE transaction */ - return 0; - } - if (res < 0) { /* Something failed in authentication */ - if (res == AUTH_FAKE_AUTH) { - ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From")); - transmit_fake_auth_response(p, req, 1); - } else { - ast_log(LOG_NOTICE, "Failed to authenticate user %s\n", get_header(req, "From")); - transmit_response_reliable(p, "403 Forbidden", req); - } - p->invitestate = INV_COMPLETED; - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - ast_string_field_free(p, theirtag); - return 0; - } - - /* We have a succesful authentication, process the SDP portion if there is one */ - if (find_sdp(req)) { - if (process_sdp(p, req)) { - /* Unacceptable codecs */ - transmit_response_reliable(p, "488 Not acceptable here", req); - p->invitestate = INV_COMPLETED; - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - if (option_debug) - ast_log(LOG_DEBUG, "No compatible codecs for this SIP call.\n"); - return -1; - } - } else { /* No SDP in invite, call control session */ - p->jointcapability = p->capability; - if (option_debug > 1) - ast_log(LOG_DEBUG, "No SDP in Invite, third party call control\n"); - } - - /* Queue NULL frame to prod ast_rtp_bridge if appropriate */ - /* This seems redundant ... see !p-owner above */ - if (p->owner) - ast_queue_frame(p->owner, &ast_null_frame); - - - /* Initialize the context if it hasn't been already */ - if (ast_strlen_zero(p->context)) - ast_string_field_set(p, context, default_context); - - - /* Check number of concurrent calls -vs- incoming limit HERE */ - if (option_debug) - ast_log(LOG_DEBUG, "Checking SIP call limits for device %s\n", p->username); - if ((res = update_call_counter(p, INC_CALL_LIMIT))) { - if (res < 0) { - ast_log(LOG_NOTICE, "Failed to place call for user %s, too many calls\n", p->username); - transmit_response_reliable(p, "480 Temporarily Unavailable (Call limit) ", req); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - p->invitestate = INV_COMPLETED; - } - return 0; - } - gotdest = get_destination(p, NULL); /* Get destination right away */ - get_rdnis(p, NULL); /* Get redirect information */ - extract_uri(p, req); /* Get the Contact URI */ - build_contact(p); /* Build our contact header */ - - if (p->rtp) { - ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); - ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); - } - - if (!replace_id && gotdest) { /* No matching extension found */ - if (gotdest == 1 && ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) - transmit_response_reliable(p, "484 Address Incomplete", req); - else { - char *decoded_exten = ast_strdupa(p->exten); - - transmit_response_reliable(p, "404 Not Found", req); - ast_uri_decode(decoded_exten); - ast_log(LOG_NOTICE, "Call from '%s' to extension" - " '%s' rejected because extension not found.\n", - S_OR(p->username, p->peername), decoded_exten); - } - p->invitestate = INV_COMPLETED; - update_call_counter(p, DEC_CALL_LIMIT); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return 0; - } else { - /* If no extension was specified, use the s one */ - /* Basically for calling to IP/Host name only */ - if (ast_strlen_zero(p->exten)) - ast_string_field_set(p, exten, "s"); - /* Initialize our tag */ - - make_our_tag(p->tag, sizeof(p->tag)); - /* First invitation - create the channel */ - c = sip_new(p, AST_STATE_DOWN, S_OR(p->username, NULL)); - *recount = 1; - - /* Save Record-Route for any later requests we make on this dialogue */ - build_route(p, req, 0); - - if (c) { - /* Pre-lock the call */ - ast_channel_lock(c); - } - } - } else { - if (option_debug > 1 && sipdebug) { - if (!ast_test_flag(req, SIP_PKT_IGNORE)) - ast_log(LOG_DEBUG, "Got a SIP re-invite for call %s\n", p->callid); - else - ast_log(LOG_DEBUG, "Got a SIP re-transmit of INVITE for call %s\n", p->callid); - } - if (!ast_test_flag(req, SIP_PKT_IGNORE)) - reinvite = 1; - c = p->owner; - } - - if (!ast_test_flag(req, SIP_PKT_IGNORE) && p) - p->lastinvite = seqno; - - if (replace_id) { /* Attended transfer or call pickup - we're the target */ - /* Go and take over the target call */ - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "Sending this call to the invite/replcaes handler %s\n", p->callid); - return handle_invite_replaces(p, req, debug, ast_test_flag(req, SIP_PKT_IGNORE), seqno, sin); - } - - - if (c) { /* We have a call -either a new call or an old one (RE-INVITE) */ - switch(c->_state) { - case AST_STATE_DOWN: - if (option_debug > 1) - ast_log(LOG_DEBUG, "%s: New call is still down.... Trying... \n", c->name); - transmit_response(p, "100 Trying", req); - p->invitestate = INV_PROCEEDING; - ast_setstate(c, AST_STATE_RING); - if (strcmp(p->exten, ast_pickup_ext())) { /* Call to extension -start pbx on this call */ - enum ast_pbx_result res; - - res = ast_pbx_start(c); - - switch(res) { - case AST_PBX_FAILED: - ast_log(LOG_WARNING, "Failed to start PBX :(\n"); - p->invitestate = INV_COMPLETED; - if (ast_test_flag(req, SIP_PKT_IGNORE)) - transmit_response(p, "503 Unavailable", req); - else - transmit_response_reliable(p, "503 Unavailable", req); - break; - case AST_PBX_CALL_LIMIT: - ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n"); - p->invitestate = INV_COMPLETED; - if (ast_test_flag(req, SIP_PKT_IGNORE)) - transmit_response(p, "480 Temporarily Unavailable", req); - else - transmit_response_reliable(p, "480 Temporarily Unavailable", req); - break; - case AST_PBX_SUCCESS: - /* nothing to do */ - break; - } - - if (res) { - - /* Unlock locks so ast_hangup can do its magic */ - ast_mutex_unlock(&c->lock); - ast_mutex_unlock(&p->lock); - ast_hangup(c); - ast_mutex_lock(&p->lock); - c = NULL; - } - } else { /* Pickup call in call group */ - ast_channel_unlock(c); - *nounlock = 1; - if (ast_pickup_call(c)) { - ast_log(LOG_NOTICE, "Nothing to pick up for %s\n", p->callid); - if (ast_test_flag(req, SIP_PKT_IGNORE)) - transmit_response(p, "503 Unavailable", req); /* OEJ - Right answer? */ - else - transmit_response_reliable(p, "503 Unavailable", req); - sip_alreadygone(p); - /* Unlock locks so ast_hangup can do its magic */ - ast_mutex_unlock(&p->lock); - c->hangupcause = AST_CAUSE_CALL_REJECTED; - } else { - ast_mutex_unlock(&p->lock); - ast_setstate(c, AST_STATE_DOWN); - c->hangupcause = AST_CAUSE_NORMAL_CLEARING; - } - p->invitestate = INV_COMPLETED; - ast_hangup(c); - ast_mutex_lock(&p->lock); - c = NULL; - } - break; - case AST_STATE_RING: - transmit_response(p, "100 Trying", req); - p->invitestate = INV_PROCEEDING; - break; - case AST_STATE_RINGING: - transmit_response(p, "180 Ringing", req); - p->invitestate = INV_PROCEEDING; - break; - case AST_STATE_UP: - if (option_debug > 1) - ast_log(LOG_DEBUG, "%s: This call is UP.... \n", c->name); - - transmit_response(p, "100 Trying", req); - - if (p->t38.state == T38_PEER_REINVITE) { - struct ast_channel *bridgepeer = NULL; - struct sip_pvt *bridgepvt = NULL; - - if ((bridgepeer = ast_bridged_channel(p->owner))) { - /* We have a bridge, and this is re-invite to switchover to T38 so we send re-invite with T38 SDP, to other side of bridge*/ - /*! XXX: we should also check here does the other side supports t38 at all !!! XXX */ - if (bridgepeer->tech == &sip_tech || bridgepeer->tech == &sip_tech_info) { - bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt; - if (bridgepvt->t38.state == T38_DISABLED) { - if (bridgepvt->udptl) { /* If everything is OK with other side's udptl struct */ - /* Send re-invite to the bridged channel */ - sip_handle_t38_reinvite(bridgepeer, p, 1); - } else { /* Something is wrong with peers udptl struct */ - ast_log(LOG_WARNING, "Strange... The other side of the bridge don't have udptl struct\n"); - ast_mutex_lock(&bridgepvt->lock); - bridgepvt->t38.state = T38_DISABLED; - ast_mutex_unlock(&bridgepvt->lock); - if (option_debug > 1) - ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n", bridgepvt->t38.state, bridgepeer->name); - if (ast_test_flag(req, SIP_PKT_IGNORE)) - transmit_response(p, "488 Not acceptable here", req); - else - transmit_response_reliable(p, "488 Not acceptable here", req); - - } - } else { - /* The other side is already setup for T.38 most likely so we need to acknowledge this too */ - ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - transmit_response_with_t38_sdp(p, "200 OK", req, XMIT_CRITICAL); - p->t38.state = T38_ENABLED; - if (option_debug) - ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); - } - } else { - /* Other side is not a SIP channel */ - if (ast_test_flag(req, SIP_PKT_IGNORE)) - transmit_response(p, "488 Not acceptable here", req); - else - transmit_response_reliable(p, "488 Not acceptable here", req); - p->t38.state = T38_DISABLED; - if (option_debug > 1) - ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); - - if (!p->lastinvite) /* Only destroy if this is *not* a re-invite */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - } else { - /* we are not bridged in a call */ - ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - transmit_response_with_t38_sdp(p, "200 OK", req, XMIT_CRITICAL); - p->t38.state = T38_ENABLED; - if (option_debug) - ast_log(LOG_DEBUG,"T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>"); - } - } else if (p->t38.state == T38_DISABLED) { /* Channel doesn't have T38 offered or enabled */ - int sendok = TRUE; - - /* If we are bridged to a channel that has T38 enabled than this is a case of RTP re-invite after T38 session */ - /* so handle it here (re-invite other party to RTP) */ - struct ast_channel *bridgepeer = NULL; - struct sip_pvt *bridgepvt = NULL; - if ((bridgepeer = ast_bridged_channel(p->owner))) { - if ((bridgepeer->tech == &sip_tech || bridgepeer->tech == &sip_tech_info) && !ast_check_hangup(bridgepeer)) { - bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt; - /* Does the bridged peer have T38 ? */ - if (bridgepvt->t38.state == T38_ENABLED) { - ast_log(LOG_WARNING, "RTP re-invite after T38 session not handled yet !\n"); - /* Insted of this we should somehow re-invite the other side of the bridge to RTP */ - if (ast_test_flag(req, SIP_PKT_IGNORE)) - transmit_response(p, "488 Not Acceptable Here (unsupported)", req); - else - transmit_response_reliable(p, "488 Not Acceptable Here (unsupported)", req); - sendok = FALSE; - } - /* No bridged peer with T38 enabled*/ - } - } - /* Respond to normal re-invite */ - if (sendok) { - /* If this is not a re-invite or something to ignore - it's critical */ - ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - transmit_response_with_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (ast_test_flag(req, SIP_PKT_IGNORE) ? XMIT_UNRELIABLE : XMIT_CRITICAL))); - } - } - p->invitestate = INV_TERMINATED; - break; - default: - ast_log(LOG_WARNING, "Don't know how to handle INVITE in state %d\n", c->_state); - transmit_response(p, "100 Trying", req); - break; - } - } else { - if (p && (p->autokillid == -1)) { - const char *msg; - - if (!p->jointcapability) - msg = "488 Not Acceptable Here (codec error)"; - else { - ast_log(LOG_NOTICE, "Unable to create/find SIP channel for this INVITE\n"); - msg = "503 Unavailable"; - } - if (ast_test_flag(req, SIP_PKT_IGNORE)) - transmit_response(p, msg, req); - else - transmit_response_reliable(p, msg, req); - p->invitestate = INV_COMPLETED; - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - } - return res; -} - -/*! \brief Find all call legs and bridge transferee with target - * called from handle_request_refer */ -static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno) -{ - struct sip_dual target; /* Chan 1: Call from tranferer to Asterisk */ - /* Chan 2: Call from Asterisk to target */ - int res = 0; - struct sip_pvt *targetcall_pvt; - - /* Check if the call ID of the replaces header does exist locally */ - if (!(targetcall_pvt = get_sip_pvt_byid_locked(transferer->refer->replaces_callid, transferer->refer->replaces_callid_totag, - transferer->refer->replaces_callid_fromtag))) { - if (transferer->refer->localtransfer) { - /* We did not find the refered call. Sorry, can't accept then */ - transmit_response(transferer, "202 Accepted", req); - /* Let's fake a response from someone else in order - to follow the standard */ - transmit_notify_with_sipfrag(transferer, seqno, "481 Call leg/transaction does not exist", TRUE); - append_history(transferer, "Xfer", "Refer failed"); - ast_clear_flag(&transferer->flags[0], SIP_GOTREFER); - transferer->refer->status = REFER_FAILED; - return -1; - } - /* Fall through for remote transfers that we did not find locally */ - if (option_debug > 2) - ast_log(LOG_DEBUG, "SIP attended transfer: Not our call - generating INVITE with replaces\n"); - return 0; - } - - /* Ok, we can accept this transfer */ - transmit_response(transferer, "202 Accepted", req); - append_history(transferer, "Xfer", "Refer accepted"); - if (!targetcall_pvt->owner) { /* No active channel */ - if (option_debug > 3) - ast_log(LOG_DEBUG, "SIP attended transfer: Error: No owner of target call\n"); - /* Cancel transfer */ - transmit_notify_with_sipfrag(transferer, seqno, "503 Service Unavailable", TRUE); - append_history(transferer, "Xfer", "Refer failed"); - ast_clear_flag(&transferer->flags[0], SIP_GOTREFER); - transferer->refer->status = REFER_FAILED; - ast_mutex_unlock(&targetcall_pvt->lock); - ast_channel_unlock(current->chan1); - return -1; - } - - /* We have a channel, find the bridge */ - target.chan1 = targetcall_pvt->owner; /* Transferer to Asterisk */ - target.chan2 = ast_bridged_channel(targetcall_pvt->owner); /* Asterisk to target */ - - if (!target.chan2 || !(target.chan2->_state == AST_STATE_UP || target.chan2->_state == AST_STATE_RINGING) ) { - /* Wrong state of new channel */ - if (option_debug > 3) { - if (target.chan2) - ast_log(LOG_DEBUG, "SIP attended transfer: Error: Wrong state of target call: %s\n", ast_state2str(target.chan2->_state)); - else if (target.chan1->_state != AST_STATE_RING) - ast_log(LOG_DEBUG, "SIP attended transfer: Error: No target channel\n"); - else - ast_log(LOG_DEBUG, "SIP attended transfer: Attempting transfer in ringing state\n"); - } - } - - /* Transfer */ - if (option_debug > 3 && sipdebug) { - if (current->chan2) /* We have two bridges */ - ast_log(LOG_DEBUG, "SIP attended transfer: trying to bridge %s and %s\n", target.chan1->name, current->chan2->name); - else /* One bridge, propably transfer of IVR/voicemail etc */ - ast_log(LOG_DEBUG, "SIP attended transfer: trying to make %s take over (masq) %s\n", target.chan1->name, current->chan1->name); - } - - ast_set_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */ - - /* Perform the transfer */ - res = attempt_transfer(current, &target); - ast_mutex_unlock(&targetcall_pvt->lock); - if (res) { - /* Failed transfer */ - transmit_notify_with_sipfrag(transferer, seqno, "486 Busy Here", TRUE); - append_history(transferer, "Xfer", "Refer failed"); - transferer->refer->status = REFER_FAILED; - if (targetcall_pvt->owner) - ast_channel_unlock(targetcall_pvt->owner); - /* Right now, we have to hangup, sorry. Bridge is destroyed */ - if (res != -2) - ast_hangup(transferer->owner); - else - ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER); - } else { - /* Transfer succeeded! */ - - /* Tell transferer that we're done. */ - transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE); - append_history(transferer, "Xfer", "Refer succeeded"); - transferer->refer->status = REFER_200OK; - if (targetcall_pvt->owner) { - if (option_debug) - ast_log(LOG_DEBUG, "SIP attended transfer: Unlocking channel %s\n", targetcall_pvt->owner->name); - ast_channel_unlock(targetcall_pvt->owner); - } - } - return 1; -} - - -/*! \brief Handle incoming REFER request */ -/*! \page SIP_REFER SIP transfer Support (REFER) - - REFER is used for call transfer in SIP. We get a REFER - to place a new call with an INVITE somwhere and then - keep the transferor up-to-date of the transfer. If the - transfer fails, get back on line with the orginal call. - - - REFER can be sent outside or inside of a dialog. - Asterisk only accepts REFER inside of a dialog. - - - If we get a replaces header, it is an attended transfer - - \par Blind transfers - The transferor provides the transferee - with the transfer targets contact. The signalling between - transferer or transferee should not be cancelled, so the - call is recoverable if the transfer target can not be reached - by the transferee. - - In this case, Asterisk receives a TRANSFER from - the transferor, thus is the transferee. We should - try to set up a call to the contact provided - and if that fails, re-connect the current session. - If the new call is set up, we issue a hangup. - In this scenario, we are following section 5.2 - in the SIP CC Transfer draft. (Transfer without - a GRUU) - - \par Transfer with consultation hold - In this case, the transferor - talks to the transfer target before the transfer takes place. - This is implemented with SIP hold and transfer. - Note: The invite From: string could indicate a transfer. - (Section 6. Transfer with consultation hold) - The transferor places the transferee on hold, starts a call - with the transfer target to alert them to the impending - transfer, terminates the connection with the target, then - proceeds with the transfer (as in Blind transfer above) - - \par Attended transfer - The transferor places the transferee - on hold, calls the transfer target to alert them, - places the target on hold, then proceeds with the transfer - using a Replaces header field in the Refer-to header. This - will force the transfee to send an Invite to the target, - with a replaces header that instructs the target to - hangup the call between the transferor and the target. - In this case, the Refer/to: uses the AOR address. (The same - URI that the transferee used to establish the session with - the transfer target (To: ). The Require: replaces header should - be in the INVITE to avoid the wrong UA in a forked SIP proxy - scenario to answer and have no call to replace with. - - The referred-by header is *NOT* required, but if we get it, - can be copied into the INVITE to the transfer target to - inform the target about the transferor - - "Any REFER request has to be appropriately authenticated.". - - We can't destroy dialogs, since we want the call to continue. - - */ -static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int debug, int ignore, int seqno, int *nounlock) -{ - struct sip_dual current; /* Chan1: Call between asterisk and transferer */ - /* Chan2: Call between asterisk and transferee */ - - int res = 0; - - if (ast_test_flag(req, SIP_PKT_DEBUG)) - ast_verbose("Call %s got a SIP call transfer from %s: (REFER)!\n", p->callid, ast_test_flag(&p->flags[0], SIP_OUTGOING) ? "callee" : "caller"); - - if (!p->owner) { - /* This is a REFER outside of an existing SIP dialog */ - /* We can't handle that, so decline it */ - if (option_debug > 2) - ast_log(LOG_DEBUG, "Call %s: Declined REFER, outside of dialog...\n", p->callid); - transmit_response(p, "603 Declined (No dialog)", req); - if (!ast_test_flag(req, SIP_PKT_IGNORE)) { - append_history(p, "Xfer", "Refer failed. Outside of dialog."); - sip_alreadygone(p); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - return 0; - } - - - /* Check if transfer is allowed from this device */ - if (p->allowtransfer == TRANSFER_CLOSED ) { - /* Transfer not allowed, decline */ - transmit_response(p, "603 Declined (policy)", req); - append_history(p, "Xfer", "Refer failed. Allowtransfer == closed."); - /* Do not destroy SIP session */ - return 0; - } - - if(!ignore && ast_test_flag(&p->flags[0], SIP_GOTREFER)) { - /* Already have a pending REFER */ - transmit_response(p, "491 Request pending", req); - append_history(p, "Xfer", "Refer failed. Request pending."); - return 0; - } - - /* Allocate memory for call transfer data */ - if (!p->refer && !sip_refer_allocate(p)) { - transmit_response(p, "500 Internal Server Error", req); - append_history(p, "Xfer", "Refer failed. Memory allocation error."); - return -3; - } - - res = get_refer_info(p, req); /* Extract headers */ - - p->refer->status = REFER_SENT; - - if (res != 0) { - switch (res) { - case -2: /* Syntax error */ - transmit_response(p, "400 Bad Request (Refer-to missing)", req); - append_history(p, "Xfer", "Refer failed. Refer-to missing."); - if (ast_test_flag(req, SIP_PKT_DEBUG) && option_debug) - ast_log(LOG_DEBUG, "SIP transfer to black hole can't be handled (no refer-to: )\n"); - break; - case -3: - transmit_response(p, "603 Declined (Non sip: uri)", req); - append_history(p, "Xfer", "Refer failed. Non SIP uri"); - if (ast_test_flag(req, SIP_PKT_DEBUG) && option_debug) - ast_log(LOG_DEBUG, "SIP transfer to non-SIP uri denied\n"); - break; - default: - /* Refer-to extension not found, fake a failed transfer */ - transmit_response(p, "202 Accepted", req); - append_history(p, "Xfer", "Refer failed. Bad extension."); - transmit_notify_with_sipfrag(p, seqno, "404 Not found", TRUE); - ast_clear_flag(&p->flags[0], SIP_GOTREFER); - if (ast_test_flag(req, SIP_PKT_DEBUG) && option_debug) - ast_log(LOG_DEBUG, "SIP transfer to bad extension: %s\n", p->refer->refer_to); - break; - } - return 0; - } - if (ast_strlen_zero(p->context)) - ast_string_field_set(p, context, default_context); - - /* If we do not support SIP domains, all transfers are local */ - if (allow_external_domains && check_sip_domain(p->refer->refer_to_domain, NULL, 0)) { - p->refer->localtransfer = 1; - if (sipdebug && option_debug > 2) - ast_log(LOG_DEBUG, "This SIP transfer is local : %s\n", p->refer->refer_to_domain); - } else if (AST_LIST_EMPTY(&domain_list) || check_sip_domain(p->refer->refer_to_domain, NULL, 0)) { - /* This PBX doesn't bother with SIP domains or domain is local, so this transfer is local */ - p->refer->localtransfer = 1; - } else if (sipdebug && option_debug > 2) - ast_log(LOG_DEBUG, "This SIP transfer is to a remote SIP extension (remote domain %s)\n", p->refer->refer_to_domain); - - /* Is this a repeat of a current request? Ignore it */ - /* Don't know what else to do right now. */ - if (ignore) - return res; - - /* If this is a blind transfer, we have the following - channels to work with: - - chan1, chan2: The current call between transferer and transferee (2 channels) - - target_channel: A new call from the transferee to the target (1 channel) - We need to stay tuned to what happens in order to be able - to bring back the call to the transferer */ - - /* If this is a attended transfer, we should have all call legs within reach: - - chan1, chan2: The call between the transferer and transferee (2 channels) - - target_channel, targetcall_pvt: The call between the transferer and the target (2 channels) - We want to bridge chan2 with targetcall_pvt! - - The replaces call id in the refer message points - to the call leg between Asterisk and the transferer. - So we need to connect the target and the transferee channel - and hangup the two other channels silently - - If the target is non-local, the call ID could be on a remote - machine and we need to send an INVITE with replaces to the - target. We basically handle this as a blind transfer - and let the sip_call function catch that we need replaces - header in the INVITE. - */ - - - /* Get the transferer's channel */ - current.chan1 = p->owner; - - /* Find the other part of the bridge (2) - transferee */ - current.chan2 = ast_bridged_channel(current.chan1); - - if (sipdebug && option_debug > 2) - ast_log(LOG_DEBUG, "SIP %s transfer: Transferer channel %s, transferee channel %s\n", p->refer->attendedtransfer ? "attended" : "blind", current.chan1->name, current.chan2 ? current.chan2->name : "<none>"); - - if (!current.chan2 && !p->refer->attendedtransfer) { - /* No bridged channel, propably IVR or echo or similar... */ - /* Guess we should masquerade or something here */ - /* Until we figure it out, refuse transfer of such calls */ - if (sipdebug && option_debug > 2) - ast_log(LOG_DEBUG,"Refused SIP transfer on non-bridged channel.\n"); - p->refer->status = REFER_FAILED; - append_history(p, "Xfer", "Refer failed. Non-bridged channel."); - transmit_response(p, "603 Declined", req); - return -1; - } - - if (current.chan2) { - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "Got SIP transfer, applying to bridged peer '%s'\n", current.chan2->name); - - ast_queue_control(current.chan1, AST_CONTROL_UNHOLD); - } - - ast_set_flag(&p->flags[0], SIP_GOTREFER); - - /* Attended transfer: Find all call legs and bridge transferee with target*/ - if (p->refer->attendedtransfer) { - if ((res = local_attended_transfer(p, ¤t, req, seqno))) - return res; /* We're done with the transfer */ - /* Fall through for remote transfers that we did not find locally */ - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "SIP attended transfer: Still not our call - generating INVITE with replaces\n"); - /* Fallthrough if we can't find the call leg internally */ - } - - - /* Parking a call */ - if (p->refer->localtransfer && !strcmp(p->refer->refer_to, ast_parking_ext())) { - /* Must release c's lock now, because it will not longer be accessible after the transfer! */ - *nounlock = 1; - ast_channel_unlock(current.chan1); - copy_request(¤t.req, req); - ast_clear_flag(&p->flags[0], SIP_GOTREFER); - p->refer->status = REFER_200OK; - append_history(p, "Xfer", "REFER to call parking."); - if (sipdebug && option_debug > 3) - ast_log(LOG_DEBUG, "SIP transfer to parking: trying to park %s. Parked by %s\n", current.chan2->name, current.chan1->name); - sip_park(current.chan2, current.chan1, req, seqno); - return res; - } - - /* Blind transfers and remote attended xfers */ - transmit_response(p, "202 Accepted", req); - - if (current.chan1 && current.chan2) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "chan1->name: %s\n", current.chan1->name); - pbx_builtin_setvar_helper(current.chan1, "BLINDTRANSFER", current.chan2->name); - } - if (current.chan2) { - pbx_builtin_setvar_helper(current.chan2, "BLINDTRANSFER", current.chan1->name); - pbx_builtin_setvar_helper(current.chan2, "SIPDOMAIN", p->refer->refer_to_domain); - pbx_builtin_setvar_helper(current.chan2, "SIPTRANSFER", "yes"); - /* One for the new channel */ - pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER", "yes"); - /* Attended transfer to remote host, prepare headers for the INVITE */ - if (p->refer->referred_by) - pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REFERER", p->refer->referred_by); - } - /* Generate a Replaces string to be used in the INVITE during attended transfer */ - if (p->refer->replaces_callid && !ast_strlen_zero(p->refer->replaces_callid)) { - char tempheader[SIPBUFSIZE]; - snprintf(tempheader, sizeof(tempheader), "%s%s%s%s%s", p->refer->replaces_callid, - p->refer->replaces_callid_totag ? ";to-tag=" : "", - p->refer->replaces_callid_totag, - p->refer->replaces_callid_fromtag ? ";from-tag=" : "", - p->refer->replaces_callid_fromtag); - if (current.chan2) - pbx_builtin_setvar_helper(current.chan2, "_SIPTRANSFER_REPLACES", tempheader); - } - /* Must release lock now, because it will not longer - be accessible after the transfer! */ - *nounlock = 1; - ast_channel_unlock(current.chan1); - - /* Connect the call */ - - /* FAKE ringing if not attended transfer */ - if (!p->refer->attendedtransfer) - transmit_notify_with_sipfrag(p, seqno, "183 Ringing", FALSE); - - /* For blind transfer, this will lead to a new call */ - /* For attended transfer to remote host, this will lead to - a new SIP call with a replaces header, if the dial plan allows it - */ - if (!current.chan2) { - /* We have no bridge, so we're talking with Asterisk somehow */ - /* We need to masquerade this call */ - /* What to do to fix this situation: - * Set up the new call in a new channel - * Let the new channel masq into this channel - Please add that code here :-) - */ - p->refer->status = REFER_FAILED; - transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable (can't handle one-legged xfers)", TRUE); - ast_clear_flag(&p->flags[0], SIP_GOTREFER); - append_history(p, "Xfer", "Refer failed (only bridged calls)."); - return -1; - } - ast_set_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Delay hangup */ - - /* For blind transfers, move the call to the new extensions. For attended transfers on multiple - servers - generate an INVITE with Replaces. Either way, let the dial plan decided */ - res = ast_async_goto(current.chan2, p->refer->refer_to_context, p->refer->refer_to, 1); - - if (!res) { - /* Success - we have a new channel */ - if (option_debug > 2) - ast_log(LOG_DEBUG, "%s transfer succeeded. Telling transferer.\n", p->refer->attendedtransfer? "Attended" : "Blind"); - transmit_notify_with_sipfrag(p, seqno, "200 Ok", TRUE); - if (p->refer->localtransfer) - p->refer->status = REFER_200OK; - if (p->owner) - p->owner->hangupcause = AST_CAUSE_NORMAL_CLEARING; - append_history(p, "Xfer", "Refer succeeded."); - ast_clear_flag(&p->flags[0], SIP_GOTREFER); - /* Do not hangup call, the other side do that when we say 200 OK */ - /* We could possibly implement a timer here, auto congestion */ - res = 0; - } else { - ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Don't delay hangup */ - if (option_debug > 2) - ast_log(LOG_DEBUG, "%s transfer failed. Resuming original call.\n", p->refer->attendedtransfer? "Attended" : "Blind"); - append_history(p, "Xfer", "Refer failed."); - /* Failure of some kind */ - p->refer->status = REFER_FAILED; - transmit_notify_with_sipfrag(p, seqno, "503 Service Unavailable", TRUE); - ast_clear_flag(&p->flags[0], SIP_GOTREFER); - res = -1; - } - return res; -} - -/*! \brief Handle incoming CANCEL request */ -static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req) -{ - - check_via(p, req); - sip_alreadygone(p); - - /* At this point, we could have cancelled the invite at the same time - as the other side sends a CANCEL. Our final reply with error code - might not have been received by the other side before the CANCEL - was sent, so let's just give up retransmissions and waiting for - ACK on our error code. The call is hanging up any way. */ - if (p->invitestate == INV_TERMINATED) - __sip_pretend_ack(p); - else - p->invitestate = INV_CANCELLED; - - if (p->owner && p->owner->_state == AST_STATE_UP) { - /* This call is up, cancel is ignored, we need a bye */ - transmit_response(p, "200 OK", req); - if (option_debug) - ast_log(LOG_DEBUG, "Got CANCEL on an answered call. Ignoring... \n"); - return 0; - } - - if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) - update_call_counter(p, DEC_CALL_LIMIT); - - stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */ - if (p->owner) - ast_queue_hangup(p->owner); - else - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - if (p->initreq.len > 0) { - transmit_response_reliable(p, "487 Request Terminated", &p->initreq); - transmit_response(p, "200 OK", req); - return 1; - } else { - transmit_response(p, "481 Call Leg Does Not Exist", req); - return 0; - } -} - -static int acf_channel_read(struct ast_channel *chan, char *funcname, char *preparse, char *buf, size_t buflen) -{ - struct ast_rtp_quality qos; - struct sip_pvt *p = chan->tech_pvt; - char *all = "", *parse = ast_strdupa(preparse); - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(param); - AST_APP_ARG(type); - AST_APP_ARG(field); - ); - AST_STANDARD_APP_ARGS(args, parse); - - /* Sanity check */ - if (chan->tech != &sip_tech && chan->tech != &sip_tech_info) { - ast_log(LOG_ERROR, "Cannot call %s on a non-SIP channel\n", funcname); - return 0; - } - - if (strcasecmp(args.param, "rtpqos")) - return 0; - - /* Default arguments of audio,all */ - if (ast_strlen_zero(args.type)) - args.type = "audio"; - if (ast_strlen_zero(args.field)) - args.field = "all"; - - memset(buf, 0, buflen); - memset(&qos, 0, sizeof(qos)); - - if (strcasecmp(args.type, "AUDIO") == 0) { - all = ast_rtp_get_quality(p->rtp, &qos); - } else if (strcasecmp(args.type, "VIDEO") == 0) { - all = ast_rtp_get_quality(p->vrtp, &qos); - } - - if (strcasecmp(args.field, "local_ssrc") == 0) - snprintf(buf, buflen, "%u", qos.local_ssrc); - else if (strcasecmp(args.field, "local_lostpackets") == 0) - snprintf(buf, buflen, "%u", qos.local_lostpackets); - else if (strcasecmp(args.field, "local_jitter") == 0) - snprintf(buf, buflen, "%.0lf", qos.local_jitter * 1000.0); - else if (strcasecmp(args.field, "local_count") == 0) - snprintf(buf, buflen, "%u", qos.local_count); - else if (strcasecmp(args.field, "remote_ssrc") == 0) - snprintf(buf, buflen, "%u", qos.remote_ssrc); - else if (strcasecmp(args.field, "remote_lostpackets") == 0) - snprintf(buf, buflen, "%u", qos.remote_lostpackets); - else if (strcasecmp(args.field, "remote_jitter") == 0) - snprintf(buf, buflen, "%.0lf", qos.remote_jitter * 1000.0); - else if (strcasecmp(args.field, "remote_count") == 0) - snprintf(buf, buflen, "%u", qos.remote_count); - else if (strcasecmp(args.field, "rtt") == 0) - snprintf(buf, buflen, "%.0lf", qos.rtt * 1000.0); - else if (strcasecmp(args.field, "all") == 0) - ast_copy_string(buf, all, buflen); - else { - ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname); - return -1; - } - return 0; -} - -/*! \brief Handle incoming BYE request */ -static int handle_request_bye(struct sip_pvt *p, struct sip_request *req) -{ - struct ast_channel *c=NULL; - int res; - struct ast_channel *bridged_to; - - /* If we have an INCOMING invite that we haven't answered, terminate that transaction */ - if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !ast_test_flag(req, SIP_PKT_IGNORE) && !p->owner) - transmit_response_reliable(p, "487 Request Terminated", &p->initreq); - - __sip_pretend_ack(p); - - p->invitestate = INV_TERMINATED; - - copy_request(&p->initreq, req); - check_via(p, req); - sip_alreadygone(p); - - /* Get RTCP quality before end of call */ - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY) || p->owner) { - char *audioqos, *videoqos; - if (p->rtp) { - audioqos = ast_rtp_get_quality(p->rtp, NULL); - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) - append_history(p, "RTCPaudio", "Quality:%s", audioqos); - if (p->owner) - pbx_builtin_setvar_helper(p->owner, "RTPAUDIOQOS", audioqos); - } - if (p->vrtp) { - videoqos = ast_rtp_get_quality(p->vrtp, NULL); - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) - append_history(p, "RTCPvideo", "Quality:%s", videoqos); - if (p->owner) - pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos); - } - } - - stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */ - - if (!ast_strlen_zero(get_header(req, "Also"))) { - ast_log(LOG_NOTICE, "Client '%s' using deprecated BYE/Also transfer method. Ask vendor to support REFER instead\n", - ast_inet_ntoa(p->recv.sin_addr)); - if (ast_strlen_zero(p->context)) - ast_string_field_set(p, context, default_context); - res = get_also_info(p, req); - if (!res) { - c = p->owner; - if (c) { - bridged_to = ast_bridged_channel(c); - if (bridged_to) { - /* Don't actually hangup here... */ - ast_queue_control(c, AST_CONTROL_UNHOLD); - ast_async_goto(bridged_to, p->context, p->refer->refer_to,1); - } else - ast_queue_hangup(p->owner); - } - } else { - ast_log(LOG_WARNING, "Invalid transfer information from '%s'\n", ast_inet_ntoa(p->recv.sin_addr)); - if (p->owner) - ast_queue_hangup(p->owner); - } - } else if (p->owner) { - ast_queue_hangup(p->owner); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Received bye, issuing owner hangup\n"); - } else { - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Received bye, no owner, selfdestruct soon.\n"); - } - ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - transmit_response(p, "200 OK", req); - - return 1; -} - -/*! \brief Handle incoming MESSAGE request */ -static int handle_request_message(struct sip_pvt *p, struct sip_request *req) -{ - if (!ast_test_flag(req, SIP_PKT_IGNORE)) { - if (ast_test_flag(req, SIP_PKT_DEBUG)) - ast_verbose("Receiving message!\n"); - receive_message(p, req); - } else - transmit_response(p, "202 Accepted", req); - return 1; -} - -/*! \brief Handle incoming SUBSCRIBE request */ -static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e) -{ - int gotdest; - int res = 0; - int firststate = AST_EXTENSION_REMOVED; - struct sip_peer *authpeer = NULL; - const char *eventheader = get_header(req, "Event"); /* Get Event package name */ - const char *accept = get_header(req, "Accept"); - int resubscribe = (p->subscribed != NONE); - char *temp, *event; - - if (p->initreq.headers) { - /* We already have a dialog */ - if (p->initreq.method != SIP_SUBSCRIBE) { - /* This is a SUBSCRIBE within another SIP dialog, which we do not support */ - /* For transfers, this could happen, but since we haven't seen it happening, let us just refuse this */ - transmit_response(p, "403 Forbidden (within dialog)", req); - /* Do not destroy session, since we will break the call if we do */ - if (option_debug) - ast_log(LOG_DEBUG, "Got a subscription within the context of another call, can't handle that - %s (Method %s)\n", p->callid, sip_methods[p->initreq.method].text); - return 0; - } else if (ast_test_flag(req, SIP_PKT_DEBUG)) { - if (option_debug) { - if (resubscribe) - ast_log(LOG_DEBUG, "Got a re-subscribe on existing subscription %s\n", p->callid); - else - ast_log(LOG_DEBUG, "Got a new subscription %s (possibly with auth)\n", p->callid); - } - } - } - - /* Check if we have a global disallow setting on subscriptions. - if so, we don't have to check peer/user settings after auth, which saves a lot of processing - */ - if (!global_allowsubscribe) { - transmit_response(p, "403 Forbidden (policy)", req); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - return 0; - } - - if (!ast_test_flag(req, SIP_PKT_IGNORE) && !resubscribe) { /* Set up dialog, new subscription */ - const char *to = get_header(req, "To"); - char totag[128]; - - /* Check to see if a tag was provided, if so this is actually a resubscription of a dialog we no longer know about */ - if (!ast_strlen_zero(to) && gettag(req, "To", totag, sizeof(totag))) { - if (ast_test_flag(req, SIP_PKT_DEBUG)) - ast_verbose("Received resubscription for a dialog we no longer know about. Telling remote side to subscribe again.\n"); - transmit_response(p, "481 Subscription does not exist", req); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - return 0; - } - - /* Use this as the basis */ - if (ast_test_flag(req, SIP_PKT_DEBUG)) - ast_verbose("Creating new subscription\n"); - - copy_request(&p->initreq, req); - check_via(p, req); - } else if (ast_test_flag(req, SIP_PKT_DEBUG) && ast_test_flag(req, SIP_PKT_IGNORE)) - ast_verbose("Ignoring this SUBSCRIBE request\n"); - - /* Find parameters to Event: header value and remove them for now */ - if (ast_strlen_zero(eventheader)) { - transmit_response(p, "489 Bad Event", req); - if (option_debug > 1) - ast_log(LOG_DEBUG, "Received SIP subscribe for unknown event package: <none>\n"); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - return 0; - } - - if ( (strchr(eventheader, ';'))) { - event = ast_strdupa(eventheader); /* Since eventheader is a const, we can't change it */ - temp = strchr(event, ';'); - *temp = '\0'; /* Remove any options for now */ - /* We might need to use them later :-) */ - } else - event = (char *) eventheader; /* XXX is this legal ? */ - - /* Handle authentication */ - res = check_user_full(p, req, SIP_SUBSCRIBE, e, 0, sin, &authpeer); - /* if an authentication response was sent, we are done here */ - if (res == AUTH_CHALLENGE_SENT) { - if (authpeer) - ASTOBJ_UNREF(authpeer, sip_destroy_peer); - return 0; - } - if (res < 0) { - if (res == AUTH_FAKE_AUTH) { - ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From")); - transmit_fake_auth_response(p, req, 1); - } else { - ast_log(LOG_NOTICE, "Failed to authenticate user %s for SUBSCRIBE\n", get_header(req, "From")); - transmit_response_reliable(p, "403 Forbidden", req); - } - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - if (authpeer) - ASTOBJ_UNREF(authpeer, sip_destroy_peer); - return 0; - } - - /* Check if this user/peer is allowed to subscribe at all */ - if (!ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) { - transmit_response(p, "403 Forbidden (policy)", req); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - if (authpeer) - ASTOBJ_UNREF(authpeer, sip_destroy_peer); - return 0; - } - - /* Get destination right away */ - gotdest = get_destination(p, NULL); - - /* Get full contact header - this needs to be used as a request URI in NOTIFY's */ - parse_ok_contact(p, req); - - build_contact(p); - if (gotdest) { - transmit_response(p, "404 Not Found", req); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - if (authpeer) - ASTOBJ_UNREF(authpeer, sip_destroy_peer); - return 0; - } - - /* Initialize tag for new subscriptions */ - if (ast_strlen_zero(p->tag)) - make_our_tag(p->tag, sizeof(p->tag)); - - if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */ - if (authpeer) /* No need for authpeer here */ - ASTOBJ_UNREF(authpeer, sip_destroy_peer); - - /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */ - /* Polycom phones only handle xpidf+xml, even if they say they can - handle pidf+xml as well - */ - if (strstr(p->useragent, "Polycom")) { - p->subscribed = XPIDF_XML; - } else if (strstr(accept, "application/pidf+xml")) { - p->subscribed = PIDF_XML; /* RFC 3863 format */ - } else if (strstr(accept, "application/dialog-info+xml")) { - p->subscribed = DIALOG_INFO_XML; - /* IETF draft: draft-ietf-sipping-dialog-package-05.txt */ - } else if (strstr(accept, "application/cpim-pidf+xml")) { - p->subscribed = CPIM_PIDF_XML; /* RFC 3863 format */ - } else if (strstr(accept, "application/xpidf+xml")) { - p->subscribed = XPIDF_XML; /* Early pre-RFC 3863 format with MSN additions (Microsoft Messenger) */ - } else if (ast_strlen_zero(accept)) { - if (p->subscribed == NONE) { /* if the subscribed field is not already set, and there is no accept header... */ - transmit_response(p, "489 Bad Event", req); - - ast_log(LOG_WARNING,"SUBSCRIBE failure: no Accept header: pvt: stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n", - p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - return 0; - } - /* if p->subscribed is non-zero, then accept is not obligatory; according to rfc 3265 section 3.1.3, at least. - so, we'll just let it ride, keeping the value from a previous subscription, and not abort the subscription */ - } else { - /* Can't find a format for events that we know about */ - char mybuf[200]; - snprintf(mybuf,sizeof(mybuf),"489 Bad Event (format %s)", accept); - transmit_response(p, mybuf, req); - - ast_log(LOG_WARNING,"SUBSCRIBE failure: unrecognized format: '%s' pvt: subscribed: %d, stateid: %d, laststate: %d, dialogver: %d, subscribecont: '%s', subscribeuri: '%s'\n", - accept, (int)p->subscribed, p->stateid, p->laststate, p->dialogver, p->subscribecontext, p->subscribeuri); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - return 0; - } - } else if (!strcmp(event, "message-summary")) { - if (!ast_strlen_zero(accept) && strcmp(accept, "application/simple-message-summary")) { - /* Format requested that we do not support */ - transmit_response(p, "406 Not Acceptable", req); - if (option_debug > 1) - ast_log(LOG_DEBUG, "Received SIP mailbox subscription for unknown format: %s\n", accept); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - if (authpeer) /* No need for authpeer here */ - ASTOBJ_UNREF(authpeer, sip_destroy_peer); - return 0; - } - /* Looks like they actually want a mailbox status - This version of Asterisk supports mailbox subscriptions - The subscribed URI needs to exist in the dial plan - In most devices, this is configurable to the voicemailmain extension you use - */ - if (!authpeer || ast_strlen_zero(authpeer->mailbox)) { - transmit_response(p, "404 Not found (no mailbox)", req); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", authpeer->name); - if (authpeer) /* No need for authpeer here */ - ASTOBJ_UNREF(authpeer, sip_destroy_peer); - return 0; - } - - p->subscribed = MWI_NOTIFICATION; - if (authpeer->mwipvt && authpeer->mwipvt != p) /* Destroy old PVT if this is a new one */ - /* We only allow one subscription per peer */ - sip_destroy(authpeer->mwipvt); - authpeer->mwipvt = p; /* Link from peer to pvt */ - p->relatedpeer = ASTOBJ_REF(authpeer); /* Link from pvt to peer */ - } else { /* At this point, Asterisk does not understand the specified event */ - transmit_response(p, "489 Bad Event", req); - if (option_debug > 1) - ast_log(LOG_DEBUG, "Received SIP subscribe for unknown event package: %s\n", event); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - if (authpeer) /* No need for authpeer here */ - ASTOBJ_UNREF(authpeer, sip_destroy_peer); - return 0; - } - - if (p->subscribed != MWI_NOTIFICATION && !resubscribe) { - if (p->stateid > -1) - ast_extension_state_del(p->stateid, cb_extensionstate); - p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p); - } - - if (!ast_test_flag(req, SIP_PKT_IGNORE) && p) - p->lastinvite = seqno; - if (p && !ast_test_flag(&p->flags[0], SIP_NEEDDESTROY)) { - p->expiry = atoi(get_header(req, "Expires")); - - /* check if the requested expiry-time is within the approved limits from sip.conf */ - if (p->expiry > max_expiry) - p->expiry = max_expiry; - if (p->expiry < min_expiry && p->expiry > 0) - p->expiry = min_expiry; - - if (sipdebug || option_debug > 1) { - if (p->subscribed == MWI_NOTIFICATION && p->relatedpeer) - ast_log(LOG_DEBUG, "Adding subscription for mailbox notification - peer %s Mailbox %s\n", p->relatedpeer->name, p->relatedpeer->mailbox); - else - ast_log(LOG_DEBUG, "Adding subscription for extension %s context %s for peer %s\n", p->exten, p->context, p->username); - } - if (p->autokillid > -1 && sip_cancel_destroy(p)) /* Remove subscription expiry for renewals */ - ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); - if (p->expiry > 0) - sip_scheddestroy(p, (p->expiry + 10) * 1000); /* Set timer for destruction of call at expiration */ - - if (p->subscribed == MWI_NOTIFICATION) { - ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - transmit_response(p, "200 OK", req); - if (p->relatedpeer) { /* Send first notification */ - ASTOBJ_WRLOCK(p->relatedpeer); - sip_send_mwi_to_peer(p->relatedpeer); - ASTOBJ_UNLOCK(p->relatedpeer); - } - } else { - struct sip_pvt *p_old; - - if ((firststate = ast_extension_state(NULL, p->context, p->exten)) < 0) { - - ast_log(LOG_NOTICE, "Got SUBSCRIBE for extension %s@%s from %s, but there is no hint for that extension.\n", p->exten, p->context, ast_inet_ntoa(p->sa.sin_addr)); - transmit_response(p, "404 Not found", req); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - return 0; - } - ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); - transmit_response(p, "200 OK", req); - transmit_state_notify(p, firststate, 1, FALSE); /* Send first notification */ - append_history(p, "Subscribestatus", "%s", ast_extension_state2str(firststate)); - /* hide the 'complete' exten/context in the refer_to field for later display */ - ast_string_field_build(p, subscribeuri, "%s@%s", p->exten, p->context); - - /* remove any old subscription from this peer for the same exten/context, - as the peer has obviously forgotten about it and it's wasteful to wait - for it to expire and send NOTIFY messages to the peer only to have them - ignored (or generate errors) - */ - ast_mutex_lock(&iflock); - for (p_old = iflist; p_old; p_old = p_old->next) { - if (p_old == p) - continue; - if (p_old->initreq.method != SIP_SUBSCRIBE) - continue; - if (p_old->subscribed == NONE) - continue; - ast_mutex_lock(&p_old->lock); - if (!strcmp(p_old->username, p->username)) { - if (!strcmp(p_old->exten, p->exten) && - !strcmp(p_old->context, p->context)) { - ast_set_flag(&p_old->flags[0], SIP_NEEDDESTROY); - ast_mutex_unlock(&p_old->lock); - break; - } - } - ast_mutex_unlock(&p_old->lock); - } - ast_mutex_unlock(&iflock); - } - if (!p->expiry) - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - } - return 1; -} - -/*! \brief Handle incoming REGISTER request */ -static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, char *e) -{ - enum check_auth_result res; - - /* Use this as the basis */ - if (ast_test_flag(req, SIP_PKT_DEBUG)) - ast_verbose("Using latest REGISTER request as basis request\n"); - copy_request(&p->initreq, req); - check_via(p, req); - if ((res = register_verify(p, sin, req, e)) < 0) { - const char *reason; - - switch (res) { - case AUTH_SECRET_FAILED: - reason = "Wrong password"; - break; - case AUTH_USERNAME_MISMATCH: - reason = "Username/auth name mismatch"; - break; - case AUTH_NOT_FOUND: - reason = "No matching peer found"; - break; - case AUTH_UNKNOWN_DOMAIN: - reason = "Not a local domain"; - break; - case AUTH_PEER_NOT_DYNAMIC: - reason = "Peer is not supposed to register"; - break; - case AUTH_ACL_FAILED: - reason = "Device does not match ACL"; - break; - default: - reason = "Unknown failure"; - break; - } - ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s' - %s\n", - get_header(req, "To"), ast_inet_ntoa(sin->sin_addr), - reason); - append_history(p, "RegRequest", "Failed : Account %s : %s", get_header(req, "To"), reason); - } else - append_history(p, "RegRequest", "Succeeded : Account %s", get_header(req, "To")); - - if (res < 1) { - /* Destroy the session, but keep us around for just a bit in case they don't - get our 200 OK */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - return res; -} - -/*! \brief Handle incoming SIP requests (methods) -\note This is where all incoming requests go first */ -/* called with p and p->owner locked */ -static int handle_request(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int *recount, int *nounlock) -{ - /* Called with p->lock held, as well as p->owner->lock if appropriate, keeping things - relatively static */ - const char *cmd; - const char *cseq; - const char *useragent; - int seqno; - int len; - int ignore = FALSE; - int respid; - int res = 0; - int debug = sip_debug_test_pvt(p); - char *e; - int error = 0; - - /* Get Method and Cseq */ - cseq = get_header(req, "Cseq"); - cmd = req->header[0]; - - /* Must have Cseq */ - if (ast_strlen_zero(cmd) || ast_strlen_zero(cseq)) { - ast_log(LOG_ERROR, "Missing Cseq. Dropping this SIP message, it's incomplete.\n"); - error = 1; - } - if (!error && sscanf(cseq, "%d%n", &seqno, &len) != 1) { - ast_log(LOG_ERROR, "No seqno in '%s'. Dropping incomplete message.\n", cmd); - error = 1; - } - if (error) { - if (!p->initreq.headers) /* New call */ - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); /* Make sure we destroy this dialog */ - return -1; - } - /* Get the command XXX */ - - cmd = req->rlPart1; - e = req->rlPart2; - - /* Save useragent of the client */ - useragent = get_header(req, "User-Agent"); - if (!ast_strlen_zero(useragent)) - ast_string_field_set(p, useragent, useragent); - - /* Find out SIP method for incoming request */ - if (req->method == SIP_RESPONSE) { /* Response to our request */ - /* Response to our request -- Do some sanity checks */ - if (!p->initreq.headers) { - if (option_debug) - ast_log(LOG_DEBUG, "That's odd... Got a response on a call we dont know about. Cseq %d Cmd %s\n", seqno, cmd); - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - return 0; - } else if (p->ocseq && (p->ocseq < seqno) && (seqno != p->lastnoninvite)) { - if (option_debug) - ast_log(LOG_DEBUG, "Ignoring out of order response %d (expecting %d)\n", seqno, p->ocseq); - return -1; - } else if (p->ocseq && (p->ocseq != seqno) && (seqno != p->lastnoninvite)) { - /* ignore means "don't do anything with it" but still have to - respond appropriately */ - ignore = TRUE; - ast_set_flag(req, SIP_PKT_IGNORE); - ast_set_flag(req, SIP_PKT_IGNORE_RESP); - append_history(p, "Ignore", "Ignoring this retransmit\n"); - } else if (e) { - e = ast_skip_blanks(e); - if (sscanf(e, "%d %n", &respid, &len) != 1) { - ast_log(LOG_WARNING, "Invalid response: '%s'\n", e); - } else { - if (respid <= 0) { - ast_log(LOG_WARNING, "Invalid SIP response code: '%d'\n", respid); - return 0; - } - /* More SIP ridiculousness, we have to ignore bogus contacts in 100 etc responses */ - if ((respid == 200) || ((respid >= 300) && (respid <= 399))) - extract_uri(p, req); - handle_response(p, respid, e + len, req, ignore, seqno); - } - } - return 0; - } - - /* New SIP request coming in - (could be new request in existing SIP dialog as well...) - */ - - p->method = req->method; /* Find out which SIP method they are using */ - if (option_debug > 3) - ast_log(LOG_DEBUG, "**** Received %s (%d) - Command in SIP %s\n", sip_methods[p->method].text, sip_methods[p->method].id, cmd); - - if (p->icseq && (p->icseq > seqno) ) { - if (p->pendinginvite && seqno == p->pendinginvite && (req->method == SIP_ACK || req->method == SIP_CANCEL)) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Got CANCEL or ACK on INVITE with transactions in between.\n"); - } else { - if (option_debug) - ast_log(LOG_DEBUG, "Ignoring too old SIP packet packet %d (expecting >= %d)\n", seqno, p->icseq); - if (req->method != SIP_ACK) - transmit_response(p, "503 Server error", req); /* We must respond according to RFC 3261 sec 12.2 */ - return -1; - } - } else if (p->icseq && - p->icseq == seqno && - req->method != SIP_ACK && - (p->method != SIP_CANCEL || ast_test_flag(&p->flags[0], SIP_ALREADYGONE))) { - /* ignore means "don't do anything with it" but still have to - respond appropriately. We do this if we receive a repeat of - the last sequence number */ - ignore = 2; - ast_set_flag(req, SIP_PKT_IGNORE); - ast_set_flag(req, SIP_PKT_IGNORE_REQ); - if (option_debug > 2) - ast_log(LOG_DEBUG, "Ignoring SIP message because of retransmit (%s Seqno %d, ours %d)\n", sip_methods[p->method].text, p->icseq, seqno); - } - - if (seqno >= p->icseq) - /* Next should follow monotonically (but not necessarily - incrementally -- thanks again to the genius authors of SIP -- - increasing */ - p->icseq = seqno; - - /* Find their tag if we haven't got it */ - if (ast_strlen_zero(p->theirtag)) { - char tag[128]; - - gettag(req, "From", tag, sizeof(tag)); - ast_string_field_set(p, theirtag, tag); - } - snprintf(p->lastmsg, sizeof(p->lastmsg), "Rx: %s", cmd); - - if (pedanticsipchecking) { - /* If this is a request packet without a from tag, it's not - correct according to RFC 3261 */ - /* Check if this a new request in a new dialog with a totag already attached to it, - RFC 3261 - section 12.2 - and we don't want to mess with recovery */ - if (!p->initreq.headers && ast_test_flag(req, SIP_PKT_WITH_TOTAG)) { - /* If this is a first request and it got a to-tag, it is not for us */ - if (!ast_test_flag(req, SIP_PKT_IGNORE) && req->method == SIP_INVITE) { - transmit_response_reliable(p, "481 Call/Transaction Does Not Exist", req); - /* Will cease to exist after ACK */ - } else if (req->method != SIP_ACK) { - transmit_response(p, "481 Call/Transaction Does Not Exist", req); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - return res; - } - } - - if (!e && (p->method == SIP_INVITE || p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER || p->method == SIP_NOTIFY)) { - transmit_response(p, "400 Bad request", req); - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - return -1; - } - - /* Handle various incoming SIP methods in requests */ - switch (p->method) { - case SIP_OPTIONS: - res = handle_request_options(p, req); - break; - case SIP_INVITE: - res = handle_request_invite(p, req, debug, seqno, sin, recount, e, nounlock); - break; - case SIP_REFER: - res = handle_request_refer(p, req, debug, ignore, seqno, nounlock); - break; - case SIP_CANCEL: - res = handle_request_cancel(p, req); - break; - case SIP_BYE: - res = handle_request_bye(p, req); - break; - case SIP_MESSAGE: - res = handle_request_message(p, req); - break; - case SIP_SUBSCRIBE: - res = handle_request_subscribe(p, req, sin, seqno, e); - break; - case SIP_REGISTER: - res = handle_request_register(p, req, sin, e); - break; - case SIP_INFO: - if (ast_test_flag(req, SIP_PKT_DEBUG)) - ast_verbose("Receiving INFO!\n"); - if (!ignore) - handle_request_info(p, req); - else /* if ignoring, transmit response */ - transmit_response(p, "200 OK", req); - break; - case SIP_NOTIFY: - res = handle_request_notify(p, req, sin, seqno, e); - break; - case SIP_ACK: - /* Make sure we don't ignore this */ - if (seqno == p->pendinginvite) { - p->invitestate = INV_TERMINATED; - p->pendinginvite = 0; - __sip_ack(p, seqno, FLAG_RESPONSE, 0); - if (find_sdp(req)) { - if (process_sdp(p, req)) - return -1; - } - check_pendings(p); - } - /* Got an ACK that we did not match. Ignore silently */ - if (!p->lastinvite && ast_strlen_zero(p->randdata)) - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - break; - default: - transmit_response_with_allow(p, "501 Method Not Implemented", req, 0); - ast_log(LOG_NOTICE, "Unknown SIP command '%s' from '%s'\n", - cmd, ast_inet_ntoa(p->sa.sin_addr)); - /* If this is some new method, and we don't have a call, destroy it now */ - if (!p->initreq.headers) - ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); - break; - } - return res; -} - -static void process_request_queue(struct sip_pvt *p, int *recount, int *nounlock) -{ - struct sip_request *req; - - while ((req = AST_LIST_REMOVE_HEAD(&p->request_queue, next))) { - if (handle_request(p, req, &p->recv, recount, nounlock) == -1) { - /* Request failed */ - if (option_debug) { - ast_log(LOG_DEBUG, "SIP message could not be handled, bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>"); - } - } - ast_free(req); - } -} - -static int scheduler_process_request_queue(const void *data) -{ - struct sip_pvt *p = (struct sip_pvt *) data; - int recount = 0; - int nounlock = 0; - int lockretry; - - for (lockretry = 10; lockretry > 0; lockretry--) { - ast_mutex_lock(&p->lock); - - /* lock the owner if it has one -- we may need it */ - /* because this is deadlock-prone, we need to try and unlock if failed */ - if (!p->owner || !ast_channel_trylock(p->owner)) { - break; /* locking succeeded */ - } - - if (lockretry != 1) { - ast_mutex_unlock(&p->lock); - /* Sleep for a very short amount of time */ - usleep(1); - } - } - - if (!lockretry) { - int retry = !AST_LIST_EMPTY(&p->request_queue); - - /* we couldn't get the owner lock, which is needed to process - the queued requests, so return a non-zero value, which will - cause the scheduler to run this request again later if there - still requests to be processed - */ - ast_mutex_unlock(&p->lock); - return retry; - }; - - process_request_queue(p, &recount, &nounlock); - p->request_queue_sched_id = -1; - - if (p->owner && !nounlock) { - ast_channel_unlock(p->owner); - } - ast_mutex_unlock(&p->lock); - - if (recount) { - ast_update_use_count(); - } - - return 0; -} - -static int queue_request(struct sip_pvt *p, const struct sip_request *req) -{ - struct sip_request *newreq; - - if (!(newreq = ast_calloc(1, sizeof(*newreq)))) { - return -1; - } - - copy_request(newreq, req); - AST_LIST_INSERT_TAIL(&p->request_queue, newreq, next); - if (p->request_queue_sched_id == -1) { - p->request_queue_sched_id = ast_sched_add(sched, 10, scheduler_process_request_queue, p); - } - - return 0; -} - -/*! \brief Read data from SIP socket -\note sipsock_read locks the owner channel while we are processing the SIP message -\return 1 on error, 0 on success -\note Successful messages is connected to SIP call and forwarded to handle_request() -*/ -static int sipsock_read(int *id, int fd, short events, void *ignore) -{ - struct sip_request req; - struct sockaddr_in sin = { 0, }; - struct sip_pvt *p; - int res; - socklen_t len = sizeof(sin); - int nounlock = 0; - int recount = 0; - int lockretry; - - memset(&req, 0, sizeof(req)); - res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len); - if (res < 0) { -#if !defined(__FreeBSD__) - if (errno == EAGAIN) - ast_log(LOG_NOTICE, "SIP: Received packet with bad UDP checksum\n"); - else -#endif - if (errno != ECONNREFUSED) - ast_log(LOG_WARNING, "Recv error: %s\n", strerror(errno)); - return 1; - } - if (option_debug && res == sizeof(req.data) - 1) - ast_log(LOG_DEBUG, "Received packet exceeds buffer. Data is possibly lost\n"); - - req.data[res] = '\0'; - req.len = res; - if(sip_debug_test_addr(&sin)) /* Set the debug flag early on packet level */ - ast_set_flag(&req, SIP_PKT_DEBUG); - if (pedanticsipchecking) - req.len = lws2sws(req.data, req.len); /* Fix multiline headers */ - if (ast_test_flag(&req, SIP_PKT_DEBUG)) - ast_verbose("\n<--- SIP read from %s:%d --->\n%s\n<------------->\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), req.data); - - if(parse_request(&req) == -1) /* Bad packet, can't parse */ - return 1; - - req.method = find_sip_method(req.rlPart1); - - if (ast_test_flag(&req, SIP_PKT_DEBUG)) - ast_verbose("--- (%d headers %d lines)%s ---\n", req.headers, req.lines, (req.headers + req.lines == 0) ? " Nat keepalive" : ""); - - if (req.headers < 2) /* Must have at least two headers */ - return 1; - - /* Process request, with netlock held, and with usual deadlock avoidance */ - for (lockretry = 10; lockretry > 0; lockretry--) { - ast_mutex_lock(&netlock); - - /* Find the active SIP dialog or create a new one */ - p = find_call(&req, &sin, req.method); /* returns p locked */ - if (p == NULL) { - if (option_debug) - ast_log(LOG_DEBUG, "Invalid SIP message - rejected , no callid, len %d\n", req.len); - ast_mutex_unlock(&netlock); - return 1; - } - /* Go ahead and lock the owner if it has one -- we may need it */ - /* because this is deadlock-prone, we need to try and unlock if failed */ - if (!p->owner || !ast_channel_trylock(p->owner)) - break; /* locking succeeded */ - if (lockretry != 1) { - ast_mutex_unlock(&p->lock); - ast_mutex_unlock(&netlock); - /* Sleep for a very short amount of time */ - usleep(1); - } - } - p->recv = sin; - - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) /* This is a request or response, note what it was for */ - append_history(p, "Rx", "%s / %s / %s", req.data, get_header(&req, "CSeq"), req.rlPart2); - - if (!lockretry) { - if (!queue_request(p, &req)) { - /* the request has been queued for later handling */ - ast_mutex_unlock(&p->lock); - ast_mutex_unlock(&netlock); - return 1; - } - - /* This is unsafe, since p->owner is not locked. */ - if (p->owner) - ast_log(LOG_ERROR, "Channel lock for %s could not be obtained, and request was unable to be queued.\n", S_OR(p->owner->name, "- no channel name ??? - ")); - ast_log(LOG_ERROR, "SIP transaction failed: %s \n", p->callid); - if (req.method != SIP_ACK) - transmit_response(p, "503 Server error", &req); /* We must respond according to RFC 3261 sec 12.2 */ - /* XXX We could add retry-after to make sure they come back */ - append_history(p, "LockFail", "Owner lock failed, transaction failed."); - ast_mutex_unlock(&p->lock); - ast_mutex_unlock(&netlock); - return 1; - } - - /* if there are queued requests on this sip_pvt, process them first, so that everything is - handled in order - */ - if (!AST_LIST_EMPTY(&p->request_queue)) { - AST_SCHED_DEL(sched, p->request_queue_sched_id); - process_request_queue(p, &recount, &nounlock); - } - - if (handle_request(p, &req, &sin, &recount, &nounlock) == -1) { - /* Request failed */ - if (option_debug) - ast_log(LOG_DEBUG, "SIP message could not be handled, bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>"); - } - - if (p->owner && !nounlock) - ast_channel_unlock(p->owner); - ast_mutex_unlock(&p->lock); - ast_mutex_unlock(&netlock); - if (recount) - ast_update_use_count(); - - return 1; -} - -/*! \brief Send message waiting indication to alert peer that they've got voicemail */ -static int sip_send_mwi_to_peer(struct sip_peer *peer) -{ - /* Called with peerl lock, but releases it */ - struct sip_pvt *p; - int newmsgs, oldmsgs; - - /* Do we have an IP address? If not, skip this peer */ - if (!peer->addr.sin_addr.s_addr && !peer->defaddr.sin_addr.s_addr) - return 0; - - /* Check for messages */ - ast_app_inboxcount(peer->mailbox, &newmsgs, &oldmsgs); - - peer->lastmsgcheck = time(NULL); - - /* Return now if it's the same thing we told them last time */ - if (((newmsgs > 0x7fff ? 0x7fff0000 : (newmsgs << 16)) | (oldmsgs > 0xffff ? 0xffff : oldmsgs)) == peer->lastmsgssent) { - return 0; - } - - - peer->lastmsgssent = ((newmsgs > 0x7fff ? 0x7fff0000 : (newmsgs << 16)) | (oldmsgs > 0xffff ? 0xffff : oldmsgs)); - - if (peer->mwipvt) { - /* Base message on subscription */ - p = peer->mwipvt; - } else { - /* Build temporary dialog for this message */ - if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY))) - return -1; - if (create_addr_from_peer(p, peer)) { - /* Maybe they're not registered, etc. */ - sip_destroy(p); - return 0; - } - /* Recalculate our side, and recalculate Call ID */ - if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip)) - p->ourip = __ourip; - build_via(p); - build_callid_pvt(p); - /* Destroy this session after 32 secs */ - sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); - } - /* Send MWI */ - ast_set_flag(&p->flags[0], SIP_OUTGOING); - transmit_notify_with_mwi(p, newmsgs, oldmsgs, peer->vmexten); - return 0; -} - -/*! \brief Check whether peer needs a new MWI notification check */ -static int does_peer_need_mwi(struct sip_peer *peer) -{ - time_t t = time(NULL); - - if (ast_test_flag(&peer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY) && - !peer->mwipvt) { /* We don't have a subscription */ - peer->lastmsgcheck = t; /* Reset timer */ - return FALSE; - } - - if (!ast_strlen_zero(peer->mailbox) && (t - peer->lastmsgcheck) > global_mwitime) - return TRUE; - - return FALSE; -} - - -/*! \brief The SIP monitoring thread -\note This thread monitors all the SIP sessions and peers that needs notification of mwi - (and thus do not have a separate thread) indefinitely -*/ -static void *do_monitor(void *data) -{ - int res; - struct sip_pvt *sip; - struct sip_peer *peer = NULL; - time_t t; - int fastrestart = FALSE; - int lastpeernum = -1; - int curpeernum; - int reloading; - - /* Add an I/O event to our SIP UDP socket */ - if (sipsock > -1) - sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL); - - /* From here on out, we die whenever asked */ - for(;;) { - /* Check for a reload request */ - ast_mutex_lock(&sip_reload_lock); - reloading = sip_reloading; - sip_reloading = FALSE; - ast_mutex_unlock(&sip_reload_lock); - if (reloading) { - if (option_verbose > 0) - ast_verbose(VERBOSE_PREFIX_1 "Reloading SIP\n"); - sip_do_reload(sip_reloadreason); - - /* Change the I/O fd of our UDP socket */ - if (sipsock > -1) { - if (sipsock_read_id) - sipsock_read_id = ast_io_change(io, sipsock_read_id, sipsock, NULL, 0, NULL); - else - sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL); - } else if (sipsock_read_id) { - ast_io_remove(io, sipsock_read_id); - sipsock_read_id = NULL; - } - } -restartsearch: - /* Check for interfaces needing to be killed */ - ast_mutex_lock(&iflock); - t = time(NULL); - /* don't scan the interface list if it hasn't been a reasonable period - of time since the last time we did it (when MWI is being sent, we can - get back to this point every millisecond or less) - */ - for (sip = iflist; !fastrestart && sip; sip = sip->next) { - /*! \note If we can't get a lock on an interface, skip it and come - * back later. Note that there is the possibility of a deadlock with - * sip_hangup otherwise, because sip_hangup is called with the channel - * locked first, and the iface lock is attempted second. - */ - if (ast_mutex_trylock(&sip->lock)) - continue; - - /* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */ - if (sip->rtp && sip->owner && - (sip->owner->_state == AST_STATE_UP) && - !sip->redirip.sin_addr.s_addr && - sip->t38.state != T38_ENABLED) { - if (sip->lastrtptx && - ast_rtp_get_rtpkeepalive(sip->rtp) && - (t > sip->lastrtptx + ast_rtp_get_rtpkeepalive(sip->rtp))) { - /* Need to send an empty RTP packet */ - sip->lastrtptx = time(NULL); - ast_rtp_sendcng(sip->rtp, 0); - } - if (sip->lastrtprx && - (ast_rtp_get_rtptimeout(sip->rtp) || ast_rtp_get_rtpholdtimeout(sip->rtp)) && - (t > sip->lastrtprx + ast_rtp_get_rtptimeout(sip->rtp))) { - /* Might be a timeout now -- see if we're on hold */ - struct sockaddr_in sin; - ast_rtp_get_peer(sip->rtp, &sin); - if (sin.sin_addr.s_addr || - (ast_rtp_get_rtpholdtimeout(sip->rtp) && - (t > sip->lastrtprx + ast_rtp_get_rtpholdtimeout(sip->rtp)))) { - /* Needs a hangup */ - if (ast_rtp_get_rtptimeout(sip->rtp)) { - while (sip->owner && ast_channel_trylock(sip->owner)) { - DEADLOCK_AVOIDANCE(&sip->lock); - } - if (sip->owner) { - ast_log(LOG_NOTICE, - "Disconnecting call '%s' for lack of RTP activity in %ld seconds\n", - sip->owner->name, - (long) (t - sip->lastrtprx)); - /* Issue a softhangup */ - ast_softhangup_nolock(sip->owner, AST_SOFTHANGUP_DEV); - ast_channel_unlock(sip->owner); - /* forget the timeouts for this call, since a hangup - has already been requested and we don't want to - repeatedly request hangups - */ - ast_rtp_set_rtptimeout(sip->rtp, 0); - ast_rtp_set_rtpholdtimeout(sip->rtp, 0); - if (sip->vrtp) { - ast_rtp_set_rtptimeout(sip->vrtp, 0); - ast_rtp_set_rtpholdtimeout(sip->vrtp, 0); - } - } - } - } - } - } - /* If we have sessions that needs to be destroyed, do it now */ - if (ast_test_flag(&sip->flags[0], SIP_NEEDDESTROY) && !sip->packets && - !sip->owner) { - ast_mutex_unlock(&sip->lock); - __sip_destroy(sip, 1); - ast_mutex_unlock(&iflock); - usleep(1); - goto restartsearch; - } - ast_mutex_unlock(&sip->lock); - } - ast_mutex_unlock(&iflock); - - /* XXX TODO The scheduler usage in this module does not have sufficient - * synchronization being done between running the scheduler and places - * scheduling tasks. As it is written, any scheduled item may not run - * any sooner than about 1 second, regardless of whether a sooner time - * was asked for. */ - - pthread_testcancel(); - /* Wait for sched or io */ - res = ast_sched_wait(sched); - if ((res < 0) || (res > 1000)) - res = 1000; - /* If we might need to send more mailboxes, don't wait long at all.*/ - if (fastrestart) - res = 1; - res = ast_io_wait(io, res); - if (option_debug && res > 20) - ast_log(LOG_DEBUG, "chan_sip: ast_io_wait ran %d all at once\n", res); - ast_mutex_lock(&monlock); - res = ast_sched_runq(sched); - if (option_debug && res >= 20) - ast_log(LOG_DEBUG, "chan_sip: ast_sched_runq ran %d all at once\n", res); - - /* Send MWI notifications to peers - static and cached realtime peers */ - t = time(NULL); - fastrestart = FALSE; - curpeernum = 0; - peer = NULL; - /* Find next peer that needs mwi */ - ASTOBJ_CONTAINER_TRAVERSE(&peerl, !peer, do { - if ((curpeernum > lastpeernum) && does_peer_need_mwi(iterator)) { - fastrestart = TRUE; - lastpeernum = curpeernum; - peer = ASTOBJ_REF(iterator); - }; - curpeernum++; - } while (0) - ); - /* Send MWI to the peer */ - if (peer) { - ASTOBJ_WRLOCK(peer); - sip_send_mwi_to_peer(peer); - ASTOBJ_UNLOCK(peer); - ASTOBJ_UNREF(peer,sip_destroy_peer); - } else { - /* Reset where we come from */ - lastpeernum = -1; - } - ast_mutex_unlock(&monlock); - } - /* Never reached */ - return NULL; - -} - -/*! \brief Start the channel monitor thread */ -static int restart_monitor(void) -{ - /* If we're supposed to be stopped -- stay stopped */ - if (monitor_thread == AST_PTHREADT_STOP) - return 0; - ast_mutex_lock(&monlock); - if (monitor_thread == pthread_self()) { - ast_mutex_unlock(&monlock); - ast_log(LOG_WARNING, "Cannot kill myself\n"); - return -1; - } - if (monitor_thread != AST_PTHREADT_NULL) { - /* Wake up the thread */ - pthread_kill(monitor_thread, SIGURG); - } else { - /* Start a new monitor */ - if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) { - ast_mutex_unlock(&monlock); - ast_log(LOG_ERROR, "Unable to start monitor thread.\n"); - return -1; - } - } - ast_mutex_unlock(&monlock); - return 0; -} - -/*! \brief React to lack of answer to Qualify poke */ -static int sip_poke_noanswer(const void *data) -{ - struct sip_peer *peer = (struct sip_peer *)data; - - peer->pokeexpire = -1; - if (peer->lastms > -1) { - ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE! Last qualify: %d\n", peer->name, peer->lastms); - manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unreachable\r\nTime: %d\r\n", peer->name, -1); - } - if (peer->call) - sip_destroy(peer->call); - peer->call = NULL; - peer->lastms = -1; - ast_device_state_changed("SIP/%s", peer->name); - - /* This function gets called one place outside of the scheduler ... */ - if (!AST_SCHED_DEL(sched, peer->pokeexpire)) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - - /* There is no need to ASTOBJ_REF() here. Just let the scheduled callback - * inherit the reference that the current callback already has. */ - peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, sip_poke_peer_s, peer); - if (peer->pokeexpire == -1) { - ASTOBJ_UNREF(peer, sip_destroy_peer); - } - - return 0; -} - -/*! \brief Check availability of peer, also keep NAT open -\note This is done with the interval in qualify= configuration option - Default is 2 seconds */ -static int sip_poke_peer(struct sip_peer *peer) -{ - struct sip_pvt *p; - int xmitres = 0; - - if (!peer->maxms || !peer->addr.sin_addr.s_addr) { - /* IF we have no IP, or this isn't to be monitored, return - imeediately after clearing things out */ - if (!AST_SCHED_DEL(sched, peer->pokeexpire)) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - peer->lastms = 0; - peer->call = NULL; - return 0; - } - if (peer->call) { - if (sipdebug) - ast_log(LOG_NOTICE, "Still have a QUALIFY dialog active, deleting\n"); - sip_destroy(peer->call); - } - if (!(p = peer->call = sip_alloc(NULL, NULL, 0, SIP_OPTIONS))) - return -1; - - p->sa = peer->addr; - p->recv = peer->addr; - ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY); - ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); - - /* Send OPTIONs to peer's fullcontact */ - if (!ast_strlen_zero(peer->fullcontact)) - ast_string_field_set(p, fullcontact, peer->fullcontact); - - if (!ast_strlen_zero(peer->tohost)) - ast_string_field_set(p, tohost, peer->tohost); - else - ast_string_field_set(p, tohost, ast_inet_ntoa(peer->addr.sin_addr)); - - /* Recalculate our side, and recalculate Call ID */ - if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip)) - p->ourip = __ourip; - build_via(p); - build_callid_pvt(p); - - if (!AST_SCHED_DEL(sched, peer->pokeexpire)) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - - p->relatedpeer = ASTOBJ_REF(peer); - ast_set_flag(&p->flags[0], SIP_OUTGOING); -#ifdef VOCAL_DATA_HACK - ast_copy_string(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username)); - xmitres = transmit_invite(p, SIP_INVITE, 0, 2); -#else - xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2); -#endif - gettimeofday(&peer->ps, NULL); - if (xmitres == XMIT_ERROR) { - sip_poke_noanswer(ASTOBJ_REF(peer)); /* Immediately unreachable, network problems */ - } else { - if (!AST_SCHED_DEL(sched, peer->pokeexpire)) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - peer->pokeexpire = ast_sched_add(sched, peer->maxms * 2, sip_poke_noanswer, ASTOBJ_REF(peer)); - if (peer->pokeexpire == -1) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - } - - return 0; -} - -/*! \brief Part of PBX channel interface -\note -\par Return values:--- - - If we have qualify on and the device is not reachable, regardless of registration - state we return AST_DEVICE_UNAVAILABLE - - For peers with call limit: - - not registered AST_DEVICE_UNAVAILABLE - - registered, no call AST_DEVICE_NOT_INUSE - - registered, active calls AST_DEVICE_INUSE - - registered, call limit reached AST_DEVICE_BUSY - - registered, onhold AST_DEVICE_ONHOLD - - registered, ringing AST_DEVICE_RINGING - - For peers without call limit: - - not registered AST_DEVICE_UNAVAILABLE - - registered AST_DEVICE_NOT_INUSE - - fixed IP (!dynamic) AST_DEVICE_NOT_INUSE - - Peers that does not have a known call and can't be reached by OPTIONS - - unreachable AST_DEVICE_UNAVAILABLE - - If we return AST_DEVICE_UNKNOWN, the device state engine will try to find - out a state by walking the channel list. - - The queue system (\ref app_queue.c) treats a member as "active" - if devicestate is != AST_DEVICE_UNAVAILBALE && != AST_DEVICE_INVALID - - When placing a call to the queue member, queue system sets a member to busy if - != AST_DEVICE_NOT_INUSE and != AST_DEVICE_UNKNOWN - -*/ -static int sip_devicestate(void *data) -{ - char *host; - char *tmp; - - struct hostent *hp; - struct ast_hostent ahp; - struct sip_peer *p; - - int res = AST_DEVICE_INVALID; - - /* make sure data is not null. Maybe unnecessary, but better be safe */ - host = ast_strdupa(data ? data : ""); - if ((tmp = strchr(host, '@'))) - host = tmp + 1; - - if (option_debug > 2) - ast_log(LOG_DEBUG, "Checking device state for peer %s\n", host); - - /* If find_peer asks for a realtime peer, then this breaks rtautoclear. This - * is because when a peer tries to autoexpire, the last thing it does is to - * queue up an event telling the system that the devicestate has changed - * (presumably to unavailable). If we ask for a realtime peer here, this would - * load it BACK into memory, thus defeating the point of trying to trying to - * clear dead hosts out of memory. - */ - if ((p = find_peer(host, NULL, 0, 1))) { - if (p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) { - /* we have an address for the peer */ - - /* Check status in this order - - Hold - - Ringing - - Busy (enforced only by call limit) - - Inuse (we have a call) - - Unreachable (qualify) - If we don't find any of these state, report AST_DEVICE_NOT_INUSE - for registered devices */ - - if (p->onHold) - /* First check for hold or ring states */ - res = AST_DEVICE_ONHOLD; - else if (p->inRinging) { - if (p->inRinging == p->inUse) - res = AST_DEVICE_RINGING; - else - res = AST_DEVICE_RINGINUSE; - } else if (p->call_limit && (p->inUse == p->call_limit)) - /* check call limit */ - res = AST_DEVICE_BUSY; - else if (p->call_limit && p->inUse) - /* Not busy, but we do have a call */ - res = AST_DEVICE_INUSE; - else if (p->maxms && ((p->lastms > p->maxms) || (p->lastms < 0))) - /* We don't have a call. Are we reachable at all? Requires qualify= */ - res = AST_DEVICE_UNAVAILABLE; - else /* Default reply if we're registered and have no other data */ - res = AST_DEVICE_NOT_INUSE; - } else { - /* there is no address, it's unavailable */ - res = AST_DEVICE_UNAVAILABLE; - } - ASTOBJ_UNREF(p,sip_destroy_peer); - } else { - char *port = strchr(host, ':'); - if (port) - *port = '\0'; - hp = ast_gethostbyname(host, &ahp); - if (hp) - res = AST_DEVICE_UNKNOWN; - } - - return res; -} - -/*! \brief PBX interface function -build SIP pvt structure - SIP calls initiated by the PBX arrive here */ -static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause) -{ - int oldformat; - struct sip_pvt *p; - struct ast_channel *tmpc = NULL; - char *ext, *host; - char tmp[256]; - char *dest = data; - - oldformat = format; - if (!(format &= ((AST_FORMAT_MAX_AUDIO << 1) - 1))) { - ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format %s while capability is %s\n", ast_getformatname(oldformat), ast_getformatname(global_capability)); - *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; /* Can't find codec to connect to host */ - return NULL; - } - if (option_debug) - ast_log(LOG_DEBUG, "Asked to create a SIP channel with formats: %s\n", ast_getformatname_multiple(tmp, sizeof(tmp), oldformat)); - - if (!(p = sip_alloc(NULL, NULL, 0, SIP_INVITE))) { - ast_log(LOG_ERROR, "Unable to build sip pvt data for '%s' (Out of memory or socket error)\n", (char *)data); - *cause = AST_CAUSE_SWITCH_CONGESTION; - return NULL; - } - - ast_set_flag(&p->flags[1], SIP_PAGE2_OUTGOING_CALL); - - if (!(p->options = ast_calloc(1, sizeof(*p->options)))) { - sip_destroy(p); - ast_log(LOG_ERROR, "Unable to build option SIP data structure - Out of memory\n"); - *cause = AST_CAUSE_SWITCH_CONGESTION; - return NULL; - } - - ast_copy_string(tmp, dest, sizeof(tmp)); - host = strchr(tmp, '@'); - if (host) { - *host++ = '\0'; - ext = tmp; - } else { - ext = strchr(tmp, '/'); - if (ext) - *ext++ = '\0'; - host = tmp; - } - - if (create_addr(p, host)) { - *cause = AST_CAUSE_UNREGISTERED; - if (option_debug > 2) - ast_log(LOG_DEBUG, "Cant create SIP call - target device not registred\n"); - sip_destroy(p); - return NULL; - } - if (ast_strlen_zero(p->peername) && ext) - ast_string_field_set(p, peername, ext); - /* Recalculate our side, and recalculate Call ID */ - if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip)) - p->ourip = __ourip; - build_via(p); - build_callid_pvt(p); - - /* We have an extension to call, don't use the full contact here */ - /* This to enable dialing registered peers with extension dialling, - like SIP/peername/extension - SIP/peername will still use the full contact */ - if (ext) { - ast_string_field_set(p, username, ext); - ast_string_field_free(p, fullcontact); - } -#if 0 - printf("Setting up to call extension '%s' at '%s'\n", ext ? ext : "<none>", host); -#endif - p->prefcodec = oldformat; /* Format for this call */ - ast_mutex_lock(&p->lock); - tmpc = sip_new(p, AST_STATE_DOWN, host); /* Place the call */ - ast_mutex_unlock(&p->lock); - if (!tmpc) - sip_destroy(p); - ast_update_use_count(); - restart_monitor(); - return tmpc; -} - -/*! - * \brief Parse the "insecure" setting from sip.conf or from realtime. - * \param flags a pointer to an ast_flags structure - * \param value the value of the SIP insecure setting - * \param lineno linenumber in sip.conf or -1 for realtime - */ -static void set_insecure_flags(struct ast_flags *flags, const char *value, int lineno) -{ - static int dep_insecure_very = 0; - static int dep_insecure_yes = 0; - - if (ast_strlen_zero(value)) - return; - - if (!strcasecmp(value, "very")) { - ast_set_flag(flags, SIP_INSECURE_PORT | SIP_INSECURE_INVITE); - if(!dep_insecure_very) { - if(lineno != -1) - ast_log(LOG_WARNING, "insecure=very at line %d is deprecated; use insecure=port,invite instead\n", lineno); - else - ast_log(LOG_WARNING, "insecure=very is deprecated; use insecure=port,invite instead\n"); - dep_insecure_very = 1; - } - } - else if (ast_true(value)) { - ast_set_flag(flags, SIP_INSECURE_PORT); - if(!dep_insecure_yes) { - if(lineno != -1) - ast_log(LOG_WARNING, "insecure=%s at line %d is deprecated; use insecure=port instead\n", value, lineno); - else - ast_log(LOG_WARNING, "insecure=%s is deprecated; use insecure=port instead\n", value); - dep_insecure_yes = 1; - } - } - else if (!ast_false(value)) { - char buf[64]; - char *word, *next; - ast_copy_string(buf, value, sizeof(buf)); - next = buf; - while ((word = strsep(&next, ","))) { - if (!strcasecmp(word, "port")) - ast_set_flag(flags, SIP_INSECURE_PORT); - else if (!strcasecmp(word, "invite")) - ast_set_flag(flags, SIP_INSECURE_INVITE); - else - ast_log(LOG_WARNING, "Unknown insecure mode '%s' on line %d\n", value, lineno); - } - } -} - -/*! - \brief Handle flag-type options common to configuration of devices - users and peers - \param flags array of two struct ast_flags - \param mask array of two struct ast_flags - \param v linked list of config variables to process - \returns non-zero if any config options were handled, zero otherwise -*/ -static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v) -{ - int res = 1; - - if (!strcasecmp(v->name, "trustrpid")) { - ast_set_flag(&mask[0], SIP_TRUSTRPID); - ast_set2_flag(&flags[0], ast_true(v->value), SIP_TRUSTRPID); - } else if (!strcasecmp(v->name, "sendrpid")) { - ast_set_flag(&mask[0], SIP_SENDRPID); - ast_set2_flag(&flags[0], ast_true(v->value), SIP_SENDRPID); - } else if (!strcasecmp(v->name, "g726nonstandard")) { - ast_set_flag(&mask[0], SIP_G726_NONSTANDARD); - ast_set2_flag(&flags[0], ast_true(v->value), SIP_G726_NONSTANDARD); - } else if (!strcasecmp(v->name, "useclientcode")) { - ast_set_flag(&mask[0], SIP_USECLIENTCODE); - ast_set2_flag(&flags[0], ast_true(v->value), SIP_USECLIENTCODE); - } else if (!strcasecmp(v->name, "dtmfmode")) { - ast_set_flag(&mask[0], SIP_DTMF); - ast_clear_flag(&flags[0], SIP_DTMF); - if (!strcasecmp(v->value, "inband")) - ast_set_flag(&flags[0], SIP_DTMF_INBAND); - else if (!strcasecmp(v->value, "rfc2833")) - ast_set_flag(&flags[0], SIP_DTMF_RFC2833); - else if (!strcasecmp(v->value, "info")) - ast_set_flag(&flags[0], SIP_DTMF_INFO); - else if (!strcasecmp(v->value, "auto")) - ast_set_flag(&flags[0], SIP_DTMF_AUTO); - else { - ast_log(LOG_WARNING, "Unknown dtmf mode '%s' on line %d, using rfc2833\n", v->value, v->lineno); - ast_set_flag(&flags[0], SIP_DTMF_RFC2833); - } - } else if (!strcasecmp(v->name, "nat")) { - ast_set_flag(&mask[0], SIP_NAT); - ast_clear_flag(&flags[0], SIP_NAT); - if (!strcasecmp(v->value, "never")) - ast_set_flag(&flags[0], SIP_NAT_NEVER); - else if (!strcasecmp(v->value, "route")) - ast_set_flag(&flags[0], SIP_NAT_ROUTE); - else if (ast_true(v->value)) - ast_set_flag(&flags[0], SIP_NAT_ALWAYS); - else - ast_set_flag(&flags[0], SIP_NAT_RFC3581); - } else if (!strcasecmp(v->name, "canreinvite")) { - ast_set_flag(&mask[0], SIP_REINVITE); - ast_clear_flag(&flags[0], SIP_REINVITE); - if(ast_true(v->value)) { - ast_set_flag(&flags[0], SIP_CAN_REINVITE | SIP_CAN_REINVITE_NAT); - } else if (!ast_false(v->value)) { - char buf[64]; - char *word, *next = buf; - - ast_copy_string(buf, v->value, sizeof(buf)); - while ((word = strsep(&next, ","))) { - if(!strcasecmp(word, "update")) { - ast_set_flag(&flags[0], SIP_REINVITE_UPDATE | SIP_CAN_REINVITE); - } else if(!strcasecmp(word, "nonat")) { - ast_set_flag(&flags[0], SIP_CAN_REINVITE); - ast_clear_flag(&flags[0], SIP_CAN_REINVITE_NAT); - } else { - ast_log(LOG_WARNING, "Unknown canreinvite mode '%s' on line %d\n", v->value, v->lineno); - } - } - } - } else if (!strcasecmp(v->name, "insecure")) { - ast_set_flag(&mask[0], SIP_INSECURE_PORT | SIP_INSECURE_INVITE); - ast_clear_flag(&flags[0], SIP_INSECURE_PORT | SIP_INSECURE_INVITE); - set_insecure_flags(flags, v->value, v->lineno); - } else if (!strcasecmp(v->name, "progressinband")) { - ast_set_flag(&mask[0], SIP_PROG_INBAND); - ast_clear_flag(&flags[0], SIP_PROG_INBAND); - if (ast_true(v->value)) - ast_set_flag(&flags[0], SIP_PROG_INBAND_YES); - else if (strcasecmp(v->value, "never")) - ast_set_flag(&flags[0], SIP_PROG_INBAND_NO); - } else if (!strcasecmp(v->name, "promiscredir")) { - ast_set_flag(&mask[0], SIP_PROMISCREDIR); - ast_set2_flag(&flags[0], ast_true(v->value), SIP_PROMISCREDIR); - } else if (!strcasecmp(v->name, "videosupport")) { - ast_set_flag(&mask[1], SIP_PAGE2_VIDEOSUPPORT); - ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_VIDEOSUPPORT); - } else if (!strcasecmp(v->name, "allowoverlap")) { - ast_set_flag(&mask[1], SIP_PAGE2_ALLOWOVERLAP); - ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWOVERLAP); - } else if (!strcasecmp(v->name, "allowsubscribe")) { - ast_set_flag(&mask[1], SIP_PAGE2_ALLOWSUBSCRIBE); - ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWSUBSCRIBE); - } else if (!strcasecmp(v->name, "t38pt_udptl")) { - ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_UDPTL); - ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_UDPTL); -#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS - } else if (!strcasecmp(v->name, "t38pt_rtp")) { - ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_RTP); - ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_RTP); - } else if (!strcasecmp(v->name, "t38pt_tcp")) { - ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_TCP); - ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_TCP); -#endif - } else if (!strcasecmp(v->name, "rfc2833compensate")) { - ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE); - ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE); - } else if (!strcasecmp(v->name, "buggymwi")) { - ast_set_flag(&mask[1], SIP_PAGE2_BUGGY_MWI); - ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_BUGGY_MWI); - } else if (!strcasecmp(v->name, "t38pt_usertpsource")) { - ast_set_flag(&mask[1], SIP_PAGE2_UDPTL_DESTINATION); - ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_UDPTL_DESTINATION); - } else - res = 0; - - return res; -} - -/*! \brief Add SIP domain to list of domains we are responsible for */ -static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context) -{ - struct domain *d; - - if (ast_strlen_zero(domain)) { - ast_log(LOG_WARNING, "Zero length domain.\n"); - return 1; - } - - if (!(d = ast_calloc(1, sizeof(*d)))) - return 0; - - ast_copy_string(d->domain, domain, sizeof(d->domain)); - - if (!ast_strlen_zero(context)) - ast_copy_string(d->context, context, sizeof(d->context)); - - d->mode = mode; - - AST_LIST_LOCK(&domain_list); - AST_LIST_INSERT_TAIL(&domain_list, d, list); - AST_LIST_UNLOCK(&domain_list); - - if (sipdebug) - ast_log(LOG_DEBUG, "Added local SIP domain '%s'\n", domain); - - return 1; -} - -/*! \brief check_sip_domain: Check if domain part of uri is local to our server */ -static int check_sip_domain(const char *domain, char *context, size_t len) -{ - struct domain *d; - int result = 0; - - AST_LIST_LOCK(&domain_list); - AST_LIST_TRAVERSE(&domain_list, d, list) { - if (strcasecmp(d->domain, domain)) - continue; - - if (len && !ast_strlen_zero(d->context)) - ast_copy_string(context, d->context, len); - - result = 1; - break; - } - AST_LIST_UNLOCK(&domain_list); - - return result; -} - -/*! \brief Clear our domain list (at reload) */ -static void clear_sip_domains(void) -{ - struct domain *d; - - AST_LIST_LOCK(&domain_list); - while ((d = AST_LIST_REMOVE_HEAD(&domain_list, list))) - free(d); - AST_LIST_UNLOCK(&domain_list); -} - - -/*! \brief Add realm authentication in list */ -static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno) -{ - char authcopy[256]; - char *username=NULL, *realm=NULL, *secret=NULL, *md5secret=NULL; - char *stringp; - struct sip_auth *a, *b, *auth; - - if (ast_strlen_zero(configuration)) - return authlist; - - if (option_debug) - ast_log(LOG_DEBUG, "Auth config :: %s\n", configuration); - - ast_copy_string(authcopy, configuration, sizeof(authcopy)); - stringp = authcopy; - - username = stringp; - realm = strrchr(stringp, '@'); - if (realm) - *realm++ = '\0'; - if (ast_strlen_zero(username) || ast_strlen_zero(realm)) { - ast_log(LOG_WARNING, "Format for authentication entry is user[:secret]@realm at line %d\n", lineno); - return authlist; - } - stringp = username; - username = strsep(&stringp, ":"); - if (username) { - secret = strsep(&stringp, ":"); - if (!secret) { - stringp = username; - md5secret = strsep(&stringp,"#"); - } - } - if (!(auth = ast_calloc(1, sizeof(*auth)))) - return authlist; - - ast_copy_string(auth->realm, realm, sizeof(auth->realm)); - ast_copy_string(auth->username, username, sizeof(auth->username)); - if (secret) - ast_copy_string(auth->secret, secret, sizeof(auth->secret)); - if (md5secret) - ast_copy_string(auth->md5secret, md5secret, sizeof(auth->md5secret)); - - /* find the end of the list */ - for (b = NULL, a = authlist; a ; b = a, a = a->next) - ; - if (b) - b->next = auth; /* Add structure add end of list */ - else - authlist = auth; - - if (option_verbose > 2) - ast_verbose("Added authentication for realm %s\n", realm); - - return authlist; - -} - -/*! \brief Clear realm authentication list (at reload) */ -static int clear_realm_authentication(struct sip_auth *authlist) -{ - struct sip_auth *a = authlist; - struct sip_auth *b; - - while (a) { - b = a; - a = a->next; - free(b); - } - - return 1; -} - -/*! \brief Find authentication for a specific realm */ -static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm) -{ - struct sip_auth *a; - - for (a = authlist; a; a = a->next) { - if (!strcasecmp(a->realm, realm)) - break; - } - - return a; -} - -/*! \brief Initiate a SIP user structure from configuration (configuration or realtime) */ -static struct sip_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime) -{ - struct sip_user *user; - int format; - struct ast_ha *oldha = NULL; - char *varname = NULL, *varval = NULL; - struct ast_variable *tmpvar = NULL; - struct ast_flags userflags[2] = {{(0)}}; - struct ast_flags mask[2] = {{(0)}}; - - - if (!(user = ast_calloc(1, sizeof(*user)))) - return NULL; - - suserobjs++; - ASTOBJ_INIT(user); - ast_copy_string(user->name, name, sizeof(user->name)); - oldha = user->ha; - user->ha = NULL; - ast_copy_flags(&user->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY); - ast_copy_flags(&user->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY); - user->capability = global_capability; - user->allowtransfer = global_allowtransfer; - user->maxcallbitrate = default_maxcallbitrate; - user->autoframing = global_autoframing; - user->prefs = default_prefs; - /* set default context */ - strcpy(user->context, default_context); - strcpy(user->language, default_language); - strcpy(user->mohinterpret, default_mohinterpret); - strcpy(user->mohsuggest, default_mohsuggest); - /* First we walk through the v parameters list and then the alt parameters list */ - for (; v || ((v = alt) && !(alt=NULL)); v = v->next) { - if (handle_common_options(&userflags[0], &mask[0], v)) - continue; - - if (!strcasecmp(v->name, "context")) { - ast_copy_string(user->context, v->value, sizeof(user->context)); - } else if (!strcasecmp(v->name, "subscribecontext")) { - ast_copy_string(user->subscribecontext, v->value, sizeof(user->subscribecontext)); - } else if (!strcasecmp(v->name, "setvar")) { - varname = ast_strdupa(v->value); - if ((varval = strchr(varname,'='))) { - *varval++ = '\0'; - if ((tmpvar = ast_variable_new(varname, varval))) { - tmpvar->next = user->chanvars; - user->chanvars = tmpvar; - } - } - } else if (!strcasecmp(v->name, "permit") || - !strcasecmp(v->name, "deny")) { - user->ha = ast_append_ha(v->name, v->value, user->ha); - } else if (!strcasecmp(v->name, "allowtransfer")) { - user->allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED; - } else if (!strcasecmp(v->name, "secret")) { - ast_copy_string(user->secret, v->value, sizeof(user->secret)); - } else if (!strcasecmp(v->name, "md5secret")) { - ast_copy_string(user->md5secret, v->value, sizeof(user->md5secret)); - } else if (!strcasecmp(v->name, "callerid")) { - ast_callerid_split(v->value, user->cid_name, sizeof(user->cid_name), user->cid_num, sizeof(user->cid_num)); - } else if (!strcasecmp(v->name, "fullname")) { - ast_copy_string(user->cid_name, v->value, sizeof(user->cid_name)); - } else if (!strcasecmp(v->name, "cid_number")) { - ast_copy_string(user->cid_num, v->value, sizeof(user->cid_num)); - } else if (!strcasecmp(v->name, "callgroup")) { - user->callgroup = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "pickupgroup")) { - user->pickupgroup = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "language")) { - ast_copy_string(user->language, v->value, sizeof(user->language)); - } else if (!strcasecmp(v->name, "mohinterpret") - || !strcasecmp(v->name, "musicclass") || !strcasecmp(v->name, "musiconhold")) { - ast_copy_string(user->mohinterpret, v->value, sizeof(user->mohinterpret)); - } else if (!strcasecmp(v->name, "mohsuggest")) { - ast_copy_string(user->mohsuggest, v->value, sizeof(user->mohsuggest)); - } else if (!strcasecmp(v->name, "accountcode")) { - ast_copy_string(user->accountcode, v->value, sizeof(user->accountcode)); - } else if (!strcasecmp(v->name, "call-limit")) { - user->call_limit = atoi(v->value); - if (user->call_limit < 0) - user->call_limit = 0; - } else if (!strcasecmp(v->name, "amaflags")) { - format = ast_cdr_amaflags2int(v->value); - if (format < 0) { - ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno); - } else { - user->amaflags = format; - } - } else if (!strcasecmp(v->name, "allow")) { - ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 1); - } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 0); - } else if (!strcasecmp(v->name, "autoframing")) { - user->autoframing = ast_true(v->value); - } else if (!strcasecmp(v->name, "callingpres")) { - user->callingpres = ast_parse_caller_presentation(v->value); - if (user->callingpres == -1) - user->callingpres = atoi(v->value); - } else if (!strcasecmp(v->name, "maxcallbitrate")) { - user->maxcallbitrate = atoi(v->value); - if (user->maxcallbitrate < 0) - user->maxcallbitrate = default_maxcallbitrate; - } - /* We can't just report unknown options here because this may be a - * type=friend entry. All user options are valid for a peer, but not - * the other way around. */ - } - ast_copy_flags(&user->flags[0], &userflags[0], mask[0].flags); - ast_copy_flags(&user->flags[1], &userflags[1], mask[1].flags); - if (ast_test_flag(&user->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) - global_allowsubscribe = TRUE; /* No global ban any more */ - ast_free_ha(oldha); - return user; -} - -/*! \brief Set peer defaults before configuring specific configurations */ -static void set_peer_defaults(struct sip_peer *peer) -{ - if (peer->expire == 0) { - /* Don't reset expire or port time during reload - if we have an active registration - */ - peer->expire = -1; - peer->pokeexpire = -1; - peer->addr.sin_port = htons(STANDARD_SIP_PORT); - } - ast_copy_flags(&peer->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY); - ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY); - strcpy(peer->context, default_context); - strcpy(peer->subscribecontext, default_subscribecontext); - strcpy(peer->language, default_language); - strcpy(peer->mohinterpret, default_mohinterpret); - strcpy(peer->mohsuggest, default_mohsuggest); - peer->addr.sin_family = AF_INET; - peer->defaddr.sin_family = AF_INET; - peer->capability = global_capability; - peer->maxcallbitrate = default_maxcallbitrate; - peer->rtptimeout = global_rtptimeout; - peer->rtpholdtimeout = global_rtpholdtimeout; - peer->rtpkeepalive = global_rtpkeepalive; - peer->allowtransfer = global_allowtransfer; - peer->autoframing = global_autoframing; - strcpy(peer->vmexten, default_vmexten); - peer->secret[0] = '\0'; - peer->md5secret[0] = '\0'; - peer->cid_num[0] = '\0'; - peer->cid_name[0] = '\0'; - peer->fromdomain[0] = '\0'; - peer->fromuser[0] = '\0'; - peer->regexten[0] = '\0'; - peer->mailbox[0] = '\0'; - peer->callgroup = 0; - peer->pickupgroup = 0; - peer->maxms = default_qualify; - peer->prefs = default_prefs; -} - -/*! \brief Create temporary peer (used in autocreatepeer mode) */ -static struct sip_peer *temp_peer(const char *name) -{ - struct sip_peer *peer; - - if (!(peer = ast_calloc(1, sizeof(*peer)))) - return NULL; - - apeerobjs++; - ASTOBJ_INIT(peer); - set_peer_defaults(peer); - - ast_copy_string(peer->name, name, sizeof(peer->name)); - - ast_set_flag(&peer->flags[1], SIP_PAGE2_SELFDESTRUCT); - ast_set_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC); - peer->prefs = default_prefs; - reg_source_db(peer); - - return peer; -} - -/*! \brief Build peer from configuration (file or realtime static/dynamic) */ -static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime) -{ - struct sip_peer *peer = NULL; - struct ast_ha *oldha = NULL; - int obproxyfound=0; - int found=0; - int firstpass=1; - int format=0; /* Ama flags */ - time_t regseconds = 0; - char *varname = NULL, *varval = NULL; - struct ast_variable *tmpvar = NULL; - struct ast_flags peerflags[2] = {{(0)}}; - struct ast_flags mask[2] = {{(0)}}; - char fullcontact[sizeof(peer->fullcontact)] = ""; - - if (!realtime || ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) - /* Note we do NOT use find_peer here, to avoid realtime recursion */ - /* We also use a case-sensitive comparison (unlike find_peer) so - that case changes made to the peer name will be properly handled - during reload - */ - peer = ASTOBJ_CONTAINER_FIND_UNLINK_FULL(&peerl, name, name, 0, 0, strcmp); - - if (peer) { - /* Already in the list, remove it and it will be added back (or FREE'd) */ - found = 1; - if (!(peer->objflags & ASTOBJ_FLAG_MARKED)) - firstpass = 0; - } else { - if (!(peer = ast_calloc(1, sizeof(*peer)))) - return NULL; - - if (realtime && !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) - rpeerobjs++; - else - speerobjs++; - ASTOBJ_INIT(peer); - } - /* Note that our peer HAS had its reference count incrased */ - if (firstpass) { - peer->lastmsgssent = -1; - oldha = peer->ha; - peer->ha = NULL; - set_peer_defaults(peer); /* Set peer defaults */ - } - if (!found && name) - ast_copy_string(peer->name, name, sizeof(peer->name)); - - /* If we have channel variables, remove them (reload) */ - if (peer->chanvars) { - ast_variables_destroy(peer->chanvars); - peer->chanvars = NULL; - /* XXX should unregister ? */ - } - - /* If we have realm authentication information, remove them (reload) */ - clear_realm_authentication(peer->auth); - peer->auth = NULL; - - for (; v || ((v = alt) && !(alt=NULL)); v = v->next) { - if (handle_common_options(&peerflags[0], &mask[0], v)) - continue; - if (realtime && !strcasecmp(v->name, "regseconds")) { - ast_get_time_t(v->value, ®seconds, 0, NULL); - } else if (realtime && !strcasecmp(v->name, "ipaddr") && !ast_strlen_zero(v->value) ) { - inet_aton(v->value, &(peer->addr.sin_addr)); - } else if (realtime && !strcasecmp(v->name, "name")) - ast_copy_string(peer->name, v->value, sizeof(peer->name)); - else if (realtime && !strcasecmp(v->name, "fullcontact")) { - /* Reconstruct field, because realtime separates our value at the ';' */ - if (!ast_strlen_zero(fullcontact)) { - strncat(fullcontact, ";", sizeof(fullcontact) - strlen(fullcontact) - 1); - strncat(fullcontact, v->value, sizeof(fullcontact) - strlen(fullcontact) - 1); - } else { - ast_copy_string(fullcontact, v->value, sizeof(fullcontact)); - ast_set_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT); - } - } else if (!strcasecmp(v->name, "secret")) - ast_copy_string(peer->secret, v->value, sizeof(peer->secret)); - else if (!strcasecmp(v->name, "md5secret")) - ast_copy_string(peer->md5secret, v->value, sizeof(peer->md5secret)); - else if (!strcasecmp(v->name, "auth")) - peer->auth = add_realm_authentication(peer->auth, v->value, v->lineno); - else if (!strcasecmp(v->name, "callerid")) { - ast_callerid_split(v->value, peer->cid_name, sizeof(peer->cid_name), peer->cid_num, sizeof(peer->cid_num)); - } else if (!strcasecmp(v->name, "fullname")) { - ast_copy_string(peer->cid_name, v->value, sizeof(peer->cid_name)); - } else if (!strcasecmp(v->name, "cid_number")) { - ast_copy_string(peer->cid_num, v->value, sizeof(peer->cid_num)); - } else if (!strcasecmp(v->name, "context")) { - ast_copy_string(peer->context, v->value, sizeof(peer->context)); - } else if (!strcasecmp(v->name, "subscribecontext")) { - ast_copy_string(peer->subscribecontext, v->value, sizeof(peer->subscribecontext)); - } else if (!strcasecmp(v->name, "fromdomain")) { - ast_copy_string(peer->fromdomain, v->value, sizeof(peer->fromdomain)); - } else if (!strcasecmp(v->name, "usereqphone")) { - ast_set2_flag(&peer->flags[0], ast_true(v->value), SIP_USEREQPHONE); - } else if (!strcasecmp(v->name, "fromuser")) { - ast_copy_string(peer->fromuser, v->value, sizeof(peer->fromuser)); - } else if (!strcasecmp(v->name, "host") || !strcasecmp(v->name, "outboundproxy")) { - if (!strcasecmp(v->value, "dynamic")) { - if (!strcasecmp(v->name, "outboundproxy") || obproxyfound) { - ast_log(LOG_WARNING, "You can't have a dynamic outbound proxy, you big silly head at line %d.\n", v->lineno); - } else { - /* They'll register with us */ - if (!found || !ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC)) { - /* Initialize stuff if this is a new peer, or if it used to be - * non-dynamic before the reload. */ - memset(&peer->addr.sin_addr, 0, 4); - if (peer->addr.sin_port) { - /* If we've already got a port, make it the default rather than absolute */ - peer->defaddr.sin_port = peer->addr.sin_port; - peer->addr.sin_port = 0; - } - } - ast_set_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC); - } - } else { - /* Non-dynamic. Make sure we become that way if we're not */ - if (!AST_SCHED_DEL(sched, peer->expire)) { - struct sip_peer *peer_ptr = peer; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - ast_clear_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC); - if (!obproxyfound || !strcasecmp(v->name, "outboundproxy")) { - if (ast_get_ip_or_srv(&peer->addr, v->value, srvlookup ? "_sip._udp" : NULL)) { - ASTOBJ_UNREF(peer, sip_destroy_peer); - return NULL; - } - } - if (!strcasecmp(v->name, "outboundproxy")) - obproxyfound=1; - else { - ast_copy_string(peer->tohost, v->value, sizeof(peer->tohost)); - if (!peer->addr.sin_port) - peer->addr.sin_port = htons(STANDARD_SIP_PORT); - } - if (global_dynamic_exclude_static) { - global_contact_ha = ast_append_ha("deny", (char *)ast_inet_ntoa(peer->addr.sin_addr), global_contact_ha); - } - } - } else if (!strcasecmp(v->name, "defaultip")) { - if (ast_get_ip(&peer->defaddr, v->value)) { - ASTOBJ_UNREF(peer, sip_destroy_peer); - return NULL; - } - } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) { - peer->ha = ast_append_ha(v->name, v->value, peer->ha); - } else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny")) { - peer->contactha = ast_append_ha(v->name + 7, v->value, peer->contactha); - } else if (!strcasecmp(v->name, "port")) { - if (!realtime && ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC)) - peer->defaddr.sin_port = htons(atoi(v->value)); - else - peer->addr.sin_port = htons(atoi(v->value)); - } else if (!strcasecmp(v->name, "callingpres")) { - peer->callingpres = ast_parse_caller_presentation(v->value); - if (peer->callingpres == -1) - peer->callingpres = atoi(v->value); - } else if (!strcasecmp(v->name, "username")) { - ast_copy_string(peer->username, v->value, sizeof(peer->username)); - } else if (!strcasecmp(v->name, "language")) { - ast_copy_string(peer->language, v->value, sizeof(peer->language)); - } else if (!strcasecmp(v->name, "regexten")) { - ast_copy_string(peer->regexten, v->value, sizeof(peer->regexten)); - } else if (!strcasecmp(v->name, "call-limit") || !strcasecmp(v->name, "incominglimit")) { - peer->call_limit = atoi(v->value); - if (peer->call_limit < 0) - peer->call_limit = 0; - } else if (!strcasecmp(v->name, "amaflags")) { - format = ast_cdr_amaflags2int(v->value); - if (format < 0) { - ast_log(LOG_WARNING, "Invalid AMA Flags for peer: %s at line %d\n", v->value, v->lineno); - } else { - peer->amaflags = format; - } - } else if (!strcasecmp(v->name, "accountcode")) { - ast_copy_string(peer->accountcode, v->value, sizeof(peer->accountcode)); - } else if (!strcasecmp(v->name, "mohinterpret") - || !strcasecmp(v->name, "musicclass") || !strcasecmp(v->name, "musiconhold")) { - ast_copy_string(peer->mohinterpret, v->value, sizeof(peer->mohinterpret)); - } else if (!strcasecmp(v->name, "mohsuggest")) { - ast_copy_string(peer->mohsuggest, v->value, sizeof(peer->mohsuggest)); - } else if (!strcasecmp(v->name, "mailbox")) { - ast_copy_string(peer->mailbox, v->value, sizeof(peer->mailbox)); - } else if (!strcasecmp(v->name, "hasvoicemail")) { - /* People expect that if 'hasvoicemail' is set, that the mailbox will - * be also set, even if not explicitly specified. */ - if (ast_true(v->value) && ast_strlen_zero(peer->mailbox)) { - ast_copy_string(peer->mailbox, name, sizeof(peer->mailbox)); - } - } else if (!strcasecmp(v->name, "subscribemwi")) { - ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_SUBSCRIBEMWIONLY); - } else if (!strcasecmp(v->name, "vmexten")) { - ast_copy_string(peer->vmexten, v->value, sizeof(peer->vmexten)); - } else if (!strcasecmp(v->name, "callgroup")) { - peer->callgroup = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "allowtransfer")) { - peer->allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED; - } else if (!strcasecmp(v->name, "pickupgroup")) { - peer->pickupgroup = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "allow")) { - ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 1); - } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&peer->prefs, &peer->capability, v->value, 0); - } else if (!strcasecmp(v->name, "autoframing")) { - peer->autoframing = ast_true(v->value); - } else if (!strcasecmp(v->name, "rtptimeout")) { - if ((sscanf(v->value, "%d", &peer->rtptimeout) != 1) || (peer->rtptimeout < 0)) { - ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno); - peer->rtptimeout = global_rtptimeout; - } - } else if (!strcasecmp(v->name, "rtpholdtimeout")) { - if ((sscanf(v->value, "%d", &peer->rtpholdtimeout) != 1) || (peer->rtpholdtimeout < 0)) { - ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno); - peer->rtpholdtimeout = global_rtpholdtimeout; - } - } else if (!strcasecmp(v->name, "rtpkeepalive")) { - if ((sscanf(v->value, "%d", &peer->rtpkeepalive) != 1) || (peer->rtpkeepalive < 0)) { - ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d. Using default.\n", v->value, v->lineno); - peer->rtpkeepalive = global_rtpkeepalive; - } - } else if (!strcasecmp(v->name, "setvar")) { - /* Set peer channel variable */ - varname = ast_strdupa(v->value); - if ((varval = strchr(varname, '='))) { - *varval++ = '\0'; - if ((tmpvar = ast_variable_new(varname, varval))) { - tmpvar->next = peer->chanvars; - peer->chanvars = tmpvar; - } - } - } else if (!strcasecmp(v->name, "qualify")) { - if (!strcasecmp(v->value, "no")) { - peer->maxms = 0; - } else if (!strcasecmp(v->value, "yes")) { - peer->maxms = default_qualify ? default_qualify : DEFAULT_MAXMS; - } else if (sscanf(v->value, "%d", &peer->maxms) != 1) { - ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", peer->name, v->lineno); - peer->maxms = 0; - } - if (realtime && !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS) && peer->maxms > 0) { - /* This would otherwise cause a network storm, where the - * qualify response refreshes the peer from the database, - * which in turn causes another qualify to be sent, ad - * infinitum. */ - ast_log(LOG_WARNING, "Qualify is incompatible with dynamic uncached realtime. Please either turn rtcachefriends on or turn qualify off on peer '%s'\n", peer->name); - peer->maxms = 0; - } - } else if (!strcasecmp(v->name, "maxcallbitrate")) { - peer->maxcallbitrate = atoi(v->value); - if (peer->maxcallbitrate < 0) - peer->maxcallbitrate = default_maxcallbitrate; - } - } - if (!ast_strlen_zero(fullcontact)) { - ast_copy_string(peer->fullcontact, fullcontact, sizeof(peer->fullcontact)); - /* We have a hostname in the fullcontact, but if we don't have an - * address listed on the entry (or if it's 'dynamic'), then we need to - * parse the entry to obtain the IP address, so a dynamic host can be - * contacted immediately after reload (as opposed to waiting for it to - * register once again). */ - __set_address_from_contact(fullcontact, &peer->addr); - } - - if (!ast_test_flag(&global_flags[1], SIP_PAGE2_IGNOREREGEXPIRE) && ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC) && realtime) { - time_t nowtime = time(NULL); - - if ((nowtime - regseconds) > 0) { - destroy_association(peer); - memset(&peer->addr, 0, sizeof(peer->addr)); - if (option_debug) - ast_log(LOG_DEBUG, "Bah, we're expired (%d/%d/%d)!\n", (int)(nowtime - regseconds), (int)regseconds, (int)nowtime); - } - } - ast_copy_flags(&peer->flags[0], &peerflags[0], mask[0].flags); - ast_copy_flags(&peer->flags[1], &peerflags[1], mask[1].flags); - if (ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) - global_allowsubscribe = TRUE; /* No global ban any more */ - if (!found && ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC) && !ast_test_flag(&peer->flags[0], SIP_REALTIME)) - reg_source_db(peer); - ASTOBJ_UNMARK(peer); - ast_free_ha(oldha); - return peer; -} - -/*! \brief Re-read SIP.conf config file -\note This function reloads all config data, except for - active peers (with registrations). They will only - change configuration data at restart, not at reload. - SIP debug and recordhistory state will not change - */ -static int reload_config(enum channelreloadreason reason) -{ - struct ast_config *cfg, *ucfg; - struct ast_variable *v; - struct sip_peer *peer; - struct sip_user *user; - struct ast_hostent ahp; - char *cat, *stringp, *context, *oldregcontext; - char newcontexts[AST_MAX_CONTEXT], oldcontexts[AST_MAX_CONTEXT]; - struct hostent *hp; - int format; - struct ast_flags dummy[2]; - int auto_sip_domains = FALSE; - struct sockaddr_in old_bindaddr = bindaddr; - int registry_count = 0, peer_count = 0, user_count = 0; - unsigned int temp_tos = 0; - struct ast_flags debugflag = {0}; - - cfg = ast_config_load(config); - - /* We *must* have a config file otherwise stop immediately */ - if (!cfg) { - ast_log(LOG_NOTICE, "Unable to load config %s\n", config); - return -1; - } - - if (option_debug > 3) - ast_log(LOG_DEBUG, "--------------- SIP reload started\n"); - - clear_realm_authentication(authl); - clear_sip_domains(); - authl = NULL; - - ast_free_ha(global_contact_ha); - global_contact_ha = NULL; - - /* First, destroy all outstanding registry calls */ - /* This is needed, since otherwise active registry entries will not be destroyed */ - ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do { - ASTOBJ_RDLOCK(iterator); - if (iterator->call) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "Destroying active SIP dialog for registry %s@%s\n", iterator->username, iterator->hostname); - /* This will also remove references to the registry */ - sip_destroy(iterator->call); - } - ASTOBJ_UNLOCK(iterator); - - } while(0)); - - /* Then, actually destroy users and registry */ - ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user); - if (option_debug > 3) - ast_log(LOG_DEBUG, "--------------- Done destroying user list\n"); - ASTOBJ_CONTAINER_DESTROYALL(®l, sip_registry_destroy); - if (option_debug > 3) - ast_log(LOG_DEBUG, "--------------- Done destroying registry list\n"); - ASTOBJ_CONTAINER_MARKALL(&peerl); - - /* Initialize copy of current global_regcontext for later use in removing stale contexts */ - ast_copy_string(oldcontexts, global_regcontext, sizeof(oldcontexts)); - oldregcontext = oldcontexts; - - /* Clear all flags before setting default values */ - /* Preserve debugging settings for console */ - ast_copy_flags(&debugflag, &global_flags[1], SIP_PAGE2_DEBUG_CONSOLE); - ast_clear_flag(&global_flags[0], AST_FLAGS_ALL); - ast_clear_flag(&global_flags[1], AST_FLAGS_ALL); - ast_copy_flags(&global_flags[1], &debugflag, SIP_PAGE2_DEBUG_CONSOLE); - - /* Reset IP addresses */ - memset(&bindaddr, 0, sizeof(bindaddr)); - ast_free_ha(localaddr); - memset(&localaddr, 0, sizeof(localaddr)); - memset(&externip, 0, sizeof(externip)); - memset(&default_prefs, 0 , sizeof(default_prefs)); - outboundproxyip.sin_port = htons(STANDARD_SIP_PORT); - outboundproxyip.sin_family = AF_INET; /* Type of address: IPv4 */ - ourport = STANDARD_SIP_PORT; - srvlookup = DEFAULT_SRVLOOKUP; - global_tos_sip = DEFAULT_TOS_SIP; - global_tos_audio = DEFAULT_TOS_AUDIO; - global_tos_video = DEFAULT_TOS_VIDEO; - externhost[0] = '\0'; /* External host name (for behind NAT DynDNS support) */ - externexpire = 0; /* Expiration for DNS re-issuing */ - externrefresh = 10; - memset(&outboundproxyip, 0, sizeof(outboundproxyip)); - - /* Reset channel settings to default before re-configuring */ - allow_external_domains = DEFAULT_ALLOW_EXT_DOM; /* Allow external invites */ - global_regcontext[0] = '\0'; - expiry = DEFAULT_EXPIRY; - global_notifyringing = DEFAULT_NOTIFYRINGING; - global_limitonpeers = FALSE; - global_directrtpsetup = FALSE; /* Experimental feature, disabled by default */ - global_notifyhold = FALSE; - global_alwaysauthreject = 0; - global_allowsubscribe = FALSE; - ast_copy_string(global_useragent, DEFAULT_USERAGENT, sizeof(global_useragent)); - ast_copy_string(default_notifymime, DEFAULT_NOTIFYMIME, sizeof(default_notifymime)); - if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) - ast_copy_string(global_realm, DEFAULT_REALM, sizeof(global_realm)); - else - ast_copy_string(global_realm, ast_config_AST_SYSTEM_NAME, sizeof(global_realm)); - ast_copy_string(default_callerid, DEFAULT_CALLERID, sizeof(default_callerid)); - compactheaders = DEFAULT_COMPACTHEADERS; - global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT; - global_regattempts_max = 0; - pedanticsipchecking = DEFAULT_PEDANTIC; - global_mwitime = DEFAULT_MWITIME; - autocreatepeer = DEFAULT_AUTOCREATEPEER; - global_autoframing = 0; - global_allowguest = DEFAULT_ALLOWGUEST; - global_rtptimeout = 0; - global_rtpholdtimeout = 0; - global_rtpkeepalive = 0; - global_allowtransfer = TRANSFER_OPENFORALL; /* Merrily accept all transfers by default */ - global_rtautoclear = 120; - ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE); /* Default for peers, users: TRUE */ - ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP); /* Default for peers, users: TRUE */ - ast_set_flag(&global_flags[1], SIP_PAGE2_RTUPDATE); - - /* Initialize some reasonable defaults at SIP reload (used both for channel and as default for peers and users */ - ast_copy_string(default_context, DEFAULT_CONTEXT, sizeof(default_context)); - default_subscribecontext[0] = '\0'; - default_language[0] = '\0'; - default_fromdomain[0] = '\0'; - default_qualify = DEFAULT_QUALIFY; - default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE; - ast_copy_string(default_mohinterpret, DEFAULT_MOHINTERPRET, sizeof(default_mohinterpret)); - ast_copy_string(default_mohsuggest, DEFAULT_MOHSUGGEST, sizeof(default_mohsuggest)); - ast_copy_string(default_vmexten, DEFAULT_VMEXTEN, sizeof(default_vmexten)); - ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833); /*!< Default DTMF setting: RFC2833 */ - ast_set_flag(&global_flags[0], SIP_NAT_RFC3581); /*!< NAT support if requested by device with rport */ - ast_set_flag(&global_flags[0], SIP_CAN_REINVITE); /*!< Allow re-invites */ - - /* Debugging settings, always default to off */ - dumphistory = FALSE; - recordhistory = FALSE; - ast_clear_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONFIG); - - /* Misc settings for the channel */ - global_relaxdtmf = FALSE; - global_callevents = FALSE; - global_t1min = DEFAULT_T1MIN; - - global_matchexterniplocally = FALSE; - - /* Copy the default jb config over global_jbconf */ - memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); - - ast_clear_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT); - - /* Read the [general] config section of sip.conf (or from realtime config) */ - for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { - if (handle_common_options(&global_flags[0], &dummy[0], v)) - continue; - /* handle jb conf */ - if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) - continue; - - /* Create the interface list */ - if (!strcasecmp(v->name, "context")) { - ast_copy_string(default_context, v->value, sizeof(default_context)); - } else if (!strcasecmp(v->name, "subscribecontext")) { - ast_copy_string(default_subscribecontext, v->value, sizeof(default_subscribecontext)); - } else if (!strcasecmp(v->name, "allowguest")) { - global_allowguest = ast_true(v->value) ? 1 : 0; - } else if (!strcasecmp(v->name, "realm")) { - ast_copy_string(global_realm, v->value, sizeof(global_realm)); - } else if (!strcasecmp(v->name, "useragent")) { - ast_copy_string(global_useragent, v->value, sizeof(global_useragent)); - if (option_debug) - ast_log(LOG_DEBUG, "Setting SIP channel User-Agent Name to %s\n", global_useragent); - } else if (!strcasecmp(v->name, "allowtransfer")) { - global_allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED; - } else if (!strcasecmp(v->name, "rtcachefriends")) { - ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_RTCACHEFRIENDS); - } else if (!strcasecmp(v->name, "rtsavesysname")) { - ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_RTSAVE_SYSNAME); - } else if (!strcasecmp(v->name, "rtupdate")) { - ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_RTUPDATE); - } else if (!strcasecmp(v->name, "ignoreregexpire")) { - ast_set2_flag(&global_flags[1], ast_true(v->value), SIP_PAGE2_IGNOREREGEXPIRE); - } else if (!strcasecmp(v->name, "t1min")) { - global_t1min = atoi(v->value); - } else if (!strcasecmp(v->name, "dynamic_exclude_static") || !strcasecmp(v->name, "dynamic_excludes_static")) { - global_dynamic_exclude_static = ast_true(v->value); - } else if (!strcasecmp(v->name, "contactpermit") || !strcasecmp(v->name, "contactdeny")) { - global_contact_ha = ast_append_ha(v->name + 7, v->value, global_contact_ha); - } else if (!strcasecmp(v->name, "rtautoclear")) { - int i = atoi(v->value); - if (i > 0) - global_rtautoclear = i; - else - i = 0; - ast_set2_flag(&global_flags[1], i || ast_true(v->value), SIP_PAGE2_RTAUTOCLEAR); - } else if (!strcasecmp(v->name, "usereqphone")) { - ast_set2_flag(&global_flags[0], ast_true(v->value), SIP_USEREQPHONE); - } else if (!strcasecmp(v->name, "relaxdtmf")) { - global_relaxdtmf = ast_true(v->value); - } else if (!strcasecmp(v->name, "checkmwi")) { - if ((sscanf(v->value, "%d", &global_mwitime) != 1) || (global_mwitime < 0)) { - ast_log(LOG_WARNING, "'%s' is not a valid MWI time setting at line %d. Using default (10).\n", v->value, v->lineno); - global_mwitime = DEFAULT_MWITIME; - } - } else if (!strcasecmp(v->name, "vmexten")) { - ast_copy_string(default_vmexten, v->value, sizeof(default_vmexten)); - } else if (!strcasecmp(v->name, "rtptimeout")) { - if ((sscanf(v->value, "%d", &global_rtptimeout) != 1) || (global_rtptimeout < 0)) { - ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno); - global_rtptimeout = 0; - } - } else if (!strcasecmp(v->name, "rtpholdtimeout")) { - if ((sscanf(v->value, "%d", &global_rtpholdtimeout) != 1) || (global_rtpholdtimeout < 0)) { - ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno); - global_rtpholdtimeout = 0; - } - } else if (!strcasecmp(v->name, "rtpkeepalive")) { - if ((sscanf(v->value, "%d", &global_rtpkeepalive) != 1) || (global_rtpkeepalive < 0)) { - ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d. Using default.\n", v->value, v->lineno); - global_rtpkeepalive = 0; - } - } else if (!strcasecmp(v->name, "compactheaders")) { - compactheaders = ast_true(v->value); - } else if (!strcasecmp(v->name, "notifymimetype")) { - ast_copy_string(default_notifymime, v->value, sizeof(default_notifymime)); - } else if (!strncasecmp(v->name, "limitonpeer", 11)) { - global_limitonpeers = ast_true(v->value); - } else if (!strcasecmp(v->name, "directrtpsetup")) { - global_directrtpsetup = ast_true(v->value); - } else if (!strcasecmp(v->name, "notifyringing")) { - global_notifyringing = ast_true(v->value); - } else if (!strcasecmp(v->name, "notifyhold")) { - global_notifyhold = ast_true(v->value); - } else if (!strcasecmp(v->name, "alwaysauthreject")) { - global_alwaysauthreject = ast_true(v->value); - } else if (!strcasecmp(v->name, "mohinterpret") - || !strcasecmp(v->name, "musicclass") || !strcasecmp(v->name, "musiconhold")) { - ast_copy_string(default_mohinterpret, v->value, sizeof(default_mohinterpret)); - } else if (!strcasecmp(v->name, "mohsuggest")) { - ast_copy_string(default_mohsuggest, v->value, sizeof(default_mohsuggest)); - } else if (!strcasecmp(v->name, "language")) { - ast_copy_string(default_language, v->value, sizeof(default_language)); - } else if (!strcasecmp(v->name, "regcontext")) { - ast_copy_string(newcontexts, v->value, sizeof(newcontexts)); - stringp = newcontexts; - /* Let's remove any contexts that are no longer defined in regcontext */ - cleanup_stale_contexts(stringp, oldregcontext); - /* Create contexts if they don't exist already */ - while ((context = strsep(&stringp, "&"))) { - if (!ast_context_find(context)) - ast_context_create(NULL, context,"SIP"); - } - ast_copy_string(global_regcontext, v->value, sizeof(global_regcontext)); - } else if (!strcasecmp(v->name, "callerid")) { - ast_copy_string(default_callerid, v->value, sizeof(default_callerid)); - } else if (!strcasecmp(v->name, "fromdomain")) { - ast_copy_string(default_fromdomain, v->value, sizeof(default_fromdomain)); - } else if (!strcasecmp(v->name, "outboundproxy")) { - if (ast_get_ip_or_srv(&outboundproxyip, v->value, srvlookup ? "_sip._udp" : NULL) < 0) - ast_log(LOG_WARNING, "Unable to locate host '%s'\n", v->value); - } else if (!strcasecmp(v->name, "outboundproxyport")) { - /* Port needs to be after IP */ - sscanf(v->value, "%d", &format); - outboundproxyip.sin_port = htons(format); - } else if (!strcasecmp(v->name, "autocreatepeer")) { - autocreatepeer = ast_true(v->value); - } else if (!strcasecmp(v->name, "srvlookup")) { - srvlookup = ast_true(v->value); - } else if (!strcasecmp(v->name, "pedantic")) { - pedanticsipchecking = ast_true(v->value); - } else if (!strcasecmp(v->name, "maxexpirey") || !strcasecmp(v->name, "maxexpiry")) { - max_expiry = atoi(v->value); - if (max_expiry < 1) - max_expiry = DEFAULT_MAX_EXPIRY; - } else if (!strcasecmp(v->name, "minexpirey") || !strcasecmp(v->name, "minexpiry")) { - min_expiry = atoi(v->value); - if (min_expiry < 1) - min_expiry = DEFAULT_MIN_EXPIRY; - } else if (!strcasecmp(v->name, "defaultexpiry") || !strcasecmp(v->name, "defaultexpirey")) { - default_expiry = atoi(v->value); - if (default_expiry < 1) - default_expiry = DEFAULT_DEFAULT_EXPIRY; - } else if (!strcasecmp(v->name, "sipdebug")) { /* XXX maybe ast_set2_flags ? */ - if (ast_true(v->value)) - ast_set_flag(&global_flags[1], SIP_PAGE2_DEBUG_CONFIG); - } else if (!strcasecmp(v->name, "dumphistory")) { - dumphistory = ast_true(v->value); - } else if (!strcasecmp(v->name, "recordhistory")) { - recordhistory = ast_true(v->value); - } else if (!strcasecmp(v->name, "registertimeout")) { - global_reg_timeout = atoi(v->value); - if (global_reg_timeout < 1) - global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT; - } else if (!strcasecmp(v->name, "registerattempts")) { - global_regattempts_max = atoi(v->value); - } else if (!strcasecmp(v->name, "bindaddr")) { - if (!(hp = ast_gethostbyname(v->value, &ahp))) { - ast_log(LOG_WARNING, "Invalid address: %s\n", v->value); - } else { - memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr)); - } - } else if (!strcasecmp(v->name, "localnet")) { - struct ast_ha *na; - if (!(na = ast_append_ha("d", v->value, localaddr))) - ast_log(LOG_WARNING, "Invalid localnet value: %s\n", v->value); - else - localaddr = na; - } else if (!strcasecmp(v->name, "localmask")) { - ast_log(LOG_WARNING, "Use of localmask is no long supported -- use localnet with mask syntax\n"); - } else if (!strcasecmp(v->name, "externip")) { - if (!(hp = ast_gethostbyname(v->value, &ahp))) - ast_log(LOG_WARNING, "Invalid address for externip keyword: %s\n", v->value); - else - memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr)); - externexpire = 0; - } else if (!strcasecmp(v->name, "externhost")) { - ast_copy_string(externhost, v->value, sizeof(externhost)); - if (!(hp = ast_gethostbyname(externhost, &ahp))) - ast_log(LOG_WARNING, "Invalid address for externhost keyword: %s\n", externhost); - else - memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr)); - externexpire = time(NULL); - } else if (!strcasecmp(v->name, "externrefresh")) { - if (sscanf(v->value, "%d", &externrefresh) != 1) { - ast_log(LOG_WARNING, "Invalid externrefresh value '%s', must be an integer >0 at line %d\n", v->value, v->lineno); - externrefresh = 10; - } - } else if (!strcasecmp(v->name, "allow")) { - ast_parse_allow_disallow(&default_prefs, &global_capability, v->value, 1); - } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&default_prefs, &global_capability, v->value, 0); - } else if (!strcasecmp(v->name, "autoframing")) { - global_autoframing = ast_true(v->value); - } else if (!strcasecmp(v->name, "allowexternaldomains")) { - allow_external_domains = ast_true(v->value); - } else if (!strcasecmp(v->name, "autodomain")) { - auto_sip_domains = ast_true(v->value); - } else if (!strcasecmp(v->name, "domain")) { - char *domain = ast_strdupa(v->value); - char *context = strchr(domain, ','); - - if (context) - *context++ = '\0'; - - if (option_debug && ast_strlen_zero(context)) - ast_log(LOG_DEBUG, "No context specified at line %d for domain '%s'\n", v->lineno, domain); - if (ast_strlen_zero(domain)) - ast_log(LOG_WARNING, "Empty domain specified at line %d\n", v->lineno); - else - add_sip_domain(ast_strip(domain), SIP_DOMAIN_CONFIG, context ? ast_strip(context) : ""); - } else if (!strcasecmp(v->name, "register")) { - if (sip_register(v->value, v->lineno) == 0) - registry_count++; - } else if (!strcasecmp(v->name, "tos")) { - if (!ast_str2tos(v->value, &temp_tos)) { - global_tos_sip = temp_tos; - global_tos_audio = temp_tos; - global_tos_video = temp_tos; - ast_log(LOG_WARNING, "tos value at line %d is deprecated. See doc/ip-tos.txt for more information.\n", v->lineno); - } else - ast_log(LOG_WARNING, "Invalid tos value at line %d, See doc/ip-tos.txt for more information.\n", v->lineno); - } else if (!strcasecmp(v->name, "tos_sip")) { - if (ast_str2tos(v->value, &global_tos_sip)) - ast_log(LOG_WARNING, "Invalid tos_sip value at line %d, recommended value is 'cs3'. See doc/ip-tos.txt.\n", v->lineno); - } else if (!strcasecmp(v->name, "tos_audio")) { - if (ast_str2tos(v->value, &global_tos_audio)) - ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, recommended value is 'ef'. See doc/ip-tos.txt.\n", v->lineno); - } else if (!strcasecmp(v->name, "tos_video")) { - if (ast_str2tos(v->value, &global_tos_video)) - ast_log(LOG_WARNING, "Invalid tos_video value at line %d, recommended value is 'af41'. See doc/ip-tos.txt.\n", v->lineno); - } else if (!strcasecmp(v->name, "bindport")) { - if (sscanf(v->value, "%d", &ourport) == 1) { - bindaddr.sin_port = htons(ourport); - } else { - ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config); - } - } else if (!strcasecmp(v->name, "qualify")) { - if (!strcasecmp(v->value, "no")) { - default_qualify = 0; - } else if (!strcasecmp(v->value, "yes")) { - default_qualify = DEFAULT_MAXMS; - } else if (sscanf(v->value, "%d", &default_qualify) != 1) { - ast_log(LOG_WARNING, "Qualification default should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", v->lineno); - default_qualify = 0; - } - } else if (!strcasecmp(v->name, "callevents")) { - global_callevents = ast_true(v->value); - } else if (!strcasecmp(v->name, "maxcallbitrate")) { - default_maxcallbitrate = atoi(v->value); - if (default_maxcallbitrate < 0) - default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE; - } else if (!strcasecmp(v->name, "matchexterniplocally")) { - global_matchexterniplocally = ast_true(v->value); - } - } - - if (!allow_external_domains && AST_LIST_EMPTY(&domain_list)) { - ast_log(LOG_WARNING, "To disallow external domains, you need to configure local SIP domains.\n"); - allow_external_domains = 1; - } - - /* Build list of authentication to various SIP realms, i.e. service providers */ - for (v = ast_variable_browse(cfg, "authentication"); v ; v = v->next) { - /* Format for authentication is auth = username:password@realm */ - if (!strcasecmp(v->name, "auth")) - authl = add_realm_authentication(authl, v->value, v->lineno); - } - - ucfg = ast_config_load("users.conf"); - if (ucfg) { - struct ast_variable *gen; - int genhassip, genregistersip; - const char *hassip, *registersip; - - genhassip = ast_true(ast_variable_retrieve(ucfg, "general", "hassip")); - genregistersip = ast_true(ast_variable_retrieve(ucfg, "general", "registersip")); - gen = ast_variable_browse(ucfg, "general"); - cat = ast_category_browse(ucfg, NULL); - while (cat) { - if (strcasecmp(cat, "general")) { - hassip = ast_variable_retrieve(ucfg, cat, "hassip"); - registersip = ast_variable_retrieve(ucfg, cat, "registersip"); - if (ast_true(hassip) || (!hassip && genhassip)) { - user = build_user(cat, gen, ast_variable_browse(ucfg, cat), 0); - if (user) { - ASTOBJ_CONTAINER_LINK(&userl,user); - ASTOBJ_UNREF(user, sip_destroy_user); - user_count++; - } - peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0); - if (peer) { - ast_device_state_changed("SIP/%s", peer->name); - ASTOBJ_CONTAINER_LINK(&peerl,peer); - ASTOBJ_UNREF(peer, sip_destroy_peer); - peer_count++; - } - } - if (ast_true(registersip) || (!registersip && genregistersip)) { - char tmp[256]; - const char *host = ast_variable_retrieve(ucfg, cat, "host"); - const char *username = ast_variable_retrieve(ucfg, cat, "username"); - const char *secret = ast_variable_retrieve(ucfg, cat, "secret"); - const char *contact = ast_variable_retrieve(ucfg, cat, "contact"); - if (!host) - host = ast_variable_retrieve(ucfg, "general", "host"); - if (!username) - username = ast_variable_retrieve(ucfg, "general", "username"); - if (!secret) - secret = ast_variable_retrieve(ucfg, "general", "secret"); - if (!contact) - contact = "s"; - if (!ast_strlen_zero(username) && !ast_strlen_zero(host)) { - if (!ast_strlen_zero(secret)) - snprintf(tmp, sizeof(tmp), "%s:%s@%s/%s", username, secret, host, contact); - else - snprintf(tmp, sizeof(tmp), "%s@%s/%s", username, host, contact); - if (sip_register(tmp, 0) == 0) - registry_count++; - } - } - } - cat = ast_category_browse(ucfg, cat); - } - ast_config_destroy(ucfg); - } - - - /* Load peers, users and friends */ - cat = NULL; - while ( (cat = ast_category_browse(cfg, cat)) ) { - const char *utype; - if (!strcasecmp(cat, "general") || !strcasecmp(cat, "authentication")) - continue; - utype = ast_variable_retrieve(cfg, cat, "type"); - if (!utype) { - ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat); - continue; - } else { - int is_user = 0, is_peer = 0; - if (!strcasecmp(utype, "user")) - is_user = 1; - else if (!strcasecmp(utype, "friend")) - is_user = is_peer = 1; - else if (!strcasecmp(utype, "peer")) - is_peer = 1; - else { - ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, "sip.conf"); - continue; - } - if (is_user) { - user = build_user(cat, ast_variable_browse(cfg, cat), NULL, 0); - if (user) { - ASTOBJ_CONTAINER_LINK(&userl,user); - ASTOBJ_UNREF(user, sip_destroy_user); - user_count++; - } - } - if (is_peer) { - peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0); - if (peer) { - ASTOBJ_CONTAINER_LINK(&peerl,peer); - ASTOBJ_UNREF(peer, sip_destroy_peer); - peer_count++; - } - } - } - } - if (ast_find_ourip(&__ourip, bindaddr)) { - ast_log(LOG_WARNING, "Unable to get own IP address, SIP disabled\n"); - ast_config_destroy(cfg); - return 0; - } - if (!ntohs(bindaddr.sin_port)) - bindaddr.sin_port = ntohs(STANDARD_SIP_PORT); - bindaddr.sin_family = AF_INET; - ast_mutex_lock(&netlock); - if ((sipsock > -1) && (memcmp(&old_bindaddr, &bindaddr, sizeof(struct sockaddr_in)))) { - close(sipsock); - sipsock = -1; - } - if (sipsock < 0) { - sipsock = socket(AF_INET, SOCK_DGRAM, 0); - if (sipsock < 0) { - ast_log(LOG_WARNING, "Unable to create SIP socket: %s\n", strerror(errno)); - ast_config_destroy(cfg); - return -1; - } else { - /* Allow SIP clients on the same host to access us: */ - const int reuseFlag = 1; - - setsockopt(sipsock, SOL_SOCKET, SO_REUSEADDR, - (const char*)&reuseFlag, - sizeof reuseFlag); - - ast_enable_packet_fragmentation(sipsock); - - if (bind(sipsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { - ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n", - ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port), - strerror(errno)); - close(sipsock); - sipsock = -1; - } else { - if (option_verbose > 1) { - ast_verbose(VERBOSE_PREFIX_2 "SIP Listening on %s:%d\n", - ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port)); - ast_verbose(VERBOSE_PREFIX_2 "Using SIP TOS: %s\n", ast_tos2str(global_tos_sip)); - } - if (setsockopt(sipsock, IPPROTO_IP, IP_TOS, &global_tos_sip, sizeof(global_tos_sip))) - ast_log(LOG_WARNING, "Unable to set SIP TOS to %s\n", ast_tos2str(global_tos_sip)); - } - } - } - ast_mutex_unlock(&netlock); - - /* Add default domains - host name, IP address and IP:port */ - /* Only do this if user added any sip domain with "localdomains" */ - /* In order to *not* break backwards compatibility */ - /* Some phones address us at IP only, some with additional port number */ - if (auto_sip_domains) { - char temp[MAXHOSTNAMELEN]; - - /* First our default IP address */ - if (bindaddr.sin_addr.s_addr) - add_sip_domain(ast_inet_ntoa(bindaddr.sin_addr), SIP_DOMAIN_AUTO, NULL); - else - ast_log(LOG_NOTICE, "Can't add wildcard IP address to domain list, please add IP address to domain manually.\n"); - - /* Our extern IP address, if configured */ - if (externip.sin_addr.s_addr) - add_sip_domain(ast_inet_ntoa(externip.sin_addr), SIP_DOMAIN_AUTO, NULL); - - /* Extern host name (NAT traversal support) */ - if (!ast_strlen_zero(externhost)) - add_sip_domain(externhost, SIP_DOMAIN_AUTO, NULL); - - /* Our host name */ - if (!gethostname(temp, sizeof(temp))) - add_sip_domain(temp, SIP_DOMAIN_AUTO, NULL); - } - - /* Release configuration from memory */ - ast_config_destroy(cfg); - - /* Load the list of manual NOTIFY types to support */ - if (notify_types) - ast_config_destroy(notify_types); - notify_types = ast_config_load(notify_config); - - /* Done, tell the manager */ - manager_event(EVENT_FLAG_SYSTEM, "ChannelReload", "Channel: SIP\r\nReloadReason: %s\r\nRegistry_Count: %d\r\nPeer_Count: %d\r\nUser_Count: %d\r\n", channelreloadreason2txt(reason), registry_count, peer_count, user_count); - - return 0; -} - -static struct ast_udptl *sip_get_udptl_peer(struct ast_channel *chan) -{ - struct sip_pvt *p; - struct ast_udptl *udptl = NULL; - - p = chan->tech_pvt; - if (!p) - return NULL; - - ast_mutex_lock(&p->lock); - if (p->udptl && ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) - udptl = p->udptl; - ast_mutex_unlock(&p->lock); - return udptl; -} - -static int sip_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl) -{ - struct sip_pvt *p; - - p = chan->tech_pvt; - if (!p) - return -1; - ast_mutex_lock(&p->lock); - if (udptl) - ast_udptl_get_peer(udptl, &p->udptlredirip); - else - memset(&p->udptlredirip, 0, sizeof(p->udptlredirip)); - if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) { - if (!p->pendinginvite) { - if (option_debug > 2) { - ast_log(LOG_DEBUG, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip), udptl ? ntohs(p->udptlredirip.sin_port) : 0); - } - transmit_reinvite_with_t38_sdp(p); - } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { - if (option_debug > 2) { - ast_log(LOG_DEBUG, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(udptl ? p->udptlredirip.sin_addr : p->ourip), udptl ? ntohs(p->udptlredirip.sin_port) : 0); - } - ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); - } - } - /* Reset lastrtprx timer */ - p->lastrtprx = p->lastrtptx = time(NULL); - ast_mutex_unlock(&p->lock); - return 0; -} - -/*! \brief Handle T38 reinvite - \todo Make sure we don't destroy the call if we can't handle the re-invite. - Nothing should be changed until we have processed the SDP and know that we - can handle it. -*/ -static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt, int reinvite) -{ - struct sip_pvt *p; - int flag = 0; - - p = chan->tech_pvt; - if (!p || !pvt->udptl) - return -1; - - /* Setup everything on the other side like offered/responded from first side */ - ast_mutex_lock(&p->lock); - - /*! \todo check if this is not set earlier when setting up the PVT. If not - maybe it should move there. */ - p->t38.jointcapability = p->t38.peercapability = pvt->t38.jointcapability; - - ast_udptl_set_far_max_datagram(p->udptl, ast_udptl_get_local_max_datagram(pvt->udptl)); - ast_udptl_set_local_max_datagram(p->udptl, ast_udptl_get_local_max_datagram(pvt->udptl)); - ast_udptl_set_error_correction_scheme(p->udptl, ast_udptl_get_error_correction_scheme(pvt->udptl)); - - if (reinvite) { /* If we are handling sending re-invite to the other side of the bridge */ - /*! \note The SIP_CAN_REINVITE flag is for RTP media redirects, - not really T38 re-invites which are different. In this - case it's used properly, to see if we can reinvite over - NAT - */ - if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE) && ast_test_flag(&pvt->flags[0], SIP_CAN_REINVITE)) { - ast_udptl_get_peer(pvt->udptl, &p->udptlredirip); - flag =1; - } else { - memset(&p->udptlredirip, 0, sizeof(p->udptlredirip)); - } - if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) { - if (!p->pendinginvite) { - if (option_debug > 2) { - if (flag) - ast_log(LOG_DEBUG, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port)); - else - ast_log(LOG_DEBUG, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip)); - } - transmit_reinvite_with_t38_sdp(p); - } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { - if (option_debug > 2) { - if (flag) - ast_log(LOG_DEBUG, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port)); - else - ast_log(LOG_DEBUG, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip)); - } - ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); - } - } - /* Reset lastrtprx timer */ - p->lastrtprx = p->lastrtptx = time(NULL); - ast_mutex_unlock(&p->lock); - return 0; - } else { /* If we are handling sending 200 OK to the other side of the bridge */ - if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE) && ast_test_flag(&pvt->flags[0], SIP_CAN_REINVITE)) { - ast_udptl_get_peer(pvt->udptl, &p->udptlredirip); - flag = 1; - } else { - memset(&p->udptlredirip, 0, sizeof(p->udptlredirip)); - } - if (option_debug > 2) { - if (flag) - ast_log(LOG_DEBUG, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port)); - else - ast_log(LOG_DEBUG, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip)); - } - pvt->t38.state = T38_ENABLED; - p->t38.state = T38_ENABLED; - if (option_debug > 1) { - ast_log(LOG_DEBUG, "T38 changed state to %d on channel %s\n", pvt->t38.state, pvt->owner ? pvt->owner->name : "<none>"); - ast_log(LOG_DEBUG, "T38 changed state to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>"); - } - transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL); - p->lastrtprx = p->lastrtptx = time(NULL); - ast_mutex_unlock(&p->lock); - return 0; - } -} - - -/*! \brief Returns null if we can't reinvite audio (part of RTP interface) */ -static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) -{ - struct sip_pvt *p = NULL; - enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; - - if (!(p = chan->tech_pvt)) - return AST_RTP_GET_FAILED; - - ast_mutex_lock(&p->lock); - if (!(p->rtp)) { - ast_mutex_unlock(&p->lock); - return AST_RTP_GET_FAILED; - } - - *rtp = p->rtp; - - if (ast_rtp_getnat(*rtp) && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) - res = AST_RTP_TRY_PARTIAL; - else if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) - res = AST_RTP_TRY_NATIVE; - else if (ast_test_flag(&global_jbconf, AST_JB_FORCED)) - res = AST_RTP_GET_FAILED; - - ast_mutex_unlock(&p->lock); - - return res; -} - -/*! \brief Returns null if we can't reinvite video (part of RTP interface) */ -static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp) -{ - struct sip_pvt *p = NULL; - enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL; - - if (!(p = chan->tech_pvt)) - return AST_RTP_GET_FAILED; - - ast_mutex_lock(&p->lock); - if (!(p->vrtp)) { - ast_mutex_unlock(&p->lock); - return AST_RTP_GET_FAILED; - } - - *rtp = p->vrtp; - - if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE)) - res = AST_RTP_TRY_NATIVE; - - ast_mutex_unlock(&p->lock); - - return res; -} - -/*! \brief Set the RTP peer for this call */ -static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active) -{ - struct sip_pvt *p; - int changed = 0; - - p = chan->tech_pvt; - if (!p) - return -1; - - /* Disable early RTP bridge */ - if (chan->_state != AST_STATE_UP && !global_directrtpsetup) /* We are in early state */ - return 0; - - ast_mutex_lock(&p->lock); - if (ast_test_flag(&p->flags[0], SIP_ALREADYGONE)) { - /* If we're destroyed, don't bother */ - ast_mutex_unlock(&p->lock); - return 0; - } - - /* if this peer cannot handle reinvites of the media stream to devices - that are known to be behind a NAT, then stop the process now - */ - if (nat_active && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) { - ast_mutex_unlock(&p->lock); - return 0; - } - - if (rtp) { - changed |= ast_rtp_get_peer(rtp, &p->redirip); - } else if (p->redirip.sin_addr.s_addr || ntohs(p->redirip.sin_port) != 0) { - memset(&p->redirip, 0, sizeof(p->redirip)); - changed = 1; - } - if (vrtp) { - changed |= ast_rtp_get_peer(vrtp, &p->vredirip); - } else if (p->vredirip.sin_addr.s_addr || ntohs(p->vredirip.sin_port) != 0) { - memset(&p->vredirip, 0, sizeof(p->vredirip)); - changed = 1; - } - if (codecs) { - if ((p->redircodecs != codecs)) { - p->redircodecs = codecs; - changed = 1; - } - if ((p->capability & codecs) != p->capability) { - p->jointcapability &= codecs; - p->capability &= codecs; - changed = 1; - } - } - if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER) && !ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) { - if (chan->_state != AST_STATE_UP) { /* We are in early state */ - if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) - append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal."); - if (option_debug) - ast_log(LOG_DEBUG, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip)); - } else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */ - if (option_debug > 2) { - ast_log(LOG_DEBUG, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip)); - } - transmit_reinvite_with_sdp(p); - } else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) { - if (option_debug > 2) { - ast_log(LOG_DEBUG, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip)); - } - /* We have a pending Invite. Send re-invite when we're done with the invite */ - ast_set_flag(&p->flags[0], SIP_NEEDREINVITE); - } - } - /* Reset lastrtprx timer */ - p->lastrtprx = p->lastrtptx = time(NULL); - ast_mutex_unlock(&p->lock); - return 0; -} - -static char *synopsis_dtmfmode = "Change the dtmfmode for a SIP call"; -static char *descrip_dtmfmode = "SIPDtmfMode(inband|info|rfc2833): Changes the dtmfmode for a SIP call\n"; -static char *app_dtmfmode = "SIPDtmfMode"; - -static char *app_sipaddheader = "SIPAddHeader"; -static char *synopsis_sipaddheader = "Add a SIP header to the outbound call"; - -static char *descrip_sipaddheader = "" -" SIPAddHeader(Header: Content)\n" -"Adds a header to a SIP call placed with DIAL.\n" -"Remember to user the X-header if you are adding non-standard SIP\n" -"headers, like \"X-Asterisk-Accountcode:\". Use this with care.\n" -"Adding the wrong headers may jeopardize the SIP dialog.\n" -"Always returns 0\n"; - - -/*! \brief Set the DTMFmode for an outbound SIP call (application) */ -static int sip_dtmfmode(struct ast_channel *chan, void *data) -{ - struct sip_pvt *p; - char *mode; - if (data) - mode = (char *)data; - else { - ast_log(LOG_WARNING, "This application requires the argument: info, inband, rfc2833\n"); - return 0; - } - ast_channel_lock(chan); - if (chan->tech != &sip_tech && chan->tech != &sip_tech_info) { - ast_log(LOG_WARNING, "Call this application only on SIP incoming calls\n"); - ast_channel_unlock(chan); - return 0; - } - p = chan->tech_pvt; - if (!p) { - ast_channel_unlock(chan); - return 0; - } - ast_mutex_lock(&p->lock); - if (!strcasecmp(mode,"info")) { - ast_clear_flag(&p->flags[0], SIP_DTMF); - ast_set_flag(&p->flags[0], SIP_DTMF_INFO); - p->jointnoncodeccapability &= ~AST_RTP_DTMF; - } else if (!strcasecmp(mode,"rfc2833")) { - ast_clear_flag(&p->flags[0], SIP_DTMF); - ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833); - p->jointnoncodeccapability |= AST_RTP_DTMF; - } else if (!strcasecmp(mode,"inband")) { - ast_clear_flag(&p->flags[0], SIP_DTMF); - ast_set_flag(&p->flags[0], SIP_DTMF_INBAND); - p->jointnoncodeccapability &= ~AST_RTP_DTMF; - } else - ast_log(LOG_WARNING, "I don't know about this dtmf mode: %s\n",mode); - if (p->rtp) - ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); - if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) { - if (!p->vad) { - p->vad = ast_dsp_new(); - ast_dsp_set_features(p->vad, DSP_FEATURE_DTMF_DETECT); - } - } else { - if (p->vad) { - ast_dsp_free(p->vad); - p->vad = NULL; - } - } - ast_mutex_unlock(&p->lock); - ast_channel_unlock(chan); - return 0; -} - -/*! \brief Add a SIP header to an outbound INVITE */ -static int sip_addheader(struct ast_channel *chan, void *data) -{ - int no = 0; - int ok = FALSE; - char varbuf[30]; - char *inbuf = (char *) data; - - if (ast_strlen_zero(inbuf)) { - ast_log(LOG_WARNING, "This application requires the argument: Header\n"); - return 0; - } - ast_channel_lock(chan); - - /* Check for headers */ - while (!ok && no <= 50) { - no++; - snprintf(varbuf, sizeof(varbuf), "__SIPADDHEADER%.2d", no); - - /* Compare without the leading underscores */ - if( (pbx_builtin_getvar_helper(chan, (const char *) varbuf + 2) == (const char *) NULL) ) - ok = TRUE; - } - if (ok) { - pbx_builtin_setvar_helper (chan, varbuf, inbuf); - if (sipdebug) - ast_log(LOG_DEBUG,"SIP Header added \"%s\" as %s\n", inbuf, varbuf); - } else { - ast_log(LOG_WARNING, "Too many SIP headers added, max 50\n"); - } - ast_channel_unlock(chan); - return 0; -} - -/*! \brief Transfer call before connect with a 302 redirect -\note Called by the transfer() dialplan application through the sip_transfer() - pbx interface function if the call is in ringing state -\todo Fix this function so that we wait for reply to the REFER and - react to errors, denials or other issues the other end might have. - */ -static int sip_sipredirect(struct sip_pvt *p, const char *dest) -{ - char *cdest; - char *extension, *host, *port; - char tmp[80]; - - cdest = ast_strdupa(dest); - - extension = strsep(&cdest, "@"); - host = strsep(&cdest, ":"); - port = strsep(&cdest, ":"); - if (ast_strlen_zero(extension)) { - ast_log(LOG_ERROR, "Missing mandatory argument: extension\n"); - return 0; - } - - /* we'll issue the redirect message here */ - if (!host) { - char *localtmp; - ast_copy_string(tmp, get_header(&p->initreq, "To"), sizeof(tmp)); - if (ast_strlen_zero(tmp)) { - ast_log(LOG_ERROR, "Cannot retrieve the 'To' header from the original SIP request!\n"); - return 0; - } - if ((localtmp = strcasestr(tmp, "sip:")) && (localtmp = strchr(localtmp, '@'))) { - char lhost[80], lport[80]; - memset(lhost, 0, sizeof(lhost)); - memset(lport, 0, sizeof(lport)); - localtmp++; - /* This is okey because lhost and lport are as big as tmp */ - sscanf(localtmp, "%[^<>:; ]:%[^<>:; ]", lhost, lport); - if (ast_strlen_zero(lhost)) { - ast_log(LOG_ERROR, "Can't find the host address\n"); - return 0; - } - host = ast_strdupa(lhost); - if (!ast_strlen_zero(lport)) { - port = ast_strdupa(lport); - } - } - } - - ast_string_field_build(p, our_contact, "Transfer <sip:%s@%s%s%s>", extension, host, port ? ":" : "", port ? port : ""); - transmit_response_reliable(p, "302 Moved Temporarily", &p->initreq); - - sip_scheddestroy(p, SIP_TRANS_TIMEOUT); /* Make sure we stop send this reply. */ - sip_alreadygone(p); - return 0; -} - -/*! \brief Return SIP UA's codec (part of the RTP interface) */ -static int sip_get_codec(struct ast_channel *chan) -{ - struct sip_pvt *p = chan->tech_pvt; - return p->jointcapability ? p->jointcapability : p->capability; -} - -/*! \brief Send a poke to all known peers - Space them out 100 ms apart - XXX We might have a cool algorithm for this or use random - any suggestions? -*/ -static void sip_poke_all_peers(void) -{ - int ms = 0; - - if (!speerobjs) /* No peers, just give up */ - return; - - ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do { - ASTOBJ_WRLOCK(iterator); - if (!AST_SCHED_DEL(sched, iterator->pokeexpire)) { - struct sip_peer *peer_ptr = iterator; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - ms += 100; - iterator->pokeexpire = ast_sched_add(sched, ms, sip_poke_peer_s, ASTOBJ_REF(iterator)); - if (iterator->pokeexpire == -1) { - struct sip_peer *peer_ptr = iterator; - ASTOBJ_UNREF(peer_ptr, sip_destroy_peer); - } - ASTOBJ_UNLOCK(iterator); - } while (0) - ); -} - -/*! \brief Send all known registrations */ -static void sip_send_all_registers(void) -{ - int ms; - int regspacing; - if (!regobjs) - return; - regspacing = default_expiry * 1000/regobjs; - if (regspacing > 100) - regspacing = 100; - ms = regspacing; - ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do { - ASTOBJ_WRLOCK(iterator); - AST_SCHED_DEL(sched, iterator->expire); - ms += regspacing; - iterator->expire = ast_sched_add(sched, ms, sip_reregister, iterator); - ASTOBJ_UNLOCK(iterator); - } while (0) - ); -} - -/*! \brief Reload module */ -static int sip_do_reload(enum channelreloadreason reason) -{ - reload_config(reason); - - /* Prune peers who still are supposed to be deleted */ - ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl, sip_destroy_peer); - if (option_debug > 3) - ast_log(LOG_DEBUG, "--------------- Done destroying pruned peers\n"); - - /* Send qualify (OPTIONS) to all peers */ - sip_poke_all_peers(); - - /* Register with all services */ - sip_send_all_registers(); - - if (option_debug > 3) - ast_log(LOG_DEBUG, "--------------- SIP reload done\n"); - - return 0; -} - -/*! \brief Force reload of module from cli */ -static int sip_reload(int fd, int argc, char *argv[]) -{ - ast_mutex_lock(&sip_reload_lock); - if (sip_reloading) - ast_verbose("Previous SIP reload not yet done\n"); - else { - sip_reloading = TRUE; - if (fd) - sip_reloadreason = CHANNEL_CLI_RELOAD; - else - sip_reloadreason = CHANNEL_MODULE_RELOAD; - } - ast_mutex_unlock(&sip_reload_lock); - restart_monitor(); - - return 0; -} - -/*! \brief Part of Asterisk module interface */ -static int reload(void) -{ - return sip_reload(0, 0, NULL); -} - -static struct ast_cli_entry cli_sip_debug_deprecated = - { { "sip", "debug", NULL }, - sip_do_debug_deprecated, "Enable SIP debugging", - debug_usage }; - -static struct ast_cli_entry cli_sip_no_debug_deprecated = - { { "sip", "no", "debug", NULL }, - sip_no_debug_deprecated, "Disable SIP debugging", - debug_usage }; - -static struct ast_cli_entry cli_sip[] = { - { { "sip", "show", "channels", NULL }, - sip_show_channels, "List active SIP channels", - show_channels_usage }, - - { { "sip", "show", "domains", NULL }, - sip_show_domains, "List our local SIP domains.", - show_domains_usage }, - - { { "sip", "show", "inuse", NULL }, - sip_show_inuse, "List all inuse/limits", - show_inuse_usage }, - - { { "sip", "show", "objects", NULL }, - sip_show_objects, "List all SIP object allocations", - show_objects_usage }, - - { { "sip", "show", "peers", NULL }, - sip_show_peers, "List defined SIP peers", - show_peers_usage }, - - { { "sip", "show", "registry", NULL }, - sip_show_registry, "List SIP registration status", - show_reg_usage }, - - { { "sip", "show", "settings", NULL }, - sip_show_settings, "Show SIP global settings", - show_settings_usage }, - - { { "sip", "show", "subscriptions", NULL }, - sip_show_subscriptions, "List active SIP subscriptions", - show_subscriptions_usage }, - - { { "sip", "show", "users", NULL }, - sip_show_users, "List defined SIP users", - show_users_usage }, - - { { "sip", "notify", NULL }, - sip_notify, "Send a notify packet to a SIP peer", - notify_usage, complete_sipnotify }, - - { { "sip", "show", "channel", NULL }, - sip_show_channel, "Show detailed SIP channel info", - show_channel_usage, complete_sipch }, - - { { "sip", "show", "history", NULL }, - sip_show_history, "Show SIP dialog history", - show_history_usage, complete_sipch }, - - { { "sip", "show", "peer", NULL }, - sip_show_peer, "Show details on specific SIP peer", - show_peer_usage, complete_sip_show_peer }, - - { { "sip", "show", "user", NULL }, - sip_show_user, "Show details on specific SIP user", - show_user_usage, complete_sip_show_user }, - - { { "sip", "prune", "realtime", NULL }, - sip_prune_realtime, "Prune cached Realtime object(s)", - prune_realtime_usage }, - - { { "sip", "prune", "realtime", "peer", NULL }, - sip_prune_realtime, "Prune cached Realtime peer(s)", - prune_realtime_usage, complete_sip_prune_realtime_peer }, - - { { "sip", "prune", "realtime", "user", NULL }, - sip_prune_realtime, "Prune cached Realtime user(s)", - prune_realtime_usage, complete_sip_prune_realtime_user }, - - { { "sip", "set", "debug", NULL }, - sip_do_debug, "Enable SIP debugging", - debug_usage, NULL, &cli_sip_debug_deprecated }, - - { { "sip", "set", "debug", "ip", NULL }, - sip_do_debug, "Enable SIP debugging on IP", - debug_usage }, - - { { "sip", "set", "debug", "peer", NULL }, - sip_do_debug, "Enable SIP debugging on Peername", - debug_usage, complete_sip_debug_peer }, - - { { "sip", "set", "debug", "off", NULL }, - sip_no_debug, "Disable SIP debugging", - no_debug_usage, NULL, &cli_sip_no_debug_deprecated }, - - { { "sip", "history", NULL }, - sip_do_history, "Enable SIP history", - history_usage }, - - { { "sip", "history", "off", NULL }, - sip_no_history, "Disable SIP history", - no_history_usage }, - - { { "sip", "reload", NULL }, - sip_reload, "Reload SIP configuration", - sip_reload_usage }, -}; - -/*! \brief PBX load module - initialization */ -static int load_module(void) -{ - ASTOBJ_CONTAINER_INIT(&userl); /* User object list */ - ASTOBJ_CONTAINER_INIT(&peerl); /* Peer object list */ - ASTOBJ_CONTAINER_INIT(®l); /* Registry object list */ - - if (!(sched = sched_context_create())) { - ast_log(LOG_ERROR, "Unable to create scheduler context\n"); - return AST_MODULE_LOAD_FAILURE; - } - - if (!(io = io_context_create())) { - ast_log(LOG_ERROR, "Unable to create I/O context\n"); - sched_context_destroy(sched); - return AST_MODULE_LOAD_FAILURE; - } - - sip_reloadreason = CHANNEL_MODULE_LOAD; - - if(reload_config(sip_reloadreason)) /* Load the configuration from sip.conf */ - return AST_MODULE_LOAD_DECLINE; - - /* Make sure we can register our sip channel type */ - if (ast_channel_register(&sip_tech)) { - ast_log(LOG_ERROR, "Unable to register channel type 'SIP'\n"); - io_context_destroy(io); - sched_context_destroy(sched); - return AST_MODULE_LOAD_FAILURE; - } - - /* Register all CLI functions for SIP */ - ast_cli_register_multiple(cli_sip, sizeof(cli_sip)/ sizeof(struct ast_cli_entry)); - - /* Tell the RTP subdriver that we're here */ - ast_rtp_proto_register(&sip_rtp); - - /* Tell the UDPTL subdriver that we're here */ - ast_udptl_proto_register(&sip_udptl); - - /* Register dialplan applications */ - ast_register_application(app_dtmfmode, sip_dtmfmode, synopsis_dtmfmode, descrip_dtmfmode); - ast_register_application(app_sipaddheader, sip_addheader, synopsis_sipaddheader, descrip_sipaddheader); - - /* Register dialplan functions */ - ast_custom_function_register(&sip_header_function); - ast_custom_function_register(&sippeer_function); - ast_custom_function_register(&sipchaninfo_function); - ast_custom_function_register(&checksipdomain_function); - - /* Register manager commands */ - ast_manager_register2("SIPpeers", EVENT_FLAG_SYSTEM, manager_sip_show_peers, - "List SIP peers (text format)", mandescr_show_peers); - ast_manager_register2("SIPshowpeer", EVENT_FLAG_SYSTEM, manager_sip_show_peer, - "Show SIP peer (text format)", mandescr_show_peer); - - sip_poke_all_peers(); - sip_send_all_registers(); - - /* And start the monitor for the first time */ - restart_monitor(); - - return AST_MODULE_LOAD_SUCCESS; -} - -/*! \brief PBX unload module API */ -static int unload_module(void) -{ - struct sip_pvt *p, *pl; - - /* First, take us out of the channel type list */ - ast_channel_unregister(&sip_tech); - - /* Unregister dial plan functions */ - ast_custom_function_unregister(&sipchaninfo_function); - ast_custom_function_unregister(&sippeer_function); - ast_custom_function_unregister(&sip_header_function); - ast_custom_function_unregister(&checksipdomain_function); - - /* Unregister dial plan applications */ - ast_unregister_application(app_dtmfmode); - ast_unregister_application(app_sipaddheader); - - /* Unregister CLI commands */ - ast_cli_unregister_multiple(cli_sip, sizeof(cli_sip) / sizeof(struct ast_cli_entry)); - - /* Disconnect from the RTP subsystem */ - ast_rtp_proto_unregister(&sip_rtp); - - /* Disconnect from UDPTL */ - ast_udptl_proto_unregister(&sip_udptl); - - /* Unregister AMI actions */ - ast_manager_unregister("SIPpeers"); - ast_manager_unregister("SIPshowpeer"); - - ast_mutex_lock(&iflock); - /* Hangup all interfaces if they have an owner */ - for (p = iflist; p ; p = p->next) { - if (p->owner) - ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD); - } - ast_mutex_unlock(&iflock); - - ast_mutex_lock(&monlock); - if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) { - pthread_cancel(monitor_thread); - pthread_kill(monitor_thread, SIGURG); - pthread_join(monitor_thread, NULL); - } - monitor_thread = AST_PTHREADT_STOP; - ast_mutex_unlock(&monlock); - -restartdestroy: - ast_mutex_lock(&iflock); - /* Destroy all the interfaces and free their memory */ - p = iflist; - while (p) { - pl = p; - p = p->next; - if (__sip_destroy(pl, TRUE) < 0) { - /* Something is still bridged, let it react to getting a hangup */ - iflist = p; - ast_mutex_unlock(&iflock); - usleep(1); - goto restartdestroy; - } - } - iflist = NULL; - ast_mutex_unlock(&iflock); - - /* Free memory for local network address mask */ - ast_free_ha(localaddr); - - ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user); - ASTOBJ_CONTAINER_DESTROY(&userl); - ASTOBJ_CONTAINER_DESTROYALL(&peerl, sip_destroy_peer); - ASTOBJ_CONTAINER_DESTROY(&peerl); - ASTOBJ_CONTAINER_DESTROYALL(®l, sip_registry_destroy); - ASTOBJ_CONTAINER_DESTROY(®l); - - clear_realm_authentication(authl); - clear_sip_domains(); - close(sipsock); - sched_context_destroy(sched); - - return 0; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Session Initiation Protocol (SIP)", - .load = load_module, - .unload = unload_module, - .reload = reload, - ); diff --git a/1.4.23-rc4/channels/chan_skinny.c b/1.4.23-rc4/channels/chan_skinny.c deleted file mode 100644 index 657163398..000000000 --- a/1.4.23-rc4/channels/chan_skinny.c +++ /dev/null @@ -1,5016 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * chan_skinny was developed by Jeremy McNamara & Florian Overkamp - * chan_skinny was heavily modified/fixed by North Antara - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief Implementation of the Skinny protocol - * - * \author Jeremy McNamara & Florian Overkamp & North Antara - * \ingroup channel_drivers - */ - - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <sys/ioctl.h> -#include <net/if.h> -#include <errno.h> -#include <fcntl.h> -#include <netdb.h> -#include <arpa/inet.h> -#include <sys/signal.h> -#include <signal.h> -#include <ctype.h> - -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/lock.h" -#include "asterisk/sched.h" -#include "asterisk/io.h" -#include "asterisk/rtp.h" -#include "asterisk/acl.h" -#include "asterisk/callerid.h" -#include "asterisk/cli.h" -#include "asterisk/say.h" -#include "asterisk/cdr.h" -#include "asterisk/astdb.h" -#include "asterisk/features.h" -#include "asterisk/app.h" -#include "asterisk/musiconhold.h" -#include "asterisk/utils.h" -#include "asterisk/dsp.h" -#include "asterisk/stringfields.h" -#include "asterisk/astobj.h" -#include "asterisk/abstract_jb.h" -#include "asterisk/threadstorage.h" - -/************************************* - * Skinny/Asterisk Protocol Settings * - *************************************/ -static const char tdesc[] = "Skinny Client Control Protocol (Skinny)"; -static const char config[] = "skinny.conf"; - -static int default_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW; -static struct ast_codec_pref default_prefs; - -enum skinny_codecs { - SKINNY_CODEC_ALAW = 2, - SKINNY_CODEC_ULAW = 4, - SKINNY_CODEC_G723_1 = 9, - SKINNY_CODEC_G729A = 12, - SKINNY_CODEC_G726_32 = 82, /* XXX Which packing order does this translate to? */ - SKINNY_CODEC_H261 = 100, - SKINNY_CODEC_H263 = 101 -}; - -#define DEFAULT_SKINNY_PORT 2000 -#define DEFAULT_SKINNY_BACKLOG 2 -#define SKINNY_MAX_PACKET 1000 - -static int keep_alive = 120; -static char date_format[6] = "D-M-Y"; -static char version_id[16] = "P002F202"; - -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define letohl(x) (x) -#define letohs(x) (x) -#define htolel(x) (x) -#define htoles(x) (x) -#else -#if defined(SOLARIS) || defined(__Darwin__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) -#define __bswap_16(x) \ - ((((x) & 0xff00) >> 8) | \ - (((x) & 0x00ff) << 8)) -#define __bswap_32(x) \ - ((((x) & 0xff000000) >> 24) | \ - (((x) & 0x00ff0000) >> 8) | \ - (((x) & 0x0000ff00) << 8) | \ - (((x) & 0x000000ff) << 24)) -#else -#include <bits/byteswap.h> -#endif -#define letohl(x) __bswap_32(x) -#define letohs(x) __bswap_16(x) -#define htolel(x) __bswap_32(x) -#define htoles(x) __bswap_16(x) -#endif - -/*! Global jitterbuffer configuration - by default, jb is disabled */ -static struct ast_jb_conf default_jbconf = -{ - .flags = 0, - .max_size = -1, - .resync_threshold = -1, - .impl = "" -}; -static struct ast_jb_conf global_jbconf; - -AST_THREADSTORAGE(device2str_threadbuf, device2str_threadbuf_init); -#define DEVICE2STR_BUFSIZE 15 - -AST_THREADSTORAGE(control2str_threadbuf, control2str_threadbuf_init); -#define CONTROL2STR_BUFSIZE 100 - -/********************* - * Protocol Messages * - *********************/ -/* message types */ -#define KEEP_ALIVE_MESSAGE 0x0000 -/* no additional struct */ - -#define REGISTER_MESSAGE 0x0001 -struct register_message { - char name[16]; - uint32_t userId; - uint32_t instance; - uint32_t ip; - uint32_t type; - uint32_t maxStreams; -}; - -#define IP_PORT_MESSAGE 0x0002 - -#define KEYPAD_BUTTON_MESSAGE 0x0003 -struct keypad_button_message { - uint32_t button; - uint32_t lineInstance; - uint32_t callReference; -}; - - -#define ENBLOC_CALL_MESSAGE 0x0004 -struct enbloc_call_message { - char calledParty[24]; -}; - -#define STIMULUS_MESSAGE 0x0005 -struct stimulus_message { - uint32_t stimulus; - uint32_t stimulusInstance; - uint32_t callreference; -}; - -#define OFFHOOK_MESSAGE 0x0006 -struct offhook_message { - uint32_t unknown1; - uint32_t unknown2; -}; - -#define ONHOOK_MESSAGE 0x0007 -struct onhook_message { - uint32_t unknown1; - uint32_t unknown2; -}; - -#define CAPABILITIES_RES_MESSAGE 0x0010 -struct station_capabilities { - uint32_t codec; - uint32_t frames; - union { - char res[8]; - uint32_t rate; - } payloads; -}; - -#define SKINNY_MAX_CAPABILITIES 18 - -struct capabilities_res_message { - uint32_t count; - struct station_capabilities caps[SKINNY_MAX_CAPABILITIES]; -}; - -#define SPEED_DIAL_STAT_REQ_MESSAGE 0x000A -struct speed_dial_stat_req_message { - uint32_t speedDialNumber; -}; - -#define LINE_STATE_REQ_MESSAGE 0x000B -struct line_state_req_message { - uint32_t lineNumber; -}; - -#define TIME_DATE_REQ_MESSAGE 0x000D -#define BUTTON_TEMPLATE_REQ_MESSAGE 0x000E -#define VERSION_REQ_MESSAGE 0x000F -#define SERVER_REQUEST_MESSAGE 0x0012 - -#define ALARM_MESSAGE 0x0020 -struct alarm_message { - uint32_t alarmSeverity; - char displayMessage[80]; - uint32_t alarmParam1; - uint32_t alarmParam2; -}; - -#define OPEN_RECEIVE_CHANNEL_ACK_MESSAGE 0x0022 -struct open_receive_channel_ack_message { - uint32_t status; - uint32_t ipAddr; - uint32_t port; - uint32_t passThruId; -}; - -#define SOFT_KEY_SET_REQ_MESSAGE 0x0025 - -#define SOFT_KEY_EVENT_MESSAGE 0x0026 -struct soft_key_event_message { - uint32_t softKeyEvent; - uint32_t instance; - uint32_t callreference; -}; - -#define UNREGISTER_MESSAGE 0x0027 -#define SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028 -#define HEADSET_STATUS_MESSAGE 0x002B -#define REGISTER_AVAILABLE_LINES_MESSAGE 0x002D - -#define REGISTER_ACK_MESSAGE 0x0081 -struct register_ack_message { - uint32_t keepAlive; - char dateTemplate[6]; - char res[2]; - uint32_t secondaryKeepAlive; - char res2[4]; -}; - -#define START_TONE_MESSAGE 0x0082 -struct start_tone_message { - uint32_t tone; - uint32_t space; - uint32_t instance; - uint32_t reference; -}; - -#define STOP_TONE_MESSAGE 0x0083 -struct stop_tone_message { - uint32_t instance; - uint32_t reference; -}; - -#define SET_RINGER_MESSAGE 0x0085 -struct set_ringer_message { - uint32_t ringerMode; - uint32_t unknown1; /* See notes in transmit_ringer_mode */ - uint32_t unknown2; - uint32_t space[2]; -}; - -#define SET_LAMP_MESSAGE 0x0086 -struct set_lamp_message { - uint32_t stimulus; - uint32_t stimulusInstance; - uint32_t deviceStimulus; -}; - -#define SET_SPEAKER_MESSAGE 0x0088 -struct set_speaker_message { - uint32_t mode; -}; - -/* XXX When do we need to use this? */ -#define SET_MICROPHONE_MESSAGE 0x0089 -struct set_microphone_message { - uint32_t mode; -}; - -#define START_MEDIA_TRANSMISSION_MESSAGE 0x008A -struct media_qualifier { - uint32_t precedence; - uint32_t vad; - uint16_t packets; - uint32_t bitRate; -}; - -struct start_media_transmission_message { - uint32_t conferenceId; - uint32_t passThruPartyId; - uint32_t remoteIp; - uint32_t remotePort; - uint32_t packetSize; - uint32_t payloadType; - struct media_qualifier qualifier; - uint32_t space[16]; -}; - -#define STOP_MEDIA_TRANSMISSION_MESSAGE 0x008B -struct stop_media_transmission_message { - uint32_t conferenceId; - uint32_t passThruPartyId; - uint32_t space[3]; -}; - -#define CALL_INFO_MESSAGE 0x008F -struct call_info_message { - char callingPartyName[40]; - char callingParty[24]; - char calledPartyName[40]; - char calledParty[24]; - uint32_t instance; - uint32_t reference; - uint32_t type; - char originalCalledPartyName[40]; - char originalCalledParty[24]; - char lastRedirectingPartyName[40]; - char lastRedirectingParty[24]; - uint32_t originalCalledPartyRedirectReason; - uint32_t lastRedirectingReason; - char callingPartyVoiceMailbox[24]; - char calledPartyVoiceMailbox[24]; - char originalCalledPartyVoiceMailbox[24]; - char lastRedirectingVoiceMailbox[24]; - uint32_t space[3]; -}; - -#define SPEED_DIAL_STAT_RES_MESSAGE 0x0091 -struct speed_dial_stat_res_message { - uint32_t speedDialNumber; - char speedDialDirNumber[24]; - char speedDialDisplayName[40]; -}; - -#define LINE_STAT_RES_MESSAGE 0x0092 -struct line_stat_res_message { - uint32_t lineNumber; - char lineDirNumber[24]; - char lineDisplayName[24]; - uint32_t space[15]; -}; - -#define DEFINETIMEDATE_MESSAGE 0x0094 -struct definetimedate_message { - uint32_t year; /* since 1900 */ - uint32_t month; - uint32_t dayofweek; /* monday = 1 */ - uint32_t day; - uint32_t hour; - uint32_t minute; - uint32_t seconds; - uint32_t milliseconds; - uint32_t timestamp; -}; - -#define BUTTON_TEMPLATE_RES_MESSAGE 0x0097 -struct button_definition { - uint8_t instanceNumber; - uint8_t buttonDefinition; -}; - -struct button_definition_template { - uint8_t buttonDefinition; - /* for now, anything between 0xB0 and 0xCF is custom */ - /*int custom;*/ -}; - -#define STIMULUS_REDIAL 0x01 -#define STIMULUS_SPEEDDIAL 0x02 -#define STIMULUS_HOLD 0x03 -#define STIMULUS_TRANSFER 0x04 -#define STIMULUS_FORWARDALL 0x05 -#define STIMULUS_FORWARDBUSY 0x06 -#define STIMULUS_FORWARDNOANSWER 0x07 -#define STIMULUS_DISPLAY 0x08 -#define STIMULUS_LINE 0x09 -#define STIMULUS_VOICEMAIL 0x0F -#define STIMULUS_AUTOANSWER 0x11 -#define STIMULUS_CONFERENCE 0x7D -#define STIMULUS_CALLPARK 0x7E -#define STIMULUS_CALLPICKUP 0x7F -#define STIMULUS_NONE 0xFF - -/* Button types */ -#define BT_REDIAL STIMULUS_REDIAL -#define BT_SPEEDDIAL STIMULUS_SPEEDDIAL -#define BT_HOLD STIMULUS_HOLD -#define BT_TRANSFER STIMULUS_TRANSFER -#define BT_FORWARDALL STIMULUS_FORWARDALL -#define BT_FORWARDBUSY STIMULUS_FORWARDBUSY -#define BT_FORWARDNOANSWER STIMULUS_FORWARDNOANSWER -#define BT_DISPLAY STIMULUS_DISPLAY -#define BT_LINE STIMULUS_LINE -#define BT_VOICEMAIL STIMULUS_VOICEMAIL -#define BT_AUTOANSWER STIMULUS_AUTOANSWER -#define BT_CONFERENCE STIMULUS_CONFERENCE -#define BT_CALLPARK STIMULUS_CALLPARK -#define BT_CALLPICKUP STIMULUS_CALLPICKUP -#define BT_NONE 0x00 - -/* Custom button types - add our own between 0xB0 and 0xCF. - This may need to be revised in the future, - if stimuluses are ever added in this range. */ -#define BT_CUST_LINESPEEDDIAL 0xB0 /* line or speeddial */ -#define BT_CUST_HINT 0xB1 /* pipe dream */ - -struct button_template_res_message { - uint32_t buttonOffset; - uint32_t buttonCount; - uint32_t totalButtonCount; - struct button_definition definition[42]; -}; - -#define VERSION_RES_MESSAGE 0x0098 -struct version_res_message { - char version[16]; -}; - -#define DISPLAYTEXT_MESSAGE 0x0099 -struct displaytext_message { - char text[40]; -}; - -#define CLEAR_NOTIFY_MESSAGE 0x0115 -#define CLEAR_DISPLAY_MESSAGE 0x009A - -#define CAPABILITIES_REQ_MESSAGE 0x009B - -#define REGISTER_REJ_MESSAGE 0x009D -struct register_rej_message { - char errMsg[33]; -}; - -#define SERVER_RES_MESSAGE 0x009E -struct server_identifier { - char serverName[48]; -}; - -struct server_res_message { - struct server_identifier server[5]; - uint32_t serverListenPort[5]; - uint32_t serverIpAddr[5]; -}; - -#define RESET_MESSAGE 0x009F -struct reset_message { - uint32_t resetType; -}; - -#define KEEP_ALIVE_ACK_MESSAGE 0x0100 - -#define OPEN_RECEIVE_CHANNEL_MESSAGE 0x0105 -struct open_receive_channel_message { - uint32_t conferenceId; - uint32_t partyId; - uint32_t packets; - uint32_t capability; - uint32_t echo; - uint32_t bitrate; - uint32_t space[16]; -}; - -#define CLOSE_RECEIVE_CHANNEL_MESSAGE 0x0106 -struct close_receive_channel_message { - uint32_t conferenceId; - uint32_t partyId; - uint32_t space[2]; -}; - -#define SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108 - -struct soft_key_template_definition { - char softKeyLabel[16]; - uint32_t softKeyEvent; -}; - -#define KEYDEF_ONHOOK 0 -#define KEYDEF_CONNECTED 1 -#define KEYDEF_ONHOLD 2 -#define KEYDEF_RINGIN 3 -#define KEYDEF_OFFHOOK 4 -#define KEYDEF_CONNWITHTRANS 5 -#define KEYDEF_DADFD 6 /* Digits After Dialing First Digit */ -#define KEYDEF_CONNWITHCONF 7 -#define KEYDEF_RINGOUT 8 -#define KEYDEF_OFFHOOKWITHFEAT 9 -#define KEYDEF_UNKNOWN 10 - -#define SOFTKEY_NONE 0x00 -#define SOFTKEY_REDIAL 0x01 -#define SOFTKEY_NEWCALL 0x02 -#define SOFTKEY_HOLD 0x03 -#define SOFTKEY_TRNSFER 0x04 -#define SOFTKEY_CFWDALL 0x05 -#define SOFTKEY_CFWDBUSY 0x06 -#define SOFTKEY_CFWDNOANSWER 0x07 -#define SOFTKEY_BKSPC 0x08 -#define SOFTKEY_ENDCALL 0x09 -#define SOFTKEY_RESUME 0x0A -#define SOFTKEY_ANSWER 0x0B -#define SOFTKEY_INFO 0x0C -#define SOFTKEY_CONFRN 0x0D -#define SOFTKEY_PARK 0x0E -#define SOFTKEY_JOIN 0x0F -#define SOFTKEY_MEETME 0x10 -#define SOFTKEY_PICKUP 0x11 -#define SOFTKEY_GPICKUP 0x12 - -struct soft_key_template_definition soft_key_template_default[] = { - { "Redial", 0x01 }, - { "NewCall", 0x02 }, - { "Hold", 0x03 }, - { "Trnsfer", 0x04 }, - { "CFwdAll", 0x05 }, - { "CFwdBusy", 0x06 }, - { "CFwdNoAnswer", 0x07 }, - { "<<", 0x08 }, - { "EndCall", 0x09 }, - { "Resume", 0x0A }, - { "Answer", 0x0B }, - { "Info", 0x0C }, - { "Confrn", 0x0D }, - { "Park", 0x0E }, - { "Join", 0x0F }, - { "MeetMe", 0x10 }, - { "PickUp", 0x11 }, - { "GPickUp", 0x12 }, -}; - -struct soft_key_definitions { - const uint8_t mode; - const uint8_t *defaults; - const int count; -}; - -static const uint8_t soft_key_default_onhook[] = { - SOFTKEY_REDIAL, - SOFTKEY_NEWCALL, - SOFTKEY_CFWDALL, - SOFTKEY_CFWDBUSY, - /*SOFTKEY_GPICKUP, - SOFTKEY_CONFRN,*/ -}; - -static const uint8_t soft_key_default_connected[] = { - SOFTKEY_HOLD, - SOFTKEY_ENDCALL, - SOFTKEY_TRNSFER, - SOFTKEY_PARK, - SOFTKEY_CFWDALL, - SOFTKEY_CFWDBUSY, -}; - -static const uint8_t soft_key_default_onhold[] = { - SOFTKEY_RESUME, - SOFTKEY_NEWCALL, - SOFTKEY_ENDCALL, - SOFTKEY_TRNSFER, -}; - -static const uint8_t soft_key_default_ringin[] = { - SOFTKEY_ANSWER, - SOFTKEY_ENDCALL, - SOFTKEY_TRNSFER, -}; - -static const uint8_t soft_key_default_offhook[] = { - SOFTKEY_REDIAL, - SOFTKEY_ENDCALL, - SOFTKEY_CFWDALL, - SOFTKEY_CFWDBUSY, - /*SOFTKEY_GPICKUP,*/ -}; - -static const uint8_t soft_key_default_connwithtrans[] = { - SOFTKEY_HOLD, - SOFTKEY_ENDCALL, - SOFTKEY_TRNSFER, - SOFTKEY_PARK, - SOFTKEY_CFWDALL, - SOFTKEY_CFWDBUSY, -}; - -static const uint8_t soft_key_default_dadfd[] = { - SOFTKEY_BKSPC, - SOFTKEY_ENDCALL, -}; - -static const uint8_t soft_key_default_connwithconf[] = { - SOFTKEY_NONE, -}; - -static const uint8_t soft_key_default_ringout[] = { - SOFTKEY_NONE, - SOFTKEY_ENDCALL, -}; - -static const uint8_t soft_key_default_offhookwithfeat[] = { - SOFTKEY_REDIAL, - SOFTKEY_ENDCALL, -}; - -static const uint8_t soft_key_default_unknown[] = { - SOFTKEY_NONE, -}; - -static const struct soft_key_definitions soft_key_default_definitions[] = { - {KEYDEF_ONHOOK, soft_key_default_onhook, sizeof(soft_key_default_onhook) / sizeof(uint8_t)}, - {KEYDEF_CONNECTED, soft_key_default_connected, sizeof(soft_key_default_connected) / sizeof(uint8_t)}, - {KEYDEF_ONHOLD, soft_key_default_onhold, sizeof(soft_key_default_onhold) / sizeof(uint8_t)}, - {KEYDEF_RINGIN, soft_key_default_ringin, sizeof(soft_key_default_ringin) / sizeof(uint8_t)}, - {KEYDEF_OFFHOOK, soft_key_default_offhook, sizeof(soft_key_default_offhook) / sizeof(uint8_t)}, - {KEYDEF_CONNWITHTRANS, soft_key_default_connwithtrans, sizeof(soft_key_default_connwithtrans) / sizeof(uint8_t)}, - {KEYDEF_DADFD, soft_key_default_dadfd, sizeof(soft_key_default_dadfd) / sizeof(uint8_t)}, - {KEYDEF_CONNWITHCONF, soft_key_default_connwithconf, sizeof(soft_key_default_connwithconf) / sizeof(uint8_t)}, - {KEYDEF_RINGOUT, soft_key_default_ringout, sizeof(soft_key_default_ringout) / sizeof(uint8_t)}, - {KEYDEF_OFFHOOKWITHFEAT, soft_key_default_offhookwithfeat, sizeof(soft_key_default_offhookwithfeat) / sizeof(uint8_t)}, - {KEYDEF_UNKNOWN, soft_key_default_unknown, sizeof(soft_key_default_unknown) / sizeof(uint8_t)} -}; - -struct soft_key_template_res_message { - uint32_t softKeyOffset; - uint32_t softKeyCount; - uint32_t totalSoftKeyCount; - struct soft_key_template_definition softKeyTemplateDefinition[32]; -}; - -#define SOFT_KEY_SET_RES_MESSAGE 0x0109 - -struct soft_key_set_definition { - uint8_t softKeyTemplateIndex[16]; - uint16_t softKeyInfoIndex[16]; -}; - -struct soft_key_set_res_message { - uint32_t softKeySetOffset; - uint32_t softKeySetCount; - uint32_t totalSoftKeySetCount; - struct soft_key_set_definition softKeySetDefinition[16]; - uint32_t res; -}; - -#define SELECT_SOFT_KEYS_MESSAGE 0x0110 -struct select_soft_keys_message { - uint32_t instance; - uint32_t reference; - uint32_t softKeySetIndex; - uint32_t validKeyMask; -}; - -#define CALL_STATE_MESSAGE 0x0111 -struct call_state_message { - uint32_t callState; - uint32_t lineInstance; - uint32_t callReference; - uint32_t space[3]; -}; - -#define DISPLAY_PROMPT_STATUS_MESSAGE 0x0112 -struct display_prompt_status_message { - uint32_t messageTimeout; - char promptMessage[32]; - uint32_t lineInstance; - uint32_t callReference; -}; - -#define CLEAR_PROMPT_MESSAGE 0x0113 -struct clear_prompt_message { - uint32_t lineInstance; - uint32_t callReference; -}; - -#define DISPLAY_NOTIFY_MESSAGE 0x0114 -struct display_notify_message { - uint32_t displayTimeout; - char displayMessage[100]; -}; - -#define ACTIVATE_CALL_PLANE_MESSAGE 0x0116 -struct activate_call_plane_message { - uint32_t lineInstance; -}; - -#define DIALED_NUMBER_MESSAGE 0x011D -struct dialed_number_message { - char dialedNumber[24]; - uint32_t lineInstance; - uint32_t callReference; -}; - -union skinny_data { - struct alarm_message alarm; - struct speed_dial_stat_req_message speeddialreq; - struct register_message reg; - struct register_ack_message regack; - struct register_rej_message regrej; - struct capabilities_res_message caps; - struct version_res_message version; - struct button_template_res_message buttontemplate; - struct displaytext_message displaytext; - struct display_prompt_status_message displaypromptstatus; - struct clear_prompt_message clearpromptstatus; - struct definetimedate_message definetimedate; - struct start_tone_message starttone; - struct stop_tone_message stoptone; - struct speed_dial_stat_res_message speeddial; - struct line_state_req_message line; - struct line_stat_res_message linestat; - struct soft_key_set_res_message softkeysets; - struct soft_key_template_res_message softkeytemplate; - struct server_res_message serverres; - struct reset_message reset; - struct set_lamp_message setlamp; - struct set_ringer_message setringer; - struct call_state_message callstate; - struct keypad_button_message keypad; - struct select_soft_keys_message selectsoftkey; - struct activate_call_plane_message activatecallplane; - struct stimulus_message stimulus; - struct offhook_message offhook; - struct onhook_message onhook; - struct set_speaker_message setspeaker; - struct set_microphone_message setmicrophone; - struct call_info_message callinfo; - struct start_media_transmission_message startmedia; - struct stop_media_transmission_message stopmedia; - struct open_receive_channel_message openreceivechannel; - struct open_receive_channel_ack_message openreceivechannelack; - struct close_receive_channel_message closereceivechannel; - struct display_notify_message displaynotify; - struct dialed_number_message dialednumber; - struct soft_key_event_message softkeyeventmessage; - struct enbloc_call_message enbloccallmessage; -}; - -/* packet composition */ -struct skinny_req { - int len; - int res; - int e; - union skinny_data data; -}; - -/* XXX This is the combined size of the variables above. (len, res, e) - If more are added, this MUST change. - (sizeof(skinny_req) - sizeof(skinny_data)) DOES NOT WORK on all systems (amd64?). */ -int skinny_header_size = 12; - -/***************************** - * Asterisk specific globals * - *****************************/ - -static int skinnydebug = 0; - -/* a hostname, portnumber, socket and such is usefull for VoIP protocols */ -static struct sockaddr_in bindaddr; -static char ourhost[256]; -static int ourport; -static struct in_addr __ourip; -struct ast_hostent ahp; -struct hostent *hp; -static int skinnysock = -1; -static pthread_t accept_t; -static char context[AST_MAX_CONTEXT] = "default"; -static char language[MAX_LANGUAGE] = ""; -static char mohinterpret[MAX_MUSICCLASS] = "default"; -static char mohsuggest[MAX_MUSICCLASS] = ""; -static char cid_num[AST_MAX_EXTENSION] = ""; -static char cid_name[AST_MAX_EXTENSION] = ""; -static char linelabel[AST_MAX_EXTENSION] =""; -static int nat = 0; -static ast_group_t cur_callergroup = 0; -static ast_group_t cur_pickupgroup = 0; -static int immediate = 0; -static int callwaiting = 0; -static int callreturn = 0; -static int threewaycalling = 0; -static int mwiblink = 0; -/* This is for flashhook transfers */ -static int transfer = 0; -static int cancallforward = 0; -/* static int busycount = 3;*/ -static char accountcode[AST_MAX_ACCOUNT_CODE] = ""; -static char mailbox[AST_MAX_EXTENSION]; -static int amaflags = 0; -static int callnums = 1; - -#define SKINNY_DEVICE_UNKNOWN -1 -#define SKINNY_DEVICE_NONE 0 -#define SKINNY_DEVICE_30SPPLUS 1 -#define SKINNY_DEVICE_12SPPLUS 2 -#define SKINNY_DEVICE_12SP 3 -#define SKINNY_DEVICE_12 4 -#define SKINNY_DEVICE_30VIP 5 -#define SKINNY_DEVICE_7910 6 -#define SKINNY_DEVICE_7960 7 -#define SKINNY_DEVICE_7940 8 -#define SKINNY_DEVICE_7935 9 -#define SKINNY_DEVICE_ATA186 12 /* Cisco ATA-186 */ -#define SKINNY_DEVICE_7941 115 -#define SKINNY_DEVICE_7971 119 -#define SKINNY_DEVICE_7914 124 /* Expansion module */ -#define SKINNY_DEVICE_7985 302 -#define SKINNY_DEVICE_7911 307 -#define SKINNY_DEVICE_7961GE 308 -#define SKINNY_DEVICE_7941GE 309 -#define SKINNY_DEVICE_7931 348 -#define SKINNY_DEVICE_7921 365 -#define SKINNY_DEVICE_7906 369 -#define SKINNY_DEVICE_7962 404 /* Not found */ -#define SKINNY_DEVICE_7937 431 -#define SKINNY_DEVICE_7942 434 -#define SKINNY_DEVICE_7945 435 -#define SKINNY_DEVICE_7965 436 -#define SKINNY_DEVICE_7975 437 -#define SKINNY_DEVICE_7905 20000 -#define SKINNY_DEVICE_7920 30002 -#define SKINNY_DEVICE_7970 30006 -#define SKINNY_DEVICE_7912 30007 -#define SKINNY_DEVICE_7902 30008 -#define SKINNY_DEVICE_CIPC 30016 /* Cisco IP Communicator */ -#define SKINNY_DEVICE_7961 30018 -#define SKINNY_DEVICE_7936 30019 -#define SKINNY_DEVICE_SCCPGATEWAY_AN 30027 /* ??? */ -#define SKINNY_DEVICE_SCCPGATEWAY_BRI 30028 /* ??? */ - -#define SKINNY_SPEAKERON 1 -#define SKINNY_SPEAKEROFF 2 - -#define SKINNY_MICON 1 -#define SKINNY_MICOFF 2 - -#define SKINNY_OFFHOOK 1 -#define SKINNY_ONHOOK 2 -#define SKINNY_RINGOUT 3 -#define SKINNY_RINGIN 4 -#define SKINNY_CONNECTED 5 -#define SKINNY_BUSY 6 -#define SKINNY_CONGESTION 7 -#define SKINNY_HOLD 8 -#define SKINNY_CALLWAIT 9 -#define SKINNY_TRANSFER 10 -#define SKINNY_PARK 11 -#define SKINNY_PROGRESS 12 -#define SKINNY_INVALID 14 - -#define SKINNY_SILENCE 0x00 -#define SKINNY_DIALTONE 0x21 -#define SKINNY_BUSYTONE 0x23 -#define SKINNY_ALERT 0x24 -#define SKINNY_REORDER 0x25 -#define SKINNY_CALLWAITTONE 0x2D -#define SKINNY_NOTONE 0x7F - -#define SKINNY_LAMP_OFF 1 -#define SKINNY_LAMP_ON 2 -#define SKINNY_LAMP_WINK 3 -#define SKINNY_LAMP_FLASH 4 -#define SKINNY_LAMP_BLINK 5 - -#define SKINNY_RING_OFF 1 -#define SKINNY_RING_INSIDE 2 -#define SKINNY_RING_OUTSIDE 3 -#define SKINNY_RING_FEATURE 4 - -#define TYPE_TRUNK 1 -#define TYPE_LINE 2 - -/* Skinny rtp stream modes. Do we really need this? */ -#define SKINNY_CX_SENDONLY 0 -#define SKINNY_CX_RECVONLY 1 -#define SKINNY_CX_SENDRECV 2 -#define SKINNY_CX_CONF 3 -#define SKINNY_CX_CONFERENCE 3 -#define SKINNY_CX_MUTE 4 -#define SKINNY_CX_INACTIVE 4 - -#if 0 -static char *skinny_cxmodes[] = { - "sendonly", - "recvonly", - "sendrecv", - "confrnce", - "inactive" -}; -#endif - -/* driver scheduler */ -static struct sched_context *sched = NULL; -static struct io_context *io; - -/* Protect the monitoring thread, so only one process can kill or start it, and not - when it's doing something critical. */ -AST_MUTEX_DEFINE_STATIC(monlock); -/* Protect the network socket */ -AST_MUTEX_DEFINE_STATIC(netlock); -/* Protect the session list */ -AST_MUTEX_DEFINE_STATIC(sessionlock); -/* Protect the device list */ -AST_MUTEX_DEFINE_STATIC(devicelock); -#if 0 -/* Protect the paging device list */ -AST_MUTEX_DEFINE_STATIC(pagingdevicelock); -#endif - -/* This is the thread for the monitor which checks for input on the channels - which are not currently in use. */ -static pthread_t monitor_thread = AST_PTHREADT_NULL; - -/* Wait up to 16 seconds for first digit */ -static int firstdigittimeout = 16000; - -/* How long to wait for following digits */ -static int gendigittimeout = 8000; - -/* How long to wait for an extra digit, if there is an ambiguous match */ -static int matchdigittimeout = 3000; - -struct skinny_subchannel { - ast_mutex_t lock; - struct ast_channel *owner; - struct ast_rtp *rtp; - struct ast_rtp *vrtp; - unsigned int callid; - /* time_t lastouttime; */ /* Unused */ - int progress; - int ringing; - int onhold; - /* int lastout; */ /* Unused */ - int cxmode; - int nat; - int outgoing; - int alreadygone; - - struct skinny_subchannel *next; - struct skinny_line *parent; -}; - -struct skinny_line { - ast_mutex_t lock; - char name[80]; - char label[24]; /* Label that shows next to the line buttons */ - char accountcode[AST_MAX_ACCOUNT_CODE]; - char exten[AST_MAX_EXTENSION]; /* Extension where to start */ - char context[AST_MAX_CONTEXT]; - char language[MAX_LANGUAGE]; - char cid_num[AST_MAX_EXTENSION]; /* Caller*ID */ - char cid_name[AST_MAX_EXTENSION]; /* Caller*ID */ - char lastcallerid[AST_MAX_EXTENSION]; /* Last Caller*ID */ - char call_forward[AST_MAX_EXTENSION]; - char mailbox[AST_MAX_EXTENSION]; - char mohinterpret[MAX_MUSICCLASS]; - char mohsuggest[MAX_MUSICCLASS]; - char lastnumberdialed[AST_MAX_EXTENSION]; /* Last number that was dialed - used for redial */ - int curtone; /* Current tone being played */ - ast_group_t callgroup; - ast_group_t pickupgroup; - int callwaiting; - int transfer; - int threewaycalling; - int mwiblink; - int cancallforward; - int callreturn; - int dnd; /* How does this affect callwait? Do we just deny a skinny_request if we're dnd? */ - int hascallerid; - int hidecallerid; - int amaflags; - int type; - int instance; - int group; - int needdestroy; - int capability; - int nonCodecCapability; - int onhooktime; - int msgstate; /* voicemail message state */ - int immediate; - int hookstate; - int nat; - - struct ast_codec_pref prefs; - struct skinny_subchannel *sub; - struct skinny_line *next; - struct skinny_device *parent; -}; - -struct skinny_speeddial { - ast_mutex_t lock; - char label[42]; - char exten[AST_MAX_EXTENSION]; - int instance; - - struct skinny_speeddial *next; - struct skinny_device *parent; -}; - -struct skinny_addon { - ast_mutex_t lock; - char type[10]; - - struct skinny_addon *next; - struct skinny_device *parent; -}; - -static struct skinny_device { - /* A device containing one or more lines */ - char name[80]; - char id[16]; - char version_id[16]; - int type; - int registered; - int lastlineinstance; - int lastcallreference; - int capability; - int earlyrtp; - char exten[AST_MAX_EXTENSION]; - struct sockaddr_in addr; - struct in_addr ourip; - struct skinny_line *lines; - struct skinny_speeddial *speeddials; - struct skinny_addon *addons; - struct ast_codec_pref prefs; - struct ast_ha *ha; - struct skinnysession *session; - struct skinny_device *next; -} *devices = NULL; - -struct skinny_paging_device { - char name[80]; - char id[16]; - struct skinny_device ** devices; - struct skinny_paging_device *next; -}; - -static struct skinnysession { - pthread_t t; - ast_mutex_t lock; - struct sockaddr_in sin; - int fd; - char inbuf[SKINNY_MAX_PACKET]; - char outbuf[SKINNY_MAX_PACKET]; - struct skinny_device *device; - struct skinnysession *next; -} *sessions = NULL; - -static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause); -static int skinny_call(struct ast_channel *ast, char *dest, int timeout); -static int skinny_hangup(struct ast_channel *ast); -static int skinny_answer(struct ast_channel *ast); -static struct ast_frame *skinny_read(struct ast_channel *ast); -static int skinny_write(struct ast_channel *ast, struct ast_frame *frame); -static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen); -static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -static int skinny_senddigit_begin(struct ast_channel *ast, char digit); -static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s); - -static const struct ast_channel_tech skinny_tech = { - .type = "Skinny", - .description = tdesc, - .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1), - .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER, - .requester = skinny_request, - .call = skinny_call, - .hangup = skinny_hangup, - .answer = skinny_answer, - .read = skinny_read, - .write = skinny_write, - .indicate = skinny_indicate, - .fixup = skinny_fixup, - .send_digit_begin = skinny_senddigit_begin, - .send_digit_end = skinny_senddigit_end, -/* .bridge = ast_rtp_bridge, */ -}; - -static void *get_button_template(struct skinnysession *s, struct button_definition_template *btn) -{ - struct skinny_device *d = s->device; - struct skinny_addon *a = d->addons; - int i; - - switch (d->type) { - case SKINNY_DEVICE_30SPPLUS: - case SKINNY_DEVICE_30VIP: - /* 13 rows, 2 columns */ - for (i = 0; i < 4; i++) - (btn++)->buttonDefinition = BT_LINE; - (btn++)->buttonDefinition = BT_REDIAL; - (btn++)->buttonDefinition = BT_VOICEMAIL; - (btn++)->buttonDefinition = BT_CALLPARK; - (btn++)->buttonDefinition = BT_FORWARDALL; - (btn++)->buttonDefinition = BT_CONFERENCE; - for (i = 0; i < 4; i++) - (btn++)->buttonDefinition = BT_NONE; - for (i = 0; i < 13; i++) - (btn++)->buttonDefinition = BT_SPEEDDIAL; - - break; - case SKINNY_DEVICE_12SPPLUS: - case SKINNY_DEVICE_12SP: - case SKINNY_DEVICE_12: - /* 6 rows, 2 columns */ - for (i = 0; i < 2; i++) - (btn++)->buttonDefinition = BT_LINE; - (btn++)->buttonDefinition = BT_REDIAL; - for (i = 0; i < 3; i++) - (btn++)->buttonDefinition = BT_SPEEDDIAL; - (btn++)->buttonDefinition = BT_HOLD; - (btn++)->buttonDefinition = BT_TRANSFER; - (btn++)->buttonDefinition = BT_FORWARDALL; - (btn++)->buttonDefinition = BT_CALLPARK; - (btn++)->buttonDefinition = BT_VOICEMAIL; - (btn++)->buttonDefinition = BT_CONFERENCE; - break; - case SKINNY_DEVICE_7910: - (btn++)->buttonDefinition = BT_LINE; - (btn++)->buttonDefinition = BT_HOLD; - (btn++)->buttonDefinition = BT_TRANSFER; - (btn++)->buttonDefinition = BT_DISPLAY; - (btn++)->buttonDefinition = BT_VOICEMAIL; - (btn++)->buttonDefinition = BT_CONFERENCE; - (btn++)->buttonDefinition = BT_FORWARDALL; - for (i = 0; i < 2; i++) - (btn++)->buttonDefinition = BT_SPEEDDIAL; - (btn++)->buttonDefinition = BT_REDIAL; - break; - case SKINNY_DEVICE_7960: - case SKINNY_DEVICE_7961: - case SKINNY_DEVICE_7961GE: - case SKINNY_DEVICE_7962: - case SKINNY_DEVICE_7965: - for (i = 0; i < 6; i++) - (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL; - break; - case SKINNY_DEVICE_7940: - case SKINNY_DEVICE_7941: - case SKINNY_DEVICE_7941GE: - case SKINNY_DEVICE_7942: - case SKINNY_DEVICE_7945: - for (i = 0; i < 2; i++) - (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL; - break; - case SKINNY_DEVICE_7935: - case SKINNY_DEVICE_7936: - for (i = 0; i < 2; i++) - (btn++)->buttonDefinition = BT_LINE; - break; - case SKINNY_DEVICE_ATA186: - (btn++)->buttonDefinition = BT_LINE; - break; - case SKINNY_DEVICE_7970: - case SKINNY_DEVICE_7971: - case SKINNY_DEVICE_7975: - case SKINNY_DEVICE_CIPC: - for (i = 0; i < 8; i++) - (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL; - break; - case SKINNY_DEVICE_7985: - /* XXX I have no idea what the buttons look like on these. */ - ast_log(LOG_WARNING, "Unsupported device type '%d (7985)' found.\n", d->type); - break; - case SKINNY_DEVICE_7912: - case SKINNY_DEVICE_7911: - case SKINNY_DEVICE_7905: - (btn++)->buttonDefinition = BT_LINE; - (btn++)->buttonDefinition = BT_HOLD; - break; - case SKINNY_DEVICE_7920: - /* XXX I don't know if this is right. */ - for (i = 0; i < 4; i++) - (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL; - break; - case SKINNY_DEVICE_7921: - for (i = 0; i < 6; i++) - (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL; - break; - case SKINNY_DEVICE_7902: - ast_log(LOG_WARNING, "Unsupported device type '%d (7902)' found.\n", d->type); - break; - case SKINNY_DEVICE_7906: - ast_log(LOG_WARNING, "Unsupported device type '%d (7906)' found.\n", d->type); - break; - case SKINNY_DEVICE_7931: - ast_log(LOG_WARNING, "Unsupported device type '%d (7931)' found.\n", d->type); - break; - case SKINNY_DEVICE_7937: - ast_log(LOG_WARNING, "Unsupported device type '%d (7937)' found.\n", d->type); - break; - case SKINNY_DEVICE_7914: - ast_log(LOG_WARNING, "Unsupported device type '%d (7914)' found. Expansion module registered by itself?\n", d->type); - break; - case SKINNY_DEVICE_SCCPGATEWAY_AN: - case SKINNY_DEVICE_SCCPGATEWAY_BRI: - ast_log(LOG_WARNING, "Unsupported device type '%d (SCCP gateway)' found.\n", d->type); - break; - default: - ast_log(LOG_WARNING, "Unknown device type '%d' found.\n", d->type); - break; - } - - for (a = d->addons; a; a = a->next) { - if (!strcasecmp(a->type, "7914")) { - for (i = 0; i < 14; i++) - (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL; - } else { - ast_log(LOG_WARNING, "Unknown addon type '%s' found. Skipping.\n", a->type); - } - } - - return btn; -} - -static struct skinny_req *req_alloc(size_t size, int response_message) -{ - struct skinny_req *req; - - if (!(req = ast_calloc(1, skinny_header_size + size + 4))) - return NULL; - - req->len = htolel(size+4); - req->e = htolel(response_message); - - return req; -} - -static struct skinny_line *find_line_by_instance(struct skinny_device *d, int instance) -{ - struct skinny_line *l; - - if (!instance) - instance = 1; - - for (l = d->lines; l; l = l->next) { - if (l->instance == instance) - break; - } - - if (!l) { - ast_log(LOG_WARNING, "Could not find line with instance '%d' on device '%s'\n", instance, d->name); - } - return l; -} - -static struct skinny_line *find_line_by_name(const char *dest) -{ - struct skinny_line *l; - struct skinny_device *d; - char line[256]; - char *at; - char *device; - - ast_copy_string(line, dest, sizeof(line)); - at = strchr(line, '@'); - if (!at) { - ast_log(LOG_NOTICE, "Device '%s' has no @ (at) sign!\n", dest); - return NULL; - } - *at++ = '\0'; - device = at; - ast_mutex_lock(&devicelock); - for (d = devices; d; d = d->next) { - if (!strcasecmp(d->name, device)) { - if (skinnydebug) - ast_verbose("Found device: %s\n", d->name); - /* Found the device */ - for (l = d->lines; l; l = l->next) { - /* Search for the right line */ - if (!strcasecmp(l->name, line)) { - ast_mutex_unlock(&devicelock); - return l; - } - } - } - } - /* Device not found */ - ast_mutex_unlock(&devicelock); - return NULL; -} - -/* It's quicker/easier to find the subchannel when we know the instance number too */ -static struct skinny_subchannel *find_subchannel_by_instance_reference(struct skinny_device *d, int instance, int reference) -{ - struct skinny_line *l = find_line_by_instance(d, instance); - struct skinny_subchannel *sub; - - if (!l) { - return NULL; - } - - if (!reference) - sub = l->sub; - else { - for (sub = l->sub; sub; sub = sub->next) { - if (sub->callid == reference) - break; - } - } - - if (!sub) { - ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s'\n", reference, d->name); - } - return sub; -} - -/* Find the subchannel when we only have the callid - this shouldn't happen often */ -static struct skinny_subchannel *find_subchannel_by_reference(struct skinny_device *d, int reference) -{ - struct skinny_line *l; - struct skinny_subchannel *sub = NULL; - - for (l = d->lines; l; l = l->next) { - for (sub = l->sub; sub; sub = sub->next) { - if (sub->callid == reference) - break; - } - if (sub) - break; - } - - if (!l) { - ast_log(LOG_WARNING, "Could not find any lines that contained a subchannel with reference '%d' on device '%s'\n", reference, d->name); - } else { - if (!sub) { - ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s@%s'\n", reference, l->name, d->name); - } - } - return sub; -} - -static struct skinny_speeddial *find_speeddial_by_instance(struct skinny_device *d, int instance) -{ - struct skinny_speeddial *sd; - - for (sd = d->speeddials; sd; sd = sd->next) { - if (sd->instance == instance) - break; - } - - if (!sd) { - ast_log(LOG_WARNING, "Could not find speeddial with instance '%d' on device '%s'\n", instance, d->name); - } - return sd; -} - -static int codec_skinny2ast(enum skinny_codecs skinnycodec) -{ - switch (skinnycodec) { - case SKINNY_CODEC_ALAW: - return AST_FORMAT_ALAW; - case SKINNY_CODEC_ULAW: - return AST_FORMAT_ULAW; - case SKINNY_CODEC_G723_1: - return AST_FORMAT_G723_1; - case SKINNY_CODEC_G729A: - return AST_FORMAT_G729A; - case SKINNY_CODEC_G726_32: - return AST_FORMAT_G726_AAL2; /* XXX Is this right? */ - case SKINNY_CODEC_H261: - return AST_FORMAT_H261; - case SKINNY_CODEC_H263: - return AST_FORMAT_H263; - default: - return 0; - } -} - -static int codec_ast2skinny(int astcodec) -{ - switch (astcodec) { - case AST_FORMAT_ALAW: - return SKINNY_CODEC_ALAW; - case AST_FORMAT_ULAW: - return SKINNY_CODEC_ULAW; - case AST_FORMAT_G723_1: - return SKINNY_CODEC_G723_1; - case AST_FORMAT_G729A: - return SKINNY_CODEC_G729A; - case AST_FORMAT_G726_AAL2: /* XXX Is this right? */ - return SKINNY_CODEC_G726_32; - case AST_FORMAT_H261: - return SKINNY_CODEC_H261; - case AST_FORMAT_H263: - return SKINNY_CODEC_H263; - default: - return 0; - } -} - - -static int skinny_register(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d; - struct sockaddr_in sin; - socklen_t slen; - - ast_mutex_lock(&devicelock); - for (d = devices; d; d = d->next) { - if (!strcasecmp(req->data.reg.name, d->id) - && ast_apply_ha(d->ha, &(s->sin))) { - s->device = d; - d->type = letohl(req->data.reg.type); - if (ast_strlen_zero(d->version_id)) { - ast_copy_string(d->version_id, version_id, sizeof(d->version_id)); - } - d->registered = 1; - d->session = s; - - slen = sizeof(sin); - if (getsockname(s->fd, (struct sockaddr *)&sin, &slen)) { - ast_log(LOG_WARNING, "Cannot get socket name\n"); - sin.sin_addr = __ourip; - } - d->ourip = sin.sin_addr; - break; - } - } - ast_mutex_unlock(&devicelock); - if (!d) { - return 0; - } - return 1; -} - -static int skinny_unregister(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d; - - d = s->device; - - if (d) { - d->session = NULL; - d->registered = 0; - } - - return -1; /* main loop will destroy the session */ -} - -static int transmit_response(struct skinnysession *s, struct skinny_req *req) -{ - int res = 0; - - if (!s) { - ast_log(LOG_WARNING, "Asked to transmit to a non-existant session!\n"); - return -1; - } - - ast_mutex_lock(&s->lock); - - if (skinnydebug) - ast_log(LOG_VERBOSE, "writing packet type %04X (%d bytes) to socket %d\n", letohl(req->e), letohl(req->len)+8, s->fd); - - if (letohl(req->len > SKINNY_MAX_PACKET) || letohl(req->len < 0)) { - ast_log(LOG_WARNING, "transmit_response: the length of the request is out of bounds\n"); - ast_mutex_unlock(&s->lock); - return -1; - } - - memset(s->outbuf,0,sizeof(s->outbuf)); - memcpy(s->outbuf, req, skinny_header_size); - memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len)); - - res = write(s->fd, s->outbuf, letohl(req->len)+8); - - if (res != letohl(req->len)+8) { - ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno)); - if (res == -1) { - if (skinnydebug) - ast_log(LOG_WARNING, "Transmit: Skinny Client was lost, unregistering\n"); - skinny_unregister(NULL, s); - } - - } - - ast_mutex_unlock(&s->lock); - return 1; -} - -static void transmit_speaker_mode(struct skinnysession *s, int mode) -{ - struct skinny_req *req; - - if (!(req = req_alloc(sizeof(struct set_speaker_message), SET_SPEAKER_MESSAGE))) - return; - - req->data.setspeaker.mode = htolel(mode); - transmit_response(s, req); -} -/* -static void transmit_microphone_mode(struct skinnysession *s, int mode) -{ - struct skinny_req *req; - - if (!(req = req_alloc(sizeof(struct set_microphone_message), SET_MICROPHONE_MESSAGE))) - return; - - req->data.setmicrophone.mode = htolel(mode); - transmit_response(s, req); -} -*/ - -static void transmit_callinfo(struct skinnysession *s, const char *fromname, const char *fromnum, const char *toname, const char *tonum, int instance, int callid, int calltype) -{ - struct skinny_req *req; - - if (!(req = req_alloc(sizeof(struct call_info_message), CALL_INFO_MESSAGE))) - return; - - if (skinnydebug) - ast_verbose("Setting Callinfo to %s(%s) from %s(%s) on %s(%d)\n", fromname, fromnum, toname, tonum, s->device->name, instance); - - if (fromname) { - ast_copy_string(req->data.callinfo.callingPartyName, fromname, sizeof(req->data.callinfo.callingPartyName)); - } - if (fromnum) { - ast_copy_string(req->data.callinfo.callingParty, fromnum, sizeof(req->data.callinfo.callingParty)); - } - if (toname) { - ast_copy_string(req->data.callinfo.calledPartyName, toname, sizeof(req->data.callinfo.calledPartyName)); - } - if (tonum) { - ast_copy_string(req->data.callinfo.calledParty, tonum, sizeof(req->data.callinfo.calledParty)); - } - req->data.callinfo.instance = htolel(instance); - req->data.callinfo.reference = htolel(callid); - req->data.callinfo.type = htolel(calltype); - transmit_response(s, req); -} - -static void transmit_connect(struct skinnysession *s, struct skinny_subchannel *sub) -{ - struct skinny_req *req; - struct skinny_line *l = sub->parent; - struct ast_format_list fmt; - - if (!(req = req_alloc(sizeof(struct open_receive_channel_message), OPEN_RECEIVE_CHANNEL_MESSAGE))) - return; - - fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability)); - - req->data.openreceivechannel.conferenceId = htolel(sub->callid); - req->data.openreceivechannel.partyId = htolel(sub->callid); - req->data.openreceivechannel.packets = htolel(fmt.cur_ms); - req->data.openreceivechannel.capability = htolel(codec_ast2skinny(fmt.bits)); - req->data.openreceivechannel.echo = htolel(0); - req->data.openreceivechannel.bitrate = htolel(0); - transmit_response(s, req); -} - -static void transmit_tone(struct skinnysession *s, int tone, int instance, int reference) -{ - struct skinny_req *req; - - if (tone == SKINNY_NOTONE) { - /* This is bad, mmm'kay? */ - return; - } - - if (tone > 0) { - if (!(req = req_alloc(sizeof(struct start_tone_message), START_TONE_MESSAGE))) - return; - req->data.starttone.tone = htolel(tone); - req->data.starttone.instance = htolel(instance); - req->data.starttone.reference = htolel(reference); - } else { - if (!(req = req_alloc(sizeof(struct stop_tone_message), STOP_TONE_MESSAGE))) - return; - req->data.stoptone.instance = htolel(instance); - req->data.stoptone.reference = htolel(reference); - } - - if (tone > 0) { - req->data.starttone.tone = htolel(tone); - } - transmit_response(s, req); -} - -static void transmit_selectsoftkeys(struct skinnysession *s, int instance, int callid, int softkey) -{ - struct skinny_req *req; - - if (!(req = req_alloc(sizeof(struct select_soft_keys_message), SELECT_SOFT_KEYS_MESSAGE))) - return; - - req->data.selectsoftkey.instance = htolel(instance); - req->data.selectsoftkey.reference = htolel(callid); - req->data.selectsoftkey.softKeySetIndex = htolel(softkey); - req->data.selectsoftkey.validKeyMask = htolel(0xFFFFFFFF); - transmit_response(s, req); -} - -static void transmit_lamp_indication(struct skinnysession *s, int stimulus, int instance, int indication) -{ - struct skinny_req *req; - - if (!(req = req_alloc(sizeof(struct set_lamp_message), SET_LAMP_MESSAGE))) - return; - - req->data.setlamp.stimulus = htolel(stimulus); - req->data.setlamp.stimulusInstance = htolel(instance); - req->data.setlamp.deviceStimulus = htolel(indication); - transmit_response(s, req); -} - -static void transmit_ringer_mode(struct skinnysession *s, int mode) -{ - struct skinny_req *req; - - if (skinnydebug) - ast_verbose("Setting ringer mode to '%d'.\n", mode); - - if (!(req = req_alloc(sizeof(struct set_ringer_message), SET_RINGER_MESSAGE))) - return; - - req->data.setringer.ringerMode = htolel(mode); - /* XXX okay, I don't quite know what this is, but here's what happens (on a 7960). - Note: The phone will always show as ringing on the display. - - 1: phone will audibly ring over and over - 2: phone will audibly ring only once - any other value, will NOT cause the phone to audibly ring - */ - req->data.setringer.unknown1 = htolel(1); - /* XXX the value here doesn't seem to change anything. Must be higher than 0. - Perhaps a packet capture can shed some light on this. */ - req->data.setringer.unknown2 = htolel(1); - transmit_response(s, req); -} - -static void transmit_displaymessage(struct skinnysession *s, const char *text, int instance, int reference) -{ - struct skinny_req *req; - - if (text == 0) { - if (!(req = req_alloc(0, CLEAR_DISPLAY_MESSAGE))) - return; - - req->data.clearpromptstatus.lineInstance = instance; - req->data.clearpromptstatus.callReference = reference; - - if (skinnydebug) - ast_verbose("Clearing Display\n"); - } else { - if (!(req = req_alloc(sizeof(struct displaytext_message), DISPLAYTEXT_MESSAGE))) - return; - - ast_copy_string(req->data.displaytext.text, text, sizeof(req->data.displaytext.text)); - if (skinnydebug) - ast_verbose("Displaying message '%s'\n", req->data.displaytext.text); - } - - transmit_response(s, req); -} - -static void transmit_displaynotify(struct skinnysession *s, const char *text, int t) -{ - struct skinny_req *req; - - if (!(req = req_alloc(sizeof(struct display_notify_message), DISPLAY_NOTIFY_MESSAGE))) - return; - - ast_copy_string(req->data.displaynotify.displayMessage, text, sizeof(req->data.displaynotify.displayMessage)); - req->data.displaynotify.displayTimeout = htolel(t); - - if (skinnydebug) - ast_verbose("Displaying notify '%s'\n", text); - - transmit_response(s, req); -} - -static void transmit_displaypromptstatus(struct skinnysession *s, const char *text, int t, int instance, int callid) -{ - struct skinny_req *req; - - if (text == 0) { - if (!(req = req_alloc(sizeof(struct clear_prompt_message), CLEAR_PROMPT_MESSAGE))) - return; - - req->data.clearpromptstatus.lineInstance = htolel(instance); - req->data.clearpromptstatus.callReference = htolel(callid); - - if (skinnydebug) - ast_verbose("Clearing Prompt\n"); - } else { - if (!(req = req_alloc(sizeof(struct display_prompt_status_message), DISPLAY_PROMPT_STATUS_MESSAGE))) - return; - - ast_copy_string(req->data.displaypromptstatus.promptMessage, text, sizeof(req->data.displaypromptstatus.promptMessage)); - req->data.displaypromptstatus.messageTimeout = htolel(t); - req->data.displaypromptstatus.lineInstance = htolel(instance); - req->data.displaypromptstatus.callReference = htolel(callid); - - if (skinnydebug) - ast_verbose("Displaying Prompt Status '%s'\n", text); - } - - transmit_response(s, req); -} - -static void transmit_dialednumber(struct skinnysession *s, const char *text, int instance, int callid) -{ - struct skinny_req *req; - - if (!(req = req_alloc(sizeof(struct dialed_number_message), DIALED_NUMBER_MESSAGE))) - return; - - ast_copy_string(req->data.dialednumber.dialedNumber, text, sizeof(req->data.dialednumber.dialedNumber)); - req->data.dialednumber.lineInstance = htolel(instance); - req->data.dialednumber.callReference = htolel(callid); - - transmit_response(s, req); -} - -static void transmit_callstate(struct skinnysession *s, int instance, int state, unsigned callid) -{ - struct skinny_req *req; - - if (state == SKINNY_ONHOOK) { - if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE))) - return; - - req->data.closereceivechannel.conferenceId = htolel(callid); - req->data.closereceivechannel.partyId = htolel(callid); - transmit_response(s, req); - - if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE))) - return; - - req->data.stopmedia.conferenceId = htolel(callid); - req->data.stopmedia.passThruPartyId = htolel(callid); - transmit_response(s, req); - - transmit_speaker_mode(s, SKINNY_SPEAKEROFF); - - transmit_displaypromptstatus(s, NULL, 0, instance, callid); - } - - if (!(req = req_alloc(sizeof(struct call_state_message), CALL_STATE_MESSAGE))) - return; - - req->data.callstate.callState = htolel(state); - req->data.callstate.lineInstance = htolel(instance); - req->data.callstate.callReference = htolel(callid); - transmit_response(s, req); - - if (state == SKINNY_ONHOOK) { - transmit_selectsoftkeys(s, 0, 0, KEYDEF_ONHOOK); - } - - if (state == SKINNY_OFFHOOK || state == SKINNY_ONHOOK) { - if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE))) - return; - - req->data.activatecallplane.lineInstance = htolel(instance); - transmit_response(s, req); - } -} - -/* -static int has_voicemail(struct skinny_line *l) -{ - return ast_app_has_voicemail(l->mailbox, NULL); -} -*/ - -static void do_housekeeping(struct skinnysession *s) -{ -/* - int new; - int old; - struct skinny_device *d = s->device; - struct skinny_line *l; -*/ - - /* Update time on device */ - handle_time_date_req_message(NULL, s); - -/* - for (l = d->lines; l; l = l->next) { - if (has_voicemail(l)) { - if (skinnydebug) - ast_verbose("Checking for voicemail Skinny %s@%s\n", l->name, d->name); - ast_app_inboxcount(l->mailbox, &new, &old); - if (skinnydebug) - ast_verbose("Skinny %s@%s has voicemail!\n", l->name, d->name); - transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON); - } else { - transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF); - } - } -*/ -} - -/* I do not believe skinny can deal with video. - Anyone know differently? */ -/* Yes, it can. Currently 7985 and Cisco VT Advantage do video. */ -static enum ast_rtp_get_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp **rtp) -{ - struct skinny_subchannel *sub = NULL; - - if (!(sub = c->tech_pvt) || !(sub->vrtp)) - return AST_RTP_GET_FAILED; - - *rtp = sub->vrtp; - - return AST_RTP_TRY_NATIVE; -} - -static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp **rtp) -{ - struct skinny_subchannel *sub = NULL; - - if (!(sub = c->tech_pvt) || !(sub->rtp)) - return AST_RTP_GET_FAILED; - - *rtp = sub->rtp; - - return AST_RTP_TRY_NATIVE; -} - -static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active) -{ - struct skinny_subchannel *sub; - sub = c->tech_pvt; - if (sub) { - /* transmit_modify_with_sdp(sub, rtp); @@FIXME@@ if needed */ - return 0; - } - return -1; -} - -static struct ast_rtp_protocol skinny_rtp = { - .type = "Skinny", - .get_rtp_info = skinny_get_rtp_peer, - .get_vrtp_info = skinny_get_vrtp_peer, - .set_rtp_peer = skinny_set_rtp_peer, -}; - -static int skinny_do_debug(int fd, int argc, char *argv[]) -{ - if (argc != 3) { - return RESULT_SHOWUSAGE; - } - skinnydebug = 1; - ast_cli(fd, "Skinny Debugging Enabled\n"); - return RESULT_SUCCESS; -} - -static int skinny_no_debug(int fd, int argc, char *argv[]) -{ - if (argc != 4) { - return RESULT_SHOWUSAGE; - } - skinnydebug = 0; - ast_cli(fd, "Skinny Debugging Disabled\n"); - return RESULT_SUCCESS; -} - -static char *complete_skinny_reset(const char *line, const char *word, int pos, int state) -{ - struct skinny_device *d; - - char *result = NULL; - int wordlen = strlen(word); - int which = 0; - - if (pos == 2) { - for (d = devices; d && !result; d = d->next) { - if (!strncasecmp(word, d->id, wordlen) && ++which > state) - result = ast_strdup(d->id); - } - } - - return result; -} - -static int skinny_reset_device(int fd, int argc, char *argv[]) -{ - struct skinny_device *d; - struct skinny_req *req; - - if (argc < 3 || argc > 4) { - return RESULT_SHOWUSAGE; - } - ast_mutex_lock(&devicelock); - - for (d = devices; d; d = d->next) { - int fullrestart = 0; - if (!strcasecmp(argv[2], d->id) || !strcasecmp(argv[2], "all")) { - if (!(d->session)) - continue; - - if (!(req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE))) - continue; - - if (argc == 4 && !strcasecmp(argv[3], "restart")) - fullrestart = 1; - - if (fullrestart) - req->data.reset.resetType = 2; - else - req->data.reset.resetType = 1; - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s device %s.\n", (fullrestart) ? "Restarting" : "Resetting", d->id); - transmit_response(d->session, req); - } - } - ast_mutex_unlock(&devicelock); - return RESULT_SUCCESS; -} - -static char *device2str(int type) -{ - char *tmp; - - switch (type) { - case SKINNY_DEVICE_NONE: - return "No Device"; - case SKINNY_DEVICE_30SPPLUS: - return "30SP Plus"; - case SKINNY_DEVICE_12SPPLUS: - return "12SP Plus"; - case SKINNY_DEVICE_12SP: - return "12SP"; - case SKINNY_DEVICE_12: - return "12"; - case SKINNY_DEVICE_30VIP: - return "30VIP"; - case SKINNY_DEVICE_7910: - return "7910"; - case SKINNY_DEVICE_7960: - return "7960"; - case SKINNY_DEVICE_7940: - return "7940"; - case SKINNY_DEVICE_7935: - return "7935"; - case SKINNY_DEVICE_ATA186: - return "ATA186"; - case SKINNY_DEVICE_7941: - return "7941"; - case SKINNY_DEVICE_7971: - return "7971"; - case SKINNY_DEVICE_7914: - return "7914"; - case SKINNY_DEVICE_7985: - return "7985"; - case SKINNY_DEVICE_7911: - return "7911"; - case SKINNY_DEVICE_7961GE: - return "7961GE"; - case SKINNY_DEVICE_7941GE: - return "7941GE"; - case SKINNY_DEVICE_7931: - return "7931"; - case SKINNY_DEVICE_7921: - return "7921"; - case SKINNY_DEVICE_7906: - return "7906"; - case SKINNY_DEVICE_7962: - return "7962"; - case SKINNY_DEVICE_7937: - return "7937"; - case SKINNY_DEVICE_7942: - return "7942"; - case SKINNY_DEVICE_7945: - return "7945"; - case SKINNY_DEVICE_7965: - return "7965"; - case SKINNY_DEVICE_7975: - return "7975"; - case SKINNY_DEVICE_7905: - return "7905"; - case SKINNY_DEVICE_7920: - return "7920"; - case SKINNY_DEVICE_7970: - return "7970"; - case SKINNY_DEVICE_7912: - return "7912"; - case SKINNY_DEVICE_7902: - return "7902"; - case SKINNY_DEVICE_CIPC: - return "IP Communicator"; - case SKINNY_DEVICE_7961: - return "7961"; - case SKINNY_DEVICE_7936: - return "7936"; - case SKINNY_DEVICE_SCCPGATEWAY_AN: - return "SCCPGATEWAY_AN"; - case SKINNY_DEVICE_SCCPGATEWAY_BRI: - return "SCCPGATEWAY_BRI"; - case SKINNY_DEVICE_UNKNOWN: - return "Unknown"; - default: - if (!(tmp = ast_threadstorage_get(&device2str_threadbuf, DEVICE2STR_BUFSIZE))) - return "Unknown"; - snprintf(tmp, DEVICE2STR_BUFSIZE, "UNKNOWN-%d", type); - return tmp; - } -} - -static int skinny_show_devices(int fd, int argc, char *argv[]) -{ - struct skinny_device *d; - struct skinny_line *l; - int numlines = 0; - - if (argc != 3) { - return RESULT_SHOWUSAGE; - } - ast_mutex_lock(&devicelock); - - ast_cli(fd, "Name DeviceId IP Type R NL\n"); - ast_cli(fd, "-------------------- ---------------- --------------- --------------- - --\n"); - for (d = devices; d; d = d->next) { - numlines = 0; - for (l = d->lines; l; l = l->next) { - numlines++; - } - - ast_cli(fd, "%-20s %-16s %-15s %-15s %c %2d\n", - d->name, - d->id, - d->session?ast_inet_ntoa(d->session->sin.sin_addr):"", - device2str(d->type), - d->registered?'Y':'N', - numlines); - } - ast_mutex_unlock(&devicelock); - return RESULT_SUCCESS; -} - -static int skinny_show_lines(int fd, int argc, char *argv[]) -{ - struct skinny_device *d; - struct skinny_line *l; - - if (argc != 3) { - return RESULT_SHOWUSAGE; - } - ast_mutex_lock(&devicelock); - - ast_cli(fd, "Device Name Instance Name Label \n"); - ast_cli(fd, "-------------------- -------- -------------------- --------------------\n"); - for (d = devices; d; d = d->next) { - for (l = d->lines; l; l = l->next) { - ast_cli(fd, "%-20s %8d %-20s %-20s\n", - d->name, - l->instance, - l->name, - l->label); - } - } - - ast_mutex_unlock(&devicelock); - return RESULT_SUCCESS; -} - -static char show_devices_usage[] = -"Usage: skinny show devices\n" -" Lists all devices known to the Skinny subsystem.\n"; - -static char show_lines_usage[] = -"Usage: skinny show lines\n" -" Lists all lines known to the Skinny subsystem.\n"; - -static char debug_usage[] = -"Usage: skinny set debug\n" -" Enables dumping of Skinny packets for debugging purposes\n"; - -static char no_debug_usage[] = -"Usage: skinny set debug off\n" -" Disables dumping of Skinny packets for debugging purposes\n"; - -static char reset_usage[] = -"Usage: skinny reset <DeviceId|all> [restart]\n" -" Causes a Skinny device to reset itself, optionally with a full restart\n"; - -static struct ast_cli_entry cli_skinny[] = { - { { "skinny", "show", "devices", NULL }, - skinny_show_devices, "List defined Skinny devices", - show_devices_usage }, - - { { "skinny", "show", "lines", NULL }, - skinny_show_lines, "List defined Skinny lines per device", - show_lines_usage }, - - { { "skinny", "set", "debug", NULL }, - skinny_do_debug, "Enable Skinny debugging", - debug_usage }, - - { { "skinny", "set", "debug", "off", NULL }, - skinny_no_debug, "Disable Skinny debugging", - no_debug_usage }, - - { { "skinny", "reset", NULL }, - skinny_reset_device, "Reset Skinny device(s)", - reset_usage, complete_skinny_reset }, -}; - -#if 0 -static struct skinny_paging_device *build_paging_device(const char *cat, struct ast_variable *v) -{ - return NULL; -} -#endif - -static struct skinny_device *build_device(const char *cat, struct ast_variable *v) -{ - struct skinny_device *d; - struct skinny_line *l; - struct skinny_speeddial *sd; - struct skinny_addon *a; - int lineInstance = 1; - int speeddialInstance = 1; - int y = 0; - - if (!(d = ast_calloc(1, sizeof(struct skinny_device)))) { - return NULL; - } else { - ast_copy_string(d->name, cat, sizeof(d->name)); - d->lastlineinstance = 1; - d->capability = default_capability; - d->prefs = default_prefs; - d->earlyrtp = 1; - while(v) { - if (!strcasecmp(v->name, "host")) { - if (ast_get_ip(&d->addr, v->value)) { - free(d); - return NULL; - } - } else if (!strcasecmp(v->name, "port")) { - d->addr.sin_port = htons(atoi(v->value)); - } else if (!strcasecmp(v->name, "device")) { - ast_copy_string(d->id, v->value, sizeof(d->id)); - } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) { - d->ha = ast_append_ha(v->name, v->value, d->ha); - } else if (!strcasecmp(v->name, "context")) { - ast_copy_string(context, v->value, sizeof(context)); - } else if (!strcasecmp(v->name, "allow")) { - ast_parse_allow_disallow(&d->prefs, &d->capability, v->value, 1); - } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&d->prefs, &d->capability, v->value, 0); - } else if (!strcasecmp(v->name, "version")) { - ast_copy_string(d->version_id, v->value, sizeof(d->version_id)); - } else if (!strcasecmp(v->name, "earlyrtp")) { - d->earlyrtp = ast_true(v->value); - } else if (!strcasecmp(v->name, "nat")) { - nat = ast_true(v->value); - } else if (!strcasecmp(v->name, "callerid")) { - if (!strcasecmp(v->value, "asreceived")) { - cid_num[0] = '\0'; - cid_name[0] = '\0'; - } else { - ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num)); - } - } else if (!strcasecmp(v->name, "language")) { - ast_copy_string(language, v->value, sizeof(language)); - } else if (!strcasecmp(v->name, "accountcode")) { - ast_copy_string(accountcode, v->value, sizeof(accountcode)); - } else if (!strcasecmp(v->name, "amaflags")) { - y = ast_cdr_amaflags2int(v->value); - if (y < 0) { - ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno); - } else { - amaflags = y; - } - } else if (!strcasecmp(v->name, "mohinterpret") || !strcasecmp(v->name, "musiconhold")) { - ast_copy_string(mohinterpret, v->value, sizeof(mohinterpret)); - } else if (!strcasecmp(v->name, "mohsuggest")) { - ast_copy_string(mohsuggest, v->value, sizeof(mohsuggest)); - } else if (!strcasecmp(v->name, "callgroup")) { - cur_callergroup = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "pickupgroup")) { - cur_pickupgroup = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "immediate")) { - immediate = ast_true(v->value); - } else if (!strcasecmp(v->name, "cancallforward")) { - cancallforward = ast_true(v->value); - } else if (!strcasecmp(v->name, "mailbox")) { - ast_copy_string(mailbox, v->value, sizeof(mailbox)); - } else if (!strcasecmp(v->name, "hasvoicemail")) { - if (ast_true(v->value) && ast_strlen_zero(mailbox)) { - ast_copy_string(mailbox, cat, sizeof(mailbox)); - } - } else if (!strcasecmp(v->name, "callreturn")) { - callreturn = ast_true(v->value); - } else if (!strcasecmp(v->name, "callwaiting")) { - callwaiting = ast_true(v->value); - } else if (!strcasecmp(v->name, "transfer")) { - transfer = ast_true(v->value); - } else if (!strcasecmp(v->name, "threewaycalling")) { - threewaycalling = ast_true(v->value); - } else if (!strcasecmp(v->name, "mwiblink")) { - mwiblink = ast_true(v->value); - } else if (!strcasecmp(v->name, "linelabel")) { - ast_copy_string(linelabel, v->value, sizeof(linelabel)); - } else if (!strcasecmp(v->name, "speeddial")) { - if (!(sd = ast_calloc(1, sizeof(struct skinny_speeddial)))) { - return NULL; - } else { - char *stringp, *exten, *label; - stringp = v->value; - exten = strsep(&stringp, ","); - label = strsep(&stringp, ","); - ast_mutex_init(&sd->lock); - ast_copy_string(sd->exten, exten, sizeof(sd->exten)); - if (label) - ast_copy_string(sd->label, label, sizeof(sd->label)); - else - ast_copy_string(sd->label, exten, sizeof(sd->label)); - sd->instance = speeddialInstance++; - - sd->parent = d; - - sd->next = d->speeddials; - d->speeddials = sd; - } - } else if (!strcasecmp(v->name, "addon")) { - if (!(a = ast_calloc(1, sizeof(struct skinny_addon)))) { - return NULL; - } else { - ast_mutex_init(&a->lock); - ast_copy_string(a->type, v->value, sizeof(a->type)); - - a->next = d->addons; - d->addons = a; - } - } else if (!strcasecmp(v->name, "trunk") || !strcasecmp(v->name, "line")) { - if (!(l = ast_calloc(1, sizeof(struct skinny_line)))) { - return NULL; - } else { - ast_mutex_init(&l->lock); - ast_copy_string(l->name, v->value, sizeof(l->name)); - - /* XXX Should we check for uniqueness?? XXX */ - ast_copy_string(l->context, context, sizeof(l->context)); - ast_copy_string(l->cid_num, cid_num, sizeof(l->cid_num)); - ast_copy_string(l->cid_name, cid_name, sizeof(l->cid_name)); - ast_copy_string(l->label, linelabel, sizeof(l->label)); - ast_copy_string(l->language, language, sizeof(l->language)); - ast_copy_string(l->mohinterpret, mohinterpret, sizeof(l->mohinterpret)); - ast_copy_string(l->mohsuggest, mohsuggest, sizeof(l->mohsuggest)); - ast_copy_string(l->mailbox, mailbox, sizeof(l->mailbox)); - if (!ast_strlen_zero(mailbox)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Setting mailbox '%s' on %s@%s\n", mailbox, d->name, l->name); - } - l->msgstate = -1; - l->capability = d->capability; - l->prefs = d->prefs; - l->parent = d; - if (!strcasecmp(v->name, "trunk")) { - l->type = TYPE_TRUNK; - } else { - l->type = TYPE_LINE; - } - l->immediate = immediate; - l->callgroup = cur_callergroup; - l->pickupgroup = cur_pickupgroup; - l->callreturn = callreturn; - l->cancallforward = cancallforward; - l->callwaiting = callwaiting; - l->transfer = transfer; - l->threewaycalling = threewaycalling; - l->mwiblink = mwiblink; - l->onhooktime = time(NULL); - l->instance = lineInstance++; - /* ASSUME we're onhook at this point */ - l->hookstate = SKINNY_ONHOOK; - l->nat = nat; - - l->next = d->lines; - d->lines = l; - } - } else { - ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno); - } - v = v->next; - } - - if (!d->lines) { - ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n"); - return NULL; - } - if (/*d->addr.sin_addr.s_addr && */!ntohs(d->addr.sin_port)) { - d->addr.sin_port = htons(DEFAULT_SKINNY_PORT); - } -#if 0 - /* I don't think we need this anymore at all, since d->ourip is set in skinny_register now */ - if (d->addr.sin_addr.s_addr) { - /* XXX See note above, in 'host' option. */ - if (ast_ouraddrfor(&d->addr.sin_addr, &d->ourip)) { - d->ourip = __ourip; - } - } else { - d->ourip = __ourip; - } -#endif - } - return d; -} - -static void start_rtp(struct skinny_subchannel *sub) -{ - struct skinny_line *l = sub->parent; - struct skinny_device *d = l->parent; - int hasvideo = 0; - - ast_mutex_lock(&sub->lock); - /* Allocate the RTP */ - sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - if (hasvideo) - sub->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - - if (sub->rtp && sub->owner) { - sub->owner->fds[0] = ast_rtp_fd(sub->rtp); - sub->owner->fds[1] = ast_rtcp_fd(sub->rtp); - } - if (hasvideo && sub->vrtp && sub->owner) { - sub->owner->fds[2] = ast_rtp_fd(sub->vrtp); - sub->owner->fds[3] = ast_rtcp_fd(sub->vrtp); - } - if (sub->rtp) { - ast_rtp_setnat(sub->rtp, l->nat); - } - if (sub->vrtp) { - ast_rtp_setnat(sub->vrtp, l->nat); - } - /* Set Frame packetization */ - if (sub->rtp) - ast_rtp_codec_setpref(sub->rtp, &l->prefs); - - /* Create the RTP connection */ - transmit_connect(d->session, sub); - ast_mutex_unlock(&sub->lock); -} - -static void *skinny_newcall(void *data) -{ - struct ast_channel *c = data; - struct skinny_subchannel *sub = c->tech_pvt; - struct skinny_line *l = sub->parent; - struct skinny_device *d = l->parent; - struct skinnysession *s = d->session; - int res = 0; - - ast_copy_string(l->lastnumberdialed, c->exten, sizeof(l->lastnumberdialed)); - ast_set_callerid(c, - l->hidecallerid ? "" : l->cid_num, - l->hidecallerid ? "" : l->cid_name, - c->cid.cid_ani ? NULL : l->cid_num); - ast_setstate(c, AST_STATE_RING); - if (!sub->rtp) { - start_rtp(sub); - } - res = ast_pbx_run(c); - if (res) { - ast_log(LOG_WARNING, "PBX exited non-zero\n"); - transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid); - } - return NULL; -} - -static void *skinny_ss(void *data) -{ - struct ast_channel *c = data; - struct skinny_subchannel *sub = c->tech_pvt; - struct skinny_line *l = sub->parent; - struct skinny_device *d = l->parent; - struct skinnysession *s = d->session; - int len = 0; - int timeout = firstdigittimeout; - int res = 0; - int getforward=0; - int loop_pause = 100; - - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Starting simple switch on '%s@%s'\n", l->name, d->name); - len = strlen(d->exten); - - while (len < AST_MAX_EXTENSION-1) { - - res = 1; /* Assume we will get a digit */ - while (strlen(d->exten) == len) { - ast_safe_sleep(c, loop_pause); - timeout -= loop_pause; - if (timeout <= 0){ - res = 0; - break; - } - } - - len = strlen(d->exten); - - if (!ast_ignore_pattern(c->context, d->exten)) { - transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); - } - - if (ast_exists_extension(c, c->context, d->exten, 1, l->cid_num)) { - if (!res || !ast_matchmore_extension(c, c->context, d->exten, 1, l->cid_num)) { - if (getforward) { - /* Record this as the forwarding extension */ - ast_copy_string(l->call_forward, d->exten, sizeof(l->call_forward)); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Setting call forward to '%s' on channel %s\n", - l->call_forward, c->name); - transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); - if (res) { - break; - } - ast_safe_sleep(c, 500); - ast_indicate(c, -1); - ast_safe_sleep(c, 1000); - memset(d->exten, 0, sizeof(d->exten)); - transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); - len = 0; - getforward = 0; - } else { - ast_copy_string(c->exten, d->exten, sizeof(c->exten)); - ast_copy_string(l->lastnumberdialed, d->exten, sizeof(l->lastnumberdialed)); - memset (d->exten, 0, sizeof(d->exten)); - skinny_newcall(c); - return NULL; - } - } else { - /* It's a match, but they just typed a digit, and there is an ambiguous match, - so just set the timeout to matchdigittimeout and wait some more */ - timeout = matchdigittimeout; - } - } else if (res == 0) { - ast_log(LOG_DEBUG, "Not enough digits (and no ambiguous match)...\n"); - memset(d->exten, 0, sizeof(d->exten)); - transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid); - if (sub->owner && sub->owner->_state != AST_STATE_UP) { - ast_indicate(c, -1); - ast_hangup(c); - } - return NULL; - } else if (!ast_canmatch_extension(c, c->context, d->exten, 1, c->cid.cid_num) && - ((d->exten[0] != '*') || (!ast_strlen_zero(d->exten) > 2))) { - ast_log(LOG_WARNING, "Can't match [%s] from '%s' in context %s\n", d->exten, c->cid.cid_num ? c->cid.cid_num : "<Unknown Caller>", c->context); - memset(d->exten, 0, sizeof(d->exten)); - transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid); - /* hang out for 3 seconds to let congestion play */ - ast_safe_sleep(c, 3000); - break; - } - if (!timeout) { - timeout = gendigittimeout; - } - if (len && !ast_ignore_pattern(c->context, d->exten)) { - ast_indicate(c, -1); - } - } - if (c) - ast_hangup(c); - - memset(d->exten, 0, sizeof(d->exten)); - return NULL; -} - - - -static int skinny_call(struct ast_channel *ast, char *dest, int timeout) -{ - int res = 0; - int tone = 0; - struct skinny_subchannel *sub = ast->tech_pvt; - struct skinny_line *l = sub->parent; - struct skinny_device *d = l->parent; - struct skinnysession *s = d->session; - - if (!d->registered) { - ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest); - return -1; - } - - if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { - ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast->name); - return -1; - } - - if (skinnydebug) - ast_verbose(VERBOSE_PREFIX_3 "skinny_call(%s)\n", ast->name); - - if (l->dnd) { - ast_queue_control(ast, AST_CONTROL_BUSY); - return -1; - } - - switch (l->hookstate) { - case SKINNY_OFFHOOK: - tone = SKINNY_CALLWAITTONE; - break; - case SKINNY_ONHOOK: - tone = SKINNY_ALERT; - break; - default: - ast_log(LOG_ERROR, "Don't know how to deal with hookstate %d\n", l->hookstate); - break; - } - - transmit_callstate(s, l->instance, SKINNY_RINGIN, sub->callid); - transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGIN); - transmit_displaypromptstatus(s, "Ring-In", 0, l->instance, sub->callid); - transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1); - transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK); - transmit_ringer_mode(s, SKINNY_RING_INSIDE); - - ast_setstate(ast, AST_STATE_RINGING); - ast_queue_control(ast, AST_CONTROL_RINGING); - sub->outgoing = 1; - return res; -} - -static int skinny_hangup(struct ast_channel *ast) -{ - struct skinny_subchannel *sub = ast->tech_pvt; - struct skinny_line *l; - struct skinny_device *d; - struct skinnysession *s; - - if (!sub) { - ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n"); - return 0; - } - l = sub->parent; - d = l->parent; - s = d->session; - if (skinnydebug) - ast_verbose("skinny_hangup(%s) on %s@%s\n", ast->name, l->name, d->name); - - if (d->registered) { - if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_OFFHOOK)) { - l->hookstate = SKINNY_ONHOOK; - transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid); - transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF); - transmit_speaker_mode(s, SKINNY_SPEAKEROFF); - } else if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_ONHOOK)) { - transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); - transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid); - transmit_ringer_mode(s, SKINNY_RING_OFF); - transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF); - do_housekeeping(s); - } - } - ast_mutex_lock(&sub->lock); - sub->owner = NULL; - ast->tech_pvt = NULL; - sub->alreadygone = 0; - sub->outgoing = 0; - if (sub->rtp) { - ast_rtp_destroy(sub->rtp); - sub->rtp = NULL; - } - ast_mutex_unlock(&sub->lock); - return 0; -} - -static int skinny_answer(struct ast_channel *ast) -{ - int res = 0; - struct skinny_subchannel *sub = ast->tech_pvt; - struct skinny_line *l = sub->parent; - struct skinny_device *d = l->parent; - struct skinnysession *s = d->session; - char exten[AST_MAX_EXTENSION] = ""; - - ast_copy_string(exten, S_OR(ast->macroexten, ast->exten), sizeof(exten)); - - sub->cxmode = SKINNY_CX_SENDRECV; - if (!sub->rtp) { - start_rtp(sub); - } - if (skinnydebug) - ast_verbose("skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, d->name, sub->callid); - if (ast->_state != AST_STATE_UP) { - ast_setstate(ast, AST_STATE_UP); - } - - transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); - /* order matters here... - for some reason, transmit_callinfo must be before transmit_callstate, - or you won't get keypad messages in some situations. */ - transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2); - transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid); - transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED); - transmit_dialednumber(s, exten, l->instance, sub->callid); - transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid); - return res; -} - -/* Retrieve audio/etc from channel. Assumes sub->lock is already held. */ -static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub) -{ - struct ast_channel *ast = sub->owner; - struct ast_frame *f; - - if (!sub->rtp) { - /* We have no RTP allocated for this channel */ - return &ast_null_frame; - } - - switch(ast->fdno) { - case 0: - f = ast_rtp_read(sub->rtp); /* RTP Audio */ - break; - case 1: - f = ast_rtcp_read(sub->rtp); /* RTCP Control Channel */ - break; - case 2: - f = ast_rtp_read(sub->vrtp); /* RTP Video */ - break; - case 3: - f = ast_rtcp_read(sub->vrtp); /* RTCP Control Channel for video */ - break; -#if 0 - case 5: - /* Not yet supported */ - f = ast_udptl_read(sub->udptl); /* UDPTL for T.38 */ - break; -#endif - default: - f = &ast_null_frame; - } - - if (ast) { - /* We already hold the channel lock */ - if (f->frametype == AST_FRAME_VOICE) { - if (f->subclass != ast->nativeformats) { - ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass); - ast->nativeformats = f->subclass; - ast_set_read_format(ast, ast->readformat); - ast_set_write_format(ast, ast->writeformat); - } - } - } - return f; -} - -static struct ast_frame *skinny_read(struct ast_channel *ast) -{ - struct ast_frame *fr; - struct skinny_subchannel *sub = ast->tech_pvt; - ast_mutex_lock(&sub->lock); - fr = skinny_rtp_read(sub); - ast_mutex_unlock(&sub->lock); - return fr; -} - -static int skinny_write(struct ast_channel *ast, struct ast_frame *frame) -{ - struct skinny_subchannel *sub = ast->tech_pvt; - int res = 0; - if (frame->frametype != AST_FRAME_VOICE) { - if (frame->frametype == AST_FRAME_IMAGE) { - return 0; - } else { - ast_log(LOG_WARNING, "Can't send %d type frames with skinny_write\n", frame->frametype); - return 0; - } - } else { - if (!(frame->subclass & ast->nativeformats)) { - ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n", - frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat); - return -1; - } - } - if (sub) { - ast_mutex_lock(&sub->lock); - if (sub->rtp) { - res = ast_rtp_write(sub->rtp, frame); - } - ast_mutex_unlock(&sub->lock); - } - return res; -} - -static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct skinny_subchannel *sub = newchan->tech_pvt; - ast_log(LOG_NOTICE, "skinny_fixup(%s, %s)\n", oldchan->name, newchan->name); - if (sub->owner != oldchan) { - ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner); - return -1; - } - sub->owner = newchan; - return 0; -} - -static int skinny_senddigit_begin(struct ast_channel *ast, char digit) -{ - return -1; /* Start inband indications */ -} - -static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ -#if 0 - struct skinny_subchannel *sub = ast->tech_pvt; - struct skinny_line *l = sub->parent; - struct skinny_device *d = l->parent; - int tmp; - /* not right */ - sprintf(tmp, "%d", digit); - transmit_tone(d->session, digit, l->instance, sub->callid); -#endif - return -1; /* Stop inband indications */ -} - -static char *control2str(int ind) { - char *tmp; - - switch (ind) { - case AST_CONTROL_HANGUP: - return "Other end has hungup"; - case AST_CONTROL_RING: - return "Local ring"; - case AST_CONTROL_RINGING: - return "Remote end is ringing"; - case AST_CONTROL_ANSWER: - return "Remote end has answered"; - case AST_CONTROL_BUSY: - return "Remote end is busy"; - case AST_CONTROL_TAKEOFFHOOK: - return "Make it go off hook"; - case AST_CONTROL_OFFHOOK: - return "Line is off hook"; - case AST_CONTROL_CONGESTION: - return "Congestion (circuits busy)"; - case AST_CONTROL_FLASH: - return "Flash hook"; - case AST_CONTROL_WINK: - return "Wink"; - case AST_CONTROL_OPTION: - return "Set a low-level option"; - case AST_CONTROL_RADIO_KEY: - return "Key Radio"; - case AST_CONTROL_RADIO_UNKEY: - return "Un-Key Radio"; - case AST_CONTROL_PROGRESS: - return "Remote end is making Progress"; - case AST_CONTROL_PROCEEDING: - return "Remote end is proceeding"; - case AST_CONTROL_HOLD: - return "Hold"; - case AST_CONTROL_UNHOLD: - return "Unhold"; - case -1: - return "Stop tone"; - default: - if (!(tmp = ast_threadstorage_get(&control2str_threadbuf, CONTROL2STR_BUFSIZE))) - return "Unknown"; - snprintf(tmp, CONTROL2STR_BUFSIZE, "UNKNOWN-%d", ind); - return tmp; - } -} - - -static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen) -{ - struct skinny_subchannel *sub = ast->tech_pvt; - struct skinny_line *l = sub->parent; - struct skinny_device *d = l->parent; - struct skinnysession *s = d->session; - char exten[AST_MAX_EXTENSION] = ""; - - if (!s) { - ast_log(LOG_NOTICE, "Asked to indicate '%s' condition on channel %s, but session does not exist.\n", control2str(ind), ast->name); - return -1; - } - - ast_copy_string(exten, S_OR(ast->macroexten, ast->exten), sizeof(exten)); - - if (skinnydebug) - ast_verbose(VERBOSE_PREFIX_3 "Asked to indicate '%s' condition on channel %s\n", control2str(ind), ast->name); - switch(ind) { - case AST_CONTROL_RINGING: - if (ast->_state != AST_STATE_UP) { - if (!sub->progress) { - if (!d->earlyrtp) { - transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid); - } - transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid); - transmit_dialednumber(s, exten, l->instance, sub->callid); - transmit_displaypromptstatus(s, "Ring Out", 0, l->instance, sub->callid); - transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */ - sub->ringing = 1; - if (!d->earlyrtp) { - break; - } - } - } - return -1; /* Tell asterisk to provide inband signalling */ - case AST_CONTROL_BUSY: - if (ast->_state != AST_STATE_UP) { - if (!d->earlyrtp) { - transmit_tone(s, SKINNY_BUSYTONE, l->instance, sub->callid); - } - transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid); - sub->alreadygone = 1; - ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV); - if (!d->earlyrtp) { - break; - } - } - return -1; /* Tell asterisk to provide inband signalling */ - case AST_CONTROL_CONGESTION: - if (ast->_state != AST_STATE_UP) { - if (!d->earlyrtp) { - transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid); - } - transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid); - sub->alreadygone = 1; - ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV); - if (!d->earlyrtp) { - break; - } - } - return -1; /* Tell asterisk to provide inband signalling */ - case AST_CONTROL_PROGRESS: - if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) { - if (!d->earlyrtp) { - transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid); - } - transmit_callstate(s, l->instance, SKINNY_PROGRESS, sub->callid); - transmit_displaypromptstatus(s, "Call Progress", 0, l->instance, sub->callid); - transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */ - sub->progress = 1; - if (!d->earlyrtp) { - break; - } - } - return -1; /* Tell asterisk to provide inband signalling */ - case -1: /* STOP_TONE */ - transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); - break; - case AST_CONTROL_HOLD: - ast_moh_start(ast, data, l->mohinterpret); - break; - case AST_CONTROL_UNHOLD: - ast_moh_stop(ast); - break; - case AST_CONTROL_PROCEEDING: - break; - case AST_CONTROL_SRCUPDATE: - ast_rtp_new_source(sub->rtp); - break; - default: - ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind); - return -1; /* Tell asterisk to provide inband signalling */ - } - return 0; -} - -static struct ast_channel *skinny_new(struct skinny_line *l, int state) -{ - struct ast_channel *tmp; - struct skinny_subchannel *sub; - struct skinny_device *d = l->parent; - int fmt; - - tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums); - if (!tmp) { - ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); - return NULL; - } else { - sub = ast_calloc(1, sizeof(struct skinny_subchannel)); - if (!sub) { - ast_log(LOG_WARNING, "Unable to allocate Skinny subchannel\n"); - return NULL; - } else { - ast_mutex_init(&sub->lock); - - sub->owner = tmp; - sub->callid = callnums++; - d->lastlineinstance = l->instance; - d->lastcallreference = sub->callid; - sub->cxmode = SKINNY_CX_INACTIVE; - sub->nat = l->nat; - sub->parent = l; - sub->onhold = 0; - - sub->next = l->sub; - l->sub = sub; - } - tmp->tech = &skinny_tech; - tmp->tech_pvt = sub; - tmp->nativeformats = l->capability; - if (!tmp->nativeformats) - tmp->nativeformats = default_capability; - fmt = ast_best_codec(tmp->nativeformats); - if (skinnydebug) - ast_verbose("skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt); - if (sub->rtp) { - tmp->fds[0] = ast_rtp_fd(sub->rtp); - } - if (state == AST_STATE_RING) { - tmp->rings = 1; - } - tmp->writeformat = fmt; - tmp->rawwriteformat = fmt; - tmp->readformat = fmt; - tmp->rawreadformat = fmt; - if (!ast_strlen_zero(l->language)) - ast_string_field_set(tmp, language, l->language); - if (!ast_strlen_zero(l->accountcode)) - ast_string_field_set(tmp, accountcode, l->accountcode); - if (l->amaflags) - tmp->amaflags = l->amaflags; - - ast_module_ref(ast_module_info->self); - tmp->callgroup = l->callgroup; - tmp->pickupgroup = l->pickupgroup; - ast_string_field_set(tmp, call_forward, l->call_forward); - ast_copy_string(tmp->context, l->context, sizeof(tmp->context)); - ast_copy_string(tmp->exten, l->exten, sizeof(tmp->exten)); - - /* Don't use ast_set_callerid() here because it will - * generate a needless NewCallerID event */ - tmp->cid.cid_ani = ast_strdup(l->cid_num); - - tmp->priority = 1; - tmp->adsicpe = AST_ADSI_UNAVAILABLE; - - if (sub->rtp) - ast_jb_configure(tmp, &global_jbconf); - - if (state != AST_STATE_DOWN) { - if (ast_pbx_start(tmp)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); - ast_hangup(tmp); - tmp = NULL; - } - } - } - return tmp; -} - -static int skinny_hold(struct skinny_subchannel *sub) -{ - struct skinny_line *l = sub->parent; - struct skinny_device *d = l->parent; - struct skinnysession *s = d->session; - struct skinny_req *req; - - /* Don't try to hold a channel that doesn't exist */ - if (!sub || !sub->owner) - return 0; - - /* Channel needs to be put on hold */ - if (skinnydebug) - ast_verbose("Putting on Hold(%d)\n", l->instance); - - ast_queue_control_data(sub->owner, AST_CONTROL_HOLD, - S_OR(l->mohsuggest, NULL), - !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0); - - if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE))) - return 0; - - req->data.activatecallplane.lineInstance = htolel(l->instance); - transmit_response(s, req); - - if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE))) - return 0; - - req->data.closereceivechannel.conferenceId = htolel(sub->callid); - req->data.closereceivechannel.partyId = htolel(sub->callid); - transmit_response(s, req); - - if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE))) - return 0; - - req->data.stopmedia.conferenceId = htolel(sub->callid); - req->data.stopmedia.passThruPartyId = htolel(sub->callid); - transmit_response(s, req); - - transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK); - sub->onhold = 1; - return 1; -} - -static int skinny_unhold(struct skinny_subchannel *sub) -{ - struct skinny_line *l = sub->parent; - struct skinny_device *d = l->parent; - struct skinnysession *s = d->session; - struct skinny_req *req; - - /* Don't try to unhold a channel that doesn't exist */ - if (!sub || !sub->owner) - return 0; - - /* Channel is on hold, so we will unhold */ - if (skinnydebug) - ast_verbose("Taking off Hold(%d)\n", l->instance); - - ast_queue_control(sub->owner, AST_CONTROL_UNHOLD); - - if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE))) - return 0; - - req->data.activatecallplane.lineInstance = htolel(l->instance); - transmit_response(s, req); - - transmit_connect(s, sub); - transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); - sub->onhold = 0; - return 1; -} - -static int handle_keep_alive_message(struct skinny_req *req, struct skinnysession *s) -{ - if (!(req = req_alloc(0, KEEP_ALIVE_ACK_MESSAGE))) - return -1; - - transmit_response(s, req); - do_housekeeping(s); - return 1; -} - -static int handle_register_message(struct skinny_req *req, struct skinnysession *s) -{ - char name[16]; - int res; - - memcpy(&name, req->data.reg.name, sizeof(name)); - - res = skinny_register(req, s); - if (!res) { - ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", name); - if (!(req = req_alloc(sizeof(struct register_rej_message), REGISTER_REJ_MESSAGE))) - return -1; - - snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name); - transmit_response(s, req); - return 0; - } - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Device '%s' successfully registered\n", name); - - if (!(req = req_alloc(sizeof(struct register_ack_message), REGISTER_ACK_MESSAGE))) - return -1; - - req->data.regack.res[0] = '0'; - req->data.regack.res[1] = '\0'; - req->data.regack.keepAlive = htolel(keep_alive); - memcpy(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate)); - req->data.regack.res2[0] = '0'; - req->data.regack.res2[1] = '\0'; - req->data.regack.secondaryKeepAlive = htolel(keep_alive); - transmit_response(s, req); - if (skinnydebug) - ast_verbose("Requesting capabilities\n"); - - if (!(req = req_alloc(0, CAPABILITIES_REQ_MESSAGE))) - return -1; - - transmit_response(s, req); - - return res; -} - -static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *s) -{ - /* no response necessary */ - return 1; -} - -static int handle_keypad_button_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_subchannel *sub = NULL; - struct skinny_line *l; - struct skinny_device *d = s->device; - struct ast_frame f = { 0, }; - char dgt; - int digit; - int lineInstance; - int callReference; - - digit = letohl(req->data.keypad.button); - lineInstance = letohl(req->data.keypad.lineInstance); - callReference = letohl(req->data.keypad.callReference); - - if (digit == 14) { - dgt = '*'; - } else if (digit == 15) { - dgt = '#'; - } else if (digit >= 0 && digit <= 9) { - dgt = '0' + digit; - } else { - /* digit=10-13 (A,B,C,D ?), or - * digit is bad value - * - * probably should not end up here, but set - * value for backward compatibility, and log - * a warning. - */ - dgt = '0' + digit; - ast_log(LOG_WARNING, "Unsupported digit %d\n", digit); - } - - f.subclass = dgt; - - f.src = "skinny"; - - if (lineInstance && callReference) - sub = find_subchannel_by_instance_reference(d, lineInstance, callReference); - else - sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference); - - if (!sub) - return 0; - - l = sub->parent; - if (sub->owner) { - if (sub->owner->_state == 0) { - f.frametype = AST_FRAME_DTMF_BEGIN; - ast_queue_frame(sub->owner, &f); - } - /* XXX MUST queue this frame to all lines in threeway call if threeway call is active */ - f.frametype = AST_FRAME_DTMF_END; - ast_queue_frame(sub->owner, &f); - /* XXX This seriously needs to be fixed */ - if (sub->next && sub->next->owner) { - if (sub->owner->_state == 0) { - f.frametype = AST_FRAME_DTMF_BEGIN; - ast_queue_frame(sub->next->owner, &f); - } - f.frametype = AST_FRAME_DTMF_END; - ast_queue_frame(sub->next->owner, &f); - } - } else { - if (skinnydebug) - ast_verbose("No owner: %s\n", l->name); - } - return 1; -} - -static int handle_stimulus_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - struct skinny_line *l; - struct skinny_subchannel *sub; - /*struct skinny_speeddial *sd;*/ - struct ast_channel *c; - pthread_t t; - int event; - int instance; - int callreference; - /*int res = 0;*/ - - event = letohl(req->data.stimulus.stimulus); - instance = letohl(req->data.stimulus.stimulusInstance); - callreference = letohl(req->data.stimulus.callreference); - if (skinnydebug) - ast_verbose("callreference in handle_stimulus_message is '%d'\n", callreference); - - /* Note that this call should be using the passed in instance and callreference */ - sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference); - - if (!sub) { - l = find_line_by_instance(d, d->lastlineinstance); - if (!l) { - return 0; - } - } else { - l = sub->parent; - } - - switch(event) { - case STIMULUS_REDIAL: - if (skinnydebug) - ast_verbose("Received Stimulus: Redial(%d/%d)\n", instance, callreference); - -#if 0 - if (ast_strlen_zero(l->lastnumberdialed)) { - ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n"); - l->hookstate = SKINNY_ONHOOK; - transmit_speaker_mode(s, SKINNY_SPEAKEROFF); - transmit_callstate(s, l->instance, SKINNY_ONHOOK, instance); - break; - } - - c = skinny_new(l, AST_STATE_DOWN); - if(!c) { - ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name); - } else { - sub = c->tech_pvt; - transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - if (skinnydebug) - ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); - transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT); - - if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) { - transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); - } - ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten)); - if (ast_pthread_create(&t, NULL, skinny_newcall, c)) { - ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno)); - ast_hangup(c); - } - } -#endif - break; - case STIMULUS_SPEEDDIAL: - if (skinnydebug) - ast_verbose("Received Stimulus: SpeedDial(%d/%d)\n", instance, callreference); - -#if 0 - if (!(sd = find_speeddial_by_instance(d, instance))) { - return 0; - } - - if (ast_strlen_zero(l->lastnumberdialed)) { - ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n"); - l->hookstate = SKINNY_ONHOOK; - transmit_speaker_mode(s, SKINNY_SPEAKEROFF); - transmit_callstate(s, l->instance, SKINNY_ONHOOK, instance); - break; - } - - c = skinny_new(l, AST_STATE_DOWN); - if(c) { - sub = c->tech_pvt; - l = sub->parent; - transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - if (skinnydebug) - ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); - transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT); - - if (!ast_ignore_pattern(c->context, sd->exten)) { - transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); - } - if (ast_exists_extension(c, c->context, sd->exten, 1, l->cid_num)) { - if (!ast_matchmore_extension(c, c->context, sd->exten, 1, l->cid_num)) { - ast_copy_string(c->exten, sd->exten, sizeof(c->exten)); - ast_copy_string(l->lastnumberdialed, sd->exten, sizeof(l->lastnumberdialed)); - skinny_newcall(c); - break; - } - } - } else { - ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name); - } -#endif - break; - case STIMULUS_HOLD: - if (skinnydebug) - ast_verbose("Received Stimulus: Hold(%d/%d)\n", instance, callreference); - - if (!sub) - break; - - if (sub->onhold) { - skinny_unhold(sub); - } else { - skinny_hold(sub); - } - break; - case STIMULUS_TRANSFER: - if (skinnydebug) - ast_verbose("Received Stimulus: Transfer(%d/%d)\n", instance, callreference); - /* XXX figure out how to transfer */ - break; - case STIMULUS_CONFERENCE: - if (skinnydebug) - ast_verbose("Received Stimulus: Conference(%d/%d)\n", instance, callreference); - /* XXX determine the best way to pull off a conference. Meetme? */ - break; - case STIMULUS_VOICEMAIL: - if (skinnydebug) - ast_verbose("Received Stimulus: Voicemail(%d/%d)\n", instance, callreference); - /* XXX Find and dial voicemail extension */ - break; - case STIMULUS_CALLPARK: - if (skinnydebug) - ast_verbose("Received Stimulus: Park Call(%d/%d)\n", instance, callreference); - /* XXX Park the call */ - break; - case STIMULUS_FORWARDALL: - if (skinnydebug) - ast_verbose("Received Stimulus: Forward All(%d/%d)\n", instance, callreference); - /* Why is DND under FORWARDALL? */ - /* Because it's the same thing. */ - - /* Do not disturb */ - if (l->dnd != 0){ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Disabling DND on %s@%s\n", l->name, d->name); - l->dnd = 0; - transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON); - transmit_displaynotify(s, "DnD disabled", 10); - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Enabling DND on %s@%s\n", l->name, d->name); - l->dnd = 1; - transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_OFF); - transmit_displaynotify(s, "DnD enabled", 10); - } - break; - case STIMULUS_FORWARDBUSY: - if (skinnydebug) - ast_verbose("Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference); - break; - case STIMULUS_FORWARDNOANSWER: - if (skinnydebug) - ast_verbose("Received Stimulus: Forward No Answer (%d/%d)\n", instance, callreference); - break; - case STIMULUS_DISPLAY: - /* Not sure what this is */ - if (skinnydebug) - ast_verbose("Received Stimulus: Display(%d/%d)\n", instance, callreference); - break; - case STIMULUS_LINE: - if (skinnydebug) - ast_verbose("Received Stimulus: Line(%d/%d)\n", instance, callreference); - - l = find_line_by_instance(s->device, instance); - - if (!l) { - return 0; - } - - /* turn the speaker on */ - transmit_speaker_mode(s, SKINNY_SPEAKERON); - transmit_ringer_mode(s, SKINNY_RING_OFF); - transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); - - l->hookstate = SKINNY_OFFHOOK; - - if (sub && sub->outgoing) { - /* We're answering a ringing call */ - ast_queue_control(sub->owner, AST_CONTROL_ANSWER); - transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); - transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid); - transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid); - transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED); - start_rtp(sub); - ast_setstate(sub->owner, AST_STATE_UP); - } else { - if (sub && sub->owner) { - ast_log(LOG_DEBUG, "Current subchannel [%s] already has owner\n", sub->owner->name); - } else { - c = skinny_new(l, AST_STATE_DOWN); - if(c) { - sub = c->tech_pvt; - transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - if (skinnydebug) - ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); - transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK); - - /* start the switch thread */ - if (ast_pthread_create(&t, NULL, skinny_ss, c)) { - ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno)); - ast_hangup(c); - } - } else { - ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name); - } - } - } - break; - default: - if (skinnydebug) - ast_verbose("RECEIVED UNKNOWN STIMULUS: %d(%d/%d)\n", event, instance, callreference); - break; - } - return 1; -} - -static int handle_offhook_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - struct skinny_line *l; - struct skinny_subchannel *sub; - struct ast_channel *c; - pthread_t t; - int unknown1; - int unknown2; - - unknown1 = letohl(req->data.offhook.unknown1); - unknown2 = letohl(req->data.offhook.unknown2); - - sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference); - - if (!sub) { - l = find_line_by_instance(d, d->lastlineinstance); - if (!l) { - return 0; - } - } else { - l = sub->parent; - } - - transmit_ringer_mode(s, SKINNY_RING_OFF); - l->hookstate = SKINNY_OFFHOOK; - - if (sub && sub->onhold) { - return 1; - } - - transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); - - if (sub && sub->outgoing) { - /* We're answering a ringing call */ - ast_queue_control(sub->owner, AST_CONTROL_ANSWER); - transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); - transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid); - transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED); - start_rtp(sub); - ast_setstate(sub->owner, AST_STATE_UP); - } else { - if (sub && sub->owner) { - ast_log(LOG_DEBUG, "Current sub [%s] already has owner\n", sub->owner->name); - } else { - c = skinny_new(l, AST_STATE_DOWN); - if(c) { - sub = c->tech_pvt; - transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - if (skinnydebug) - ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); - transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK); - - /* start the switch thread */ - if (ast_pthread_create(&t, NULL, skinny_ss, c)) { - ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno)); - ast_hangup(c); - } - } else { - ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name); - } - } - } - return 1; -} - -static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - struct skinny_line *l; - struct skinny_subchannel *sub; - int unknown1; - int unknown2; - - unknown1 = letohl(req->data.onhook.unknown1); - unknown2 = letohl(req->data.onhook.unknown2); - - sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference); - - if (!sub) { - return 0; - } - l = sub->parent; - - if (l->hookstate == SKINNY_ONHOOK) { - /* Something else already put us back on hook */ - return 0; - } - l->hookstate = SKINNY_ONHOOK; - - if (sub->onhold) { - return 0; - } - - sub->cxmode = SKINNY_CX_RECVONLY; - transmit_callstate(s, l->instance, l->hookstate, sub->callid); - if (skinnydebug) - ast_verbose("Skinny %s@%s went on hook\n", l->name, d->name); - if (l->transfer && (sub->owner && sub->next && sub->next->owner) && ((!sub->outgoing) || (sub->next && !sub->next->outgoing))) { - /* We're allowed to transfer, we have two active calls and - we made at least one of the calls. Let's try and transfer */ - -#if 0 - if ((res = attempt_transfer(p)) < 0) { - if (sub->next && sub->next->owner) { - sub->next->alreadygone = 1; - ast_queue_hangup(sub->next->owner,1); - } - } else if (res) { - ast_log(LOG_WARNING, "Transfer attempt failed\n"); - return 0; - } -#endif - } else { - /* Hangup the current call */ - /* If there is another active call, skinny_hangup will ring the phone with the other call */ - if (sub->owner) { - sub->alreadygone = 1; - ast_queue_hangup(sub->owner); - } else { - ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n", - l->name, d->name, sub->callid); - } - } - if ((l->hookstate == SKINNY_ONHOOK) && (sub->next && !sub->next->rtp)) { - do_housekeeping(s); - } - return 1; -} - -static int handle_capabilities_res_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - struct skinny_line *l; - uint32_t count = 0; - int codecs = 0; - int i; - - count = letohl(req->data.caps.count); - if (count > SKINNY_MAX_CAPABILITIES) { - count = SKINNY_MAX_CAPABILITIES; - ast_log(LOG_WARNING, "Received more capabilities than we can handle (%d). Ignoring the rest.\n", SKINNY_MAX_CAPABILITIES); - } - - for (i = 0; i < count; i++) { - int acodec = 0; - int scodec = 0; - scodec = letohl(req->data.caps.caps[i].codec); - acodec = codec_skinny2ast(scodec); - if (skinnydebug) - ast_verbose("Adding codec capability '%d (%d)'\n", acodec, scodec); - codecs |= acodec; - } - - d->capability &= codecs; - ast_verbose("Device capability set to '%d'\n", d->capability); - for (l = d->lines; l; l = l->next) { - ast_mutex_lock(&l->lock); - l->capability = d->capability; - ast_mutex_unlock(&l->lock); - } - - return 1; -} - -static int handle_speed_dial_stat_req_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - struct skinny_speeddial *sd; - int instance; - - instance = letohl(req->data.speeddialreq.speedDialNumber); - - sd = find_speeddial_by_instance(d, instance); - - if (!sd) { - return 0; - } - - if (!(req = req_alloc(sizeof(struct speed_dial_stat_res_message), SPEED_DIAL_STAT_RES_MESSAGE))) - return -1; - - req->data.speeddialreq.speedDialNumber = htolel(instance); - ast_copy_string(req->data.speeddial.speedDialDirNumber, sd->exten, sizeof(req->data.speeddial.speedDialDirNumber)); - ast_copy_string(req->data.speeddial.speedDialDisplayName, sd->label, sizeof(req->data.speeddial.speedDialDisplayName)); - - transmit_response(s, req); - return 1; -} - -static int handle_line_state_req_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - struct skinny_line *l; - int instance; - - instance = letohl(req->data.line.lineNumber); - - ast_mutex_lock(&devicelock); - - l = find_line_by_instance(d, instance); - - if (!l) { - return 0; - } - - ast_mutex_unlock(&devicelock); - - if (!(req = req_alloc(sizeof(struct line_stat_res_message), LINE_STAT_RES_MESSAGE))) - return -1; - - req->data.linestat.lineNumber = letohl(instance); - memcpy(req->data.linestat.lineDirNumber, l->name, - sizeof(req->data.linestat.lineDirNumber)); - memcpy(req->data.linestat.lineDisplayName, l->label, - sizeof(req->data.linestat.lineDisplayName)); - transmit_response(s,req); - return 1; -} - -static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s) -{ - time_t timer; - struct tm *cmtime; - - if (!(req = req_alloc(sizeof(struct definetimedate_message), DEFINETIMEDATE_MESSAGE))) - return -1; - - timer = time(NULL); - cmtime = localtime(&timer); - req->data.definetimedate.year = htolel(cmtime->tm_year+1900); - req->data.definetimedate.month = htolel(cmtime->tm_mon+1); - req->data.definetimedate.dayofweek = htolel(cmtime->tm_wday); - req->data.definetimedate.day = htolel(cmtime->tm_mday); - req->data.definetimedate.hour = htolel(cmtime->tm_hour); - req->data.definetimedate.minute = htolel(cmtime->tm_min); - req->data.definetimedate.seconds = htolel(cmtime->tm_sec); - req->data.definetimedate.milliseconds = htolel(0); - req->data.definetimedate.timestamp = htolel(timer); - transmit_response(s, req); - return 1; -} - -static int handle_button_template_req_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - struct skinny_line *l; - int i; - - struct skinny_speeddial *sd; - struct button_definition_template btn[42]; - int lineInstance = 1; - int speeddialInstance = 1; - int buttonCount = 0; - - if (!(req = req_alloc(sizeof(struct button_template_res_message), BUTTON_TEMPLATE_RES_MESSAGE))) - return -1; - - memset(&btn, 0, sizeof(btn)); - - get_button_template(s, btn); - - for (i=0; i<42; i++) { - int btnSet = 0; - switch (btn[i].buttonDefinition) { - case BT_CUST_LINESPEEDDIAL: - /* assume failure */ - req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE; - req->data.buttontemplate.definition[i].instanceNumber = htolel(0); - - for (l = d->lines; l; l = l->next) { - if (l->instance == lineInstance) { - ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); - req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE; - req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance); - lineInstance++; - buttonCount++; - btnSet = 1; - break; - } - } - - if (!btnSet) { - for (sd = d->speeddials; sd; sd = sd->next) { - if (sd->instance == speeddialInstance) { - ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance); - req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL; - req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance); - speeddialInstance++; - buttonCount++; - btnSet = 1; - break; - } - } - } - break; - case BT_LINE: - req->data.buttontemplate.definition[i].buttonDefinition = htolel(BT_NONE); - req->data.buttontemplate.definition[i].instanceNumber = htolel(0); - - for (l = d->lines; l; l = l->next) { - if (l->instance == lineInstance) { - ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance); - req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE; - req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance); - lineInstance++; - buttonCount++; - btnSet = 1; - break; - } - } - break; - case BT_SPEEDDIAL: - req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE; - req->data.buttontemplate.definition[i].instanceNumber = 0; - - for (sd = d->speeddials; sd; sd = sd->next) { - if (sd->instance == speeddialInstance) { - ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance); - req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL; - req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance); - speeddialInstance++; - buttonCount++; - btnSet = 1; - break; - } - } - break; - case BT_CUST_HINT: - break; - case BT_NONE: - break; - default: - ast_verbose("Adding button: %d, %d\n", btn[i].buttonDefinition, 0); - req->data.buttontemplate.definition[i].buttonDefinition = htolel(btn[i].buttonDefinition); - req->data.buttontemplate.definition[i].instanceNumber = htolel(0); - buttonCount++; - btnSet = 1; - break; - } - } - - req->data.buttontemplate.buttonOffset = htolel(0); - req->data.buttontemplate.buttonCount = htolel(buttonCount); - req->data.buttontemplate.totalButtonCount = htolel(buttonCount); - - if (skinnydebug) - ast_verbose("Sending %d template to %s\n", - d->type, - d->name); - transmit_response(s, req); - return 1; -} - -static int handle_version_req_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - if (!(req = req_alloc(sizeof(struct version_res_message), VERSION_RES_MESSAGE))) - return -1; - - ast_copy_string(req->data.version.version, d->version_id, sizeof(req->data.version.version)); - transmit_response(s, req); - return 1; -} - -static int handle_server_request_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - if (!(req = req_alloc(sizeof(struct server_res_message), SERVER_RES_MESSAGE))) - return -1; - - memcpy(req->data.serverres.server[0].serverName, ourhost, - sizeof(req->data.serverres.server[0].serverName)); - req->data.serverres.serverListenPort[0] = htolel(ourport); - req->data.serverres.serverIpAddr[0] = htolel(d->ourip.s_addr); - transmit_response(s, req); - return 1; -} - -static int handle_alarm_message(struct skinny_req *req, struct skinnysession *s) -{ - /* no response necessary */ - if (skinnydebug) - ast_verbose("Received Alarm Message: %s\n", req->data.alarm.displayMessage); - - return 1; -} - -static int handle_open_receive_channel_ack_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - struct skinny_line *l; - struct skinny_subchannel *sub; - struct ast_format_list fmt; - struct sockaddr_in sin; - struct sockaddr_in us; - uint32_t addr; - int port; - int status; - int passthruid; - - status = letohl(req->data.openreceivechannelack.status); - if (status) { - ast_log(LOG_ERROR, "Open Receive Channel Failure\n"); - return 0; - } - addr = letohl(req->data.openreceivechannelack.ipAddr); - port = letohl(req->data.openreceivechannelack.port); - passthruid = letohl(req->data.openreceivechannelack.passThruId); - - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = addr; - sin.sin_port = htons(port); - - sub = find_subchannel_by_reference(d, passthruid); - - if (!sub) - return 0; - - l = sub->parent; - - if (sub->rtp) { - ast_rtp_set_peer(sub->rtp, &sin); - ast_rtp_get_us(sub->rtp, &us); - } else { - ast_log(LOG_ERROR, "No RTP structure, this is very bad\n"); - return 0; - } - - if (skinnydebug) { - ast_verbose("ipaddr = %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - ast_verbose("ourip = %s:%d\n", ast_inet_ntoa(d->ourip), ntohs(us.sin_port)); - } - - if (!(req = req_alloc(sizeof(struct start_media_transmission_message), START_MEDIA_TRANSMISSION_MESSAGE))) - return -1; - - fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability)); - - if (skinnydebug) - ast_verbose("Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms); - - req->data.startmedia.conferenceId = htolel(sub->callid); - req->data.startmedia.passThruPartyId = htolel(sub->callid); - req->data.startmedia.remoteIp = htolel(d->ourip.s_addr); - req->data.startmedia.remotePort = htolel(ntohs(us.sin_port)); - req->data.startmedia.packetSize = htolel(fmt.cur_ms); - req->data.startmedia.payloadType = htolel(codec_ast2skinny(fmt.bits)); - req->data.startmedia.qualifier.precedence = htolel(127); - req->data.startmedia.qualifier.vad = htolel(0); - req->data.startmedia.qualifier.packets = htolel(0); - req->data.startmedia.qualifier.bitRate = htolel(0); - transmit_response(s, req); - - return 1; -} - -static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - struct skinny_line *l; - struct skinny_subchannel *sub = NULL; - struct ast_channel *c; - pthread_t t; - - if (skinnydebug) - ast_verbose("Received Enbloc Call: %s\n", req->data.enbloccallmessage.calledParty); - - sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference); - - if (!sub) { - l = find_line_by_instance(d, d->lastlineinstance); - if (!l) { - return 0; - } - } else { - l = sub->parent; - } - - c = skinny_new(l, AST_STATE_DOWN); - - if(!c) { - ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name); - } else { - l->hookstate = SKINNY_OFFHOOK; - - sub = c->tech_pvt; - transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - if (skinnydebug) - ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); - - if (!ast_ignore_pattern(c->context, req->data.enbloccallmessage.calledParty)) { - transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); - } - ast_copy_string(c->exten, req->data.enbloccallmessage.calledParty, sizeof(c->exten)); - if (ast_pthread_create(&t, NULL, skinny_newcall, c)) { - ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno)); - ast_hangup(c); - } - } - - return 1; -} - - -static int handle_soft_key_set_req_message(struct skinny_req *req, struct skinnysession *s) -{ - int i; - int x; - int y; - const struct soft_key_definitions *softkeymode = soft_key_default_definitions; - - if (!(req = req_alloc(sizeof(struct soft_key_set_res_message), SOFT_KEY_SET_RES_MESSAGE))) - return -1; - - req->data.softkeysets.softKeySetOffset = htolel(0); - req->data.softkeysets.softKeySetCount = htolel(11); - req->data.softkeysets.totalSoftKeySetCount = htolel(11); - for (x = 0; x < sizeof(soft_key_default_definitions) / sizeof(struct soft_key_definitions); x++) { - const uint8_t *defaults = softkeymode->defaults; - /* XXX I wanted to get the size of the array dynamically, but that wasn't wanting to work. - This will have to do for now. */ - for (y = 0; y < softkeymode->count; y++) { - for (i = 0; i < (sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition)); i++) { - if (defaults[y] == i+1) { - req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyTemplateIndex[y] = htolel(i+1); - req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyInfoIndex[y] = htolel(i+301); - } - } - } - softkeymode++; - } - transmit_response(s,req); - transmit_selectsoftkeys(s, 0, 0, KEYDEF_ONHOOK); - return 1; -} - -static int handle_soft_key_event_message(struct skinny_req *req, struct skinnysession *s) -{ - struct skinny_device *d = s->device; - struct skinny_line *l; - struct skinny_subchannel *sub = NULL; - struct ast_channel *c; - pthread_t t; - int event; - int instance; - int callreference; - - event = letohl(req->data.softkeyeventmessage.softKeyEvent); - instance = letohl(req->data.softkeyeventmessage.instance); - callreference = letohl(req->data.softkeyeventmessage.callreference); - - if (instance) { - l = find_line_by_instance(d, instance); - if (callreference) { - sub = find_subchannel_by_instance_reference(d, instance, callreference); - } else { - sub = find_subchannel_by_instance_reference(d, instance, d->lastcallreference); - } - } else { - l = find_line_by_instance(d, d->lastlineinstance); - } - - if (!l) { - if (skinnydebug) - ast_verbose("Received Softkey Event: %d(%d/%d)\n", event, instance, callreference); - return 0; - } - - switch(event) { - case SOFTKEY_NONE: - if (skinnydebug) - ast_verbose("Received Softkey Event: None(%d/%d)\n", instance, callreference); - break; - case SOFTKEY_REDIAL: - if (skinnydebug) - ast_verbose("Received Softkey Event: Redial(%d/%d)\n", instance, callreference); - -#if 0 - if (!sub || !sub->owner) { - c = skinny_new(l, AST_STATE_DOWN); - } else { - c = sub->owner; - } - - if(!c) { - ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name); - } else { - sub = c->tech_pvt; - transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - if (skinnydebug) - ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); - transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT); - - if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) { - transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); - } - ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten)); - if (ast_pthread_create(&t, NULL, skinny_newcall, c)) { - ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno)); - ast_hangup(c); - } - } -#endif - break; - case SOFTKEY_NEWCALL: /* Actually the DIAL softkey */ - if (skinnydebug) - ast_verbose("Received Softkey Event: New Call(%d/%d)\n", instance, callreference); - - if (!sub || !sub->owner) { - c = skinny_new(l, AST_STATE_DOWN); - } else { - c = sub->owner; - } - - if (!c) { - ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name); - } else { - sub = c->tech_pvt; - if (l->hookstate == SKINNY_ONHOOK) { - l->hookstate = SKINNY_OFFHOOK; - transmit_speaker_mode(s, SKINNY_SPEAKERON); - transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - } - - if (skinnydebug) - ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name); - transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */ - transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid); - transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK); - - /* start the switch thread */ - if (ast_pthread_create(&t, NULL, skinny_ss, c)) { - ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno)); - ast_hangup(c); - } - } - break; - case SOFTKEY_HOLD: - if (skinnydebug) - ast_verbose("Received Softkey Event: Hold(%d/%d)\n", instance, callreference); - - if (sub) { - if (sub->onhold) { - skinny_unhold(sub); - } else { - skinny_hold(sub); - } - } - - break; - case SOFTKEY_TRNSFER: - if (skinnydebug) - ast_verbose("Received Softkey Event: Transfer(%d/%d)\n", instance, callreference); - /* XXX figure out how to transfer */ - break; - case SOFTKEY_CFWDALL: - if (skinnydebug) - ast_verbose("Received Softkey Event: Forward All(%d/%d)\n", instance, callreference); - - /* Do not disturb */ - if (l->dnd != 0){ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Disabling DND on %s@%s\n", l->name, d->name); - l->dnd = 0; - transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON); - transmit_displaynotify(s, "DnD disabled", 10); - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Enabling DND on %s@%s\n", l->name, d->name); - l->dnd = 1; - transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_OFF); - transmit_displaynotify(s, "DnD enabled", 10); - } - break; - case SOFTKEY_CFWDBUSY: - if (skinnydebug) - ast_verbose("Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference); - break; - case SOFTKEY_CFWDNOANSWER: - if (skinnydebug) - ast_verbose("Received Softkey Event: Forward No Answer (%d/%d)\n", instance, callreference); - break; - case SOFTKEY_BKSPC: - if (skinnydebug) - ast_verbose("Received Softkey Event: Backspace(%d/%d)\n", instance, callreference); - break; - case SOFTKEY_ENDCALL: - if (skinnydebug) - ast_verbose("Received Softkey Event: End Call(%d/%d)\n", instance, callreference); - - if (l->hookstate == SKINNY_ONHOOK) { - /* Something else already put us back on hook */ - break; - } - if (sub) { - sub->cxmode = SKINNY_CX_RECVONLY; - l->hookstate = SKINNY_ONHOOK; - transmit_callstate(s, l->instance, l->hookstate, sub->callid); - if (skinnydebug) - ast_verbose("Skinny %s@%s went on hook\n", l->name, d->name); - if (l->transfer && (sub->owner && sub->next && sub->next->owner) && ((!sub->outgoing) || (sub->next && !sub->next->outgoing))) { - /* We're allowed to transfer, we have two active calls and - we made at least one of the calls. Let's try and transfer */ - -#if 0 - if ((res = attempt_transfer(p)) < 0) { - if (sub->next && sub->next->owner) { - sub->next->alreadygone = 1; - ast_queue_hangup(sub->next->owner, 1); - } - } else if (res) { - ast_log(LOG_WARNING, "Transfer attempt failed\n"); - break; - } -#endif - } else { - /* Hangup the current call */ - /* If there is another active call, skinny_hangup will ring the phone with the other call */ - if (sub->owner) { - sub->alreadygone = 1; - ast_queue_hangup(sub->owner); - } else { - ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n", - l->name, d->name, sub->callid); - } - } - if ((l->hookstate == SKINNY_ONHOOK) && (sub->next && !sub->next->rtp)) { - do_housekeeping(s); - } - } - break; - case SOFTKEY_RESUME: - if (skinnydebug) - ast_verbose("Received Softkey Event: Resume(%d/%d)\n", instance, callreference); - break; - case SOFTKEY_ANSWER: - if (skinnydebug) - ast_verbose("Received Softkey Event: Answer(%d/%d)\n", instance, callreference); - - transmit_ringer_mode(s,SKINNY_RING_OFF); - transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); - - l->hookstate = SKINNY_OFFHOOK; - - if (sub && sub->outgoing) { - /* We're answering a ringing call */ - ast_queue_control(sub->owner, AST_CONTROL_ANSWER); - transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid); - transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid); - transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid); - transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED); - start_rtp(sub); - ast_setstate(sub->owner, AST_STATE_UP); - } - break; - case SOFTKEY_INFO: - if (skinnydebug) - ast_verbose("Received Softkey Event: Info(%d/%d)\n", instance, callreference); - break; - case SOFTKEY_CONFRN: - if (skinnydebug) - ast_verbose("Received Softkey Event: Conference(%d/%d)\n", instance, callreference); - /* XXX determine the best way to pull off a conference. Meetme? */ - break; - case SOFTKEY_PARK: - if (skinnydebug) - ast_verbose("Received Softkey Event: Park Call(%d/%d)\n", instance, callreference); - /* XXX Park the call */ - break; - case SOFTKEY_JOIN: - if (skinnydebug) - ast_verbose("Received Softkey Event: Join(%d/%d)\n", instance, callreference); - break; - case SOFTKEY_MEETME: - /* XXX How is this different from CONFRN? */ - if (skinnydebug) - ast_verbose("Received Softkey Event: Meetme(%d/%d)\n", instance, callreference); - break; - case SOFTKEY_PICKUP: - if (skinnydebug) - ast_verbose("Received Softkey Event: Pickup(%d/%d)\n", instance, callreference); - break; - case SOFTKEY_GPICKUP: - if (skinnydebug) - ast_verbose("Received Softkey Event: Group Pickup(%d/%d)\n", instance, callreference); - break; - default: - if (skinnydebug) - ast_verbose("Received unknown Softkey Event: %d(%d/%d)\n", event, instance, callreference); - break; - } - return 1; -} - -static int handle_unregister_message(struct skinny_req *req, struct skinnysession *s) -{ - return skinny_unregister(req, s); -} - -static int handle_soft_key_template_req_message(struct skinny_req *req, struct skinnysession *s) -{ - if (!(req = req_alloc(sizeof(struct soft_key_template_res_message), SOFT_KEY_TEMPLATE_RES_MESSAGE))) - return -1; - - req->data.softkeytemplate.softKeyOffset = htolel(0); - req->data.softkeytemplate.softKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition)); - req->data.softkeytemplate.totalSoftKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition)); - memcpy(req->data.softkeytemplate.softKeyTemplateDefinition, - soft_key_template_default, - sizeof(soft_key_template_default)); - transmit_response(s,req); - return 1; -} - -static int handle_headset_status_message(struct skinny_req *req, struct skinnysession *s) -{ - /* XXX umm...okay? Why do I care? */ - return 1; -} - -static int handle_register_available_lines_message(struct skinny_req *req, struct skinnysession *s) -{ - /* XXX I have no clue what this is for, but my phone was sending it, so... */ - return 1; -} - -static int handle_message(struct skinny_req *req, struct skinnysession *s) -{ - int res = 0; - struct skinny_device *d = s->device; - struct skinny_subchannel *sub; - int lineInstance; - int callReference; - - if ((!s->device) && (letohl(req->e) != REGISTER_MESSAGE && letohl(req->e) != ALARM_MESSAGE)) { - ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e); - free(req); - return 0; - } - - switch(letohl(req->e)) { - case KEEP_ALIVE_MESSAGE: - res = handle_keep_alive_message(req, s); - break; - case REGISTER_MESSAGE: - if (skinnydebug) - ast_verbose("Device %s is attempting to register\n", req->data.reg.name); - - res = handle_register_message(req, s); - break; - case IP_PORT_MESSAGE: - res = handle_ip_port_message(req, s); - break; - case KEYPAD_BUTTON_MESSAGE: - if (skinnydebug) - ast_verbose("Collected digit: [%d]\n", letohl(req->data.keypad.button)); - - lineInstance = letohl(req->data.keypad.lineInstance); - callReference = letohl(req->data.keypad.callReference); - - sub = find_subchannel_by_instance_reference(d, lineInstance, callReference); - - if (sub && (sub->owner && sub->owner->_state < AST_STATE_UP)) { - char dgt; - int digit = letohl(req->data.keypad.button); - size_t len; - - if (digit == 14) { - dgt = '*'; - } else if (digit == 15) { - dgt = '#'; - } else if (digit >= 0 && digit <= 9) { - dgt = '0' + digit; - } else { - /* digit=10-13 (A,B,C,D ?), or - * digit is bad value - * - * probably should not end up here, but set - * value for backward compatibility, and log - * a warning. - */ - dgt = '0' + digit; - ast_log(LOG_WARNING, "Unsupported digit %d\n", digit); - } - - len = strlen(d->exten); - if (len < sizeof(d->exten) - 1) { - d->exten[len] = dgt; - d->exten[len+1] = '\0'; - } else { - ast_log(LOG_WARNING, "Dropping digit with value %d because digit queue is full\n", dgt); - } - } else - res = handle_keypad_button_message(req, s); - break; - case ENBLOC_CALL_MESSAGE: - res = handle_enbloc_call_message(req, s); - break; - case STIMULUS_MESSAGE: - res = handle_stimulus_message(req, s); - break; - case OFFHOOK_MESSAGE: - res = handle_offhook_message(req, s); - break; - case ONHOOK_MESSAGE: - res = handle_onhook_message(req, s); - break; - case CAPABILITIES_RES_MESSAGE: - if (skinnydebug) - ast_verbose("Received CapabilitiesRes\n"); - - res = handle_capabilities_res_message(req, s); - break; - case SPEED_DIAL_STAT_REQ_MESSAGE: - if (skinnydebug) - ast_verbose("Received SpeedDialStatRequest\n"); - - res = handle_speed_dial_stat_req_message(req, s); - break; - case LINE_STATE_REQ_MESSAGE: - if (skinnydebug) - ast_verbose("Received LineStatRequest\n"); - res = handle_line_state_req_message(req, s); - break; - case TIME_DATE_REQ_MESSAGE: - if (skinnydebug) - ast_verbose("Received Time/Date Request\n"); - - res = handle_time_date_req_message(req, s); - break; - case BUTTON_TEMPLATE_REQ_MESSAGE: - if (skinnydebug) - ast_verbose("Buttontemplate requested\n"); - - res = handle_button_template_req_message(req, s); - break; - case VERSION_REQ_MESSAGE: - if (skinnydebug) - ast_verbose("Version Request\n"); - - res = handle_version_req_message(req, s); - break; - case SERVER_REQUEST_MESSAGE: - if (skinnydebug) - ast_verbose("Received Server Request\n"); - - res = handle_server_request_message(req, s); - break; - case ALARM_MESSAGE: - res = handle_alarm_message(req, s); - break; - case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE: - if (skinnydebug) - ast_verbose("Received Open Receive Channel Ack\n"); - - res = handle_open_receive_channel_ack_message(req, s); - break; - case SOFT_KEY_SET_REQ_MESSAGE: - if (skinnydebug) - ast_verbose("Received SoftKeySetReq\n"); - - res = handle_soft_key_set_req_message(req, s); - break; - case SOFT_KEY_EVENT_MESSAGE: - res = handle_soft_key_event_message(req, s); - break; - case UNREGISTER_MESSAGE: - if (skinnydebug) - ast_verbose("Received Unregister Request\n"); - - res = handle_unregister_message(req, s); - break; - case SOFT_KEY_TEMPLATE_REQ_MESSAGE: - if (skinnydebug) - ast_verbose("Received SoftKey Template Request\n"); - - res = handle_soft_key_template_req_message(req, s); - break; - case HEADSET_STATUS_MESSAGE: - res = handle_headset_status_message(req, s); - break; - case REGISTER_AVAILABLE_LINES_MESSAGE: - res = handle_register_available_lines_message(req, s); - break; - default: - if (skinnydebug) - ast_verbose("RECEIVED UNKNOWN MESSAGE TYPE: %x\n", letohl(req->e)); - break; - } - if (res >= 0 && req) - free(req); - return res; -} - -static void destroy_session(struct skinnysession *s) -{ - struct skinnysession *cur, *prev = NULL; - ast_mutex_lock(&sessionlock); - cur = sessions; - while(cur) { - if (cur == s) { - break; - } - prev = cur; - cur = cur->next; - } - if (cur) { - if (prev) { - prev->next = cur->next; - } else { - sessions = cur->next; - } - if (s->fd > -1) { - close(s->fd); - } - ast_mutex_destroy(&s->lock); - free(s); - } else { - ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s); - } - ast_mutex_unlock(&sessionlock); -} - -static int get_input(struct skinnysession *s) -{ - int res; - int dlen = 0; - struct pollfd fds[1]; - - fds[0].fd = s->fd; - fds[0].events = POLLIN; - fds[0].revents = 0; - res = poll(fds, 1, (keep_alive * 1100)); /* If nothing has happen, client is dead */ - /* we add 10% to the keep_alive to deal */ - /* with network delays, etc */ - if (res < 0) { - if (errno != EINTR) { - ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno)); - return res; - } - } else if (res == 0) { - if (skinnydebug) - ast_verbose("Skinny Client was lost, unregistering\n"); - skinny_unregister(NULL, s); - return -1; - } - - if (fds[0].revents) { - ast_mutex_lock(&s->lock); - memset(s->inbuf,0,sizeof(s->inbuf)); - res = read(s->fd, s->inbuf, 4); - if (res < 0) { - ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno)); - - if (skinnydebug) - ast_verbose("Skinny Client was lost, unregistering\n"); - - skinny_unregister(NULL,s); - ast_mutex_unlock(&s->lock); - return res; - } else if (res != 4) { - ast_log(LOG_WARNING, "Skinny Client sent less data than expected. Expected 4 but got %d.\n", res); - ast_mutex_unlock(&s->lock); - - if (res == 0) { - if (skinnydebug) - ast_verbose("Skinny Client was lost, unregistering\n"); - skinny_unregister(NULL, s); - } - - return -1; - } - - dlen = letohl(*(int *)s->inbuf); - if (dlen < 4) { - ast_log(LOG_WARNING, "Skinny Client sent invalid data.\n"); - ast_mutex_unlock(&s->lock); - return -1; - } - if (dlen+8 > sizeof(s->inbuf)) { - dlen = sizeof(s->inbuf) - 8; - } - *(int *)s->inbuf = htolel(dlen); - - res = read(s->fd, s->inbuf+4, dlen+4); - ast_mutex_unlock(&s->lock); - if (res < 0) { - ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno)); - return res; - } else if (res != (dlen+4)) { - ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n"); - return -1; - } - return res; - } - return 0; -} - -static struct skinny_req *skinny_req_parse(struct skinnysession *s) -{ - struct skinny_req *req; - - if (!(req = ast_calloc(1, SKINNY_MAX_PACKET))) - return NULL; - - ast_mutex_lock(&s->lock); - memcpy(req, s->inbuf, skinny_header_size); - memcpy(&req->data, s->inbuf+skinny_header_size, letohl(*(int*)(s->inbuf))-4); - - ast_mutex_unlock(&s->lock); - - if (letohl(req->e) < 0) { - ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd); - free(req); - return NULL; - } - - return req; -} - -static void *skinny_session(void *data) -{ - int res; - struct skinny_req *req; - struct skinnysession *s = data; - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Starting Skinny session from %s\n", ast_inet_ntoa(s->sin.sin_addr)); - - for (;;) { - res = get_input(s); - if (res < 0) { - break; - } - - if (res > 0) - { - if (!(req = skinny_req_parse(s))) { - destroy_session(s); - return NULL; - } - - res = handle_message(req, s); - if (res < 0) { - destroy_session(s); - return NULL; - } - } - } - ast_log(LOG_NOTICE, "Skinny Session returned: %s\n", strerror(errno)); - - if (s) - destroy_session(s); - - return 0; -} - -static void *accept_thread(void *ignore) -{ - int as; - struct sockaddr_in sin; - socklen_t sinlen; - struct skinnysession *s; - struct protoent *p; - int arg = 1; - pthread_attr_t attr; - pthread_t tcp_thread; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - for (;;) { - sinlen = sizeof(sin); - as = accept(skinnysock, (struct sockaddr *)&sin, &sinlen); - if (as < 0) { - ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno)); - continue; - } - p = getprotobyname("tcp"); - if(p) { - if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) { - ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno)); - } - } - if (!(s = ast_calloc(1, sizeof(struct skinnysession)))) - continue; - - memcpy(&s->sin, &sin, sizeof(sin)); - ast_mutex_init(&s->lock); - s->fd = as; - ast_mutex_lock(&sessionlock); - s->next = sessions; - sessions = s; - ast_mutex_unlock(&sessionlock); - - if (ast_pthread_create(&tcp_thread, &attr, skinny_session, s)) { - destroy_session(s); - } - } - if (skinnydebug) - ast_verbose("killing accept thread\n"); - close(as); - pthread_attr_destroy(&attr); - return 0; -} - -static void *do_monitor(void *data) -{ - int res; - - /* This thread monitors all the interfaces which are not yet in use - (and thus do not have a separate thread) indefinitely */ - /* From here on out, we die whenever asked */ - for(;;) { - pthread_testcancel(); - /* Wait for sched or io */ - res = ast_sched_wait(sched); - if ((res < 0) || (res > 1000)) { - res = 1000; - } - res = ast_io_wait(io, res); - ast_mutex_lock(&monlock); - if (res >= 0) { - ast_sched_runq(sched); - } - ast_mutex_unlock(&monlock); - } - /* Never reached */ - return NULL; - -} - -static int restart_monitor(void) -{ - /* If we're supposed to be stopped -- stay stopped */ - if (monitor_thread == AST_PTHREADT_STOP) - return 0; - - ast_mutex_lock(&monlock); - if (monitor_thread == pthread_self()) { - ast_mutex_unlock(&monlock); - ast_log(LOG_WARNING, "Cannot kill myself\n"); - return -1; - } - if (monitor_thread != AST_PTHREADT_NULL) { - /* Wake up the thread */ - pthread_kill(monitor_thread, SIGURG); - } else { - /* Start a new monitor */ - if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) { - ast_mutex_unlock(&monlock); - ast_log(LOG_ERROR, "Unable to start monitor thread.\n"); - return -1; - } - } - ast_mutex_unlock(&monlock); - return 0; -} - -static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause) -{ - int oldformat; - - struct skinny_line *l; - struct ast_channel *tmpc = NULL; - char tmp[256]; - char *dest = data; - - oldformat = format; - - if (!(format &= ((AST_FORMAT_MAX_AUDIO << 1) - 1))) { - ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format); - return NULL; - } - - ast_copy_string(tmp, dest, sizeof(tmp)); - if (ast_strlen_zero(tmp)) { - ast_log(LOG_NOTICE, "Skinny channels require a device\n"); - return NULL; - } - l = find_line_by_name(tmp); - if (!l) { - ast_log(LOG_NOTICE, "No available lines on: %s\n", dest); - return NULL; - } - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "skinny_request(%s)\n", tmp); - } - tmpc = skinny_new(l, AST_STATE_DOWN); - if (!tmpc) { - ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp); - } - restart_monitor(); - return tmpc; -} - -static int reload_config(void) -{ - int on = 1; - struct ast_config *cfg; - struct ast_variable *v; - char *cat; - struct skinny_device *d; - int oldport = ntohs(bindaddr.sin_port); - - if (gethostname(ourhost, sizeof(ourhost))) { - ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled\n"); - return 0; - } - cfg = ast_config_load(config); - - /* We *must* have a config file otherwise stop immediately */ - if (!cfg) { - ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled\n", config); - return -1; - } - memset(&bindaddr, 0, sizeof(bindaddr)); - memset(&default_prefs, 0, sizeof(default_prefs)); - - /* Copy the default jb config over global_jbconf */ - memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf)); - - /* load the general section */ - v = ast_variable_browse(cfg, "general"); - while (v) { - /* handle jb conf */ - if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) { - v = v->next; - continue; - } - - /* Create the interface list */ - if (!strcasecmp(v->name, "bindaddr")) { - if (!(hp = ast_gethostbyname(v->value, &ahp))) { - ast_log(LOG_WARNING, "Invalid address: %s\n", v->value); - } else { - memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr)); - } - } else if (!strcasecmp(v->name, "keepalive")) { - keep_alive = atoi(v->value); - } else if (!strcasecmp(v->name, "dateformat")) { - memcpy(date_format, v->value, sizeof(date_format)); - } else if (!strcasecmp(v->name, "allow")) { - ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 1); - } else if (!strcasecmp(v->name, "disallow")) { - ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 0); - } else if (!strcasecmp(v->name, "bindport") || !strcasecmp(v->name, "port")) { - if (sscanf(v->value, "%d", &ourport) == 1) { - bindaddr.sin_port = htons(ourport); - } else { - ast_log(LOG_WARNING, "Invalid bindport '%s' at line %d of %s\n", v->value, v->lineno, config); - } - if (!strcasecmp(v->name, "port")) { /*! \todo Remove 'port' option after 1.4 */ - ast_log(LOG_WARNING, "Option 'port' at line %d of %s has been deprecated. Please use 'bindport' instead.\n", v->lineno, config); - } - } - v = v->next; - } - - if (ntohl(bindaddr.sin_addr.s_addr)) { - __ourip = bindaddr.sin_addr; - } else { - hp = ast_gethostbyname(ourhost, &ahp); - if (!hp) { - ast_log(LOG_WARNING, "Unable to get our IP address, Skinny disabled\n"); - ast_config_destroy(cfg); - return 0; - } - memcpy(&__ourip, hp->h_addr, sizeof(__ourip)); - } - if (!ntohs(bindaddr.sin_port)) { - bindaddr.sin_port = ntohs(DEFAULT_SKINNY_PORT); - } - bindaddr.sin_family = AF_INET; - - /* load the device sections */ - cat = ast_category_browse(cfg, NULL); - while(cat) { - if (!strcasecmp(cat, "general")) { - /* Nothing to do */ -#if 0 - } else if (!strncasecmp(cat, "paging-", 7)) { - p = build_paging_device(cat, ast_variable_browse(cfg, cat)); - if (p) { - } -#endif - } else { - d = build_device(cat, ast_variable_browse(cfg, cat)); - if (d) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Added device '%s'\n", d->name); - ast_mutex_lock(&devicelock); - d->next = devices; - devices = d; - ast_mutex_unlock(&devicelock); - } - } - cat = ast_category_browse(cfg, cat); - } - ast_mutex_lock(&netlock); - if ((skinnysock > -1) && (ntohs(bindaddr.sin_port) != oldport)) { - close(skinnysock); - skinnysock = -1; - } - if (skinnysock < 0) { - skinnysock = socket(AF_INET, SOCK_STREAM, 0); - if(setsockopt(skinnysock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { - ast_log(LOG_ERROR, "Set Socket Options failed: errno %d, %s\n", errno, strerror(errno)); - ast_config_destroy(cfg); - ast_mutex_unlock(&netlock); - return 0; - } - if (skinnysock < 0) { - ast_log(LOG_WARNING, "Unable to create Skinny socket: %s\n", strerror(errno)); - } else { - if (bind(skinnysock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { - ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n", - ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port), - strerror(errno)); - close(skinnysock); - skinnysock = -1; - ast_config_destroy(cfg); - ast_mutex_unlock(&netlock); - return 0; - } - if (listen(skinnysock,DEFAULT_SKINNY_BACKLOG)) { - ast_log(LOG_WARNING, "Failed to start listening to %s:%d: %s\n", - ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port), - strerror(errno)); - close(skinnysock); - skinnysock = -1; - ast_config_destroy(cfg); - ast_mutex_unlock(&netlock); - return 0; - } - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Skinny listening on %s:%d\n", - ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port)); - ast_pthread_create_background(&accept_t,NULL, accept_thread, NULL); - } - } - ast_mutex_unlock(&netlock); - ast_config_destroy(cfg); - return 1; -} - -static void delete_devices(void) -{ - struct skinny_device *d, *dlast; - struct skinny_line *l, *llast; - struct skinny_speeddial *sd, *sdlast; - struct skinny_addon *a, *alast; - - ast_mutex_lock(&devicelock); - - /* Delete all devices */ - for (d=devices;d;) { - /* Delete all lines for this device */ - for (l=d->lines;l;) { - llast = l; - l = l->next; - ast_mutex_destroy(&llast->lock); - free(llast); - } - /* Delete all speeddials for this device */ - for (sd=d->speeddials;sd;) { - sdlast = sd; - sd = sd->next; - ast_mutex_destroy(&sdlast->lock); - free(sdlast); - } - /* Delete all addons for this device */ - for (a=d->addons;a;) { - alast = a; - a = a->next; - ast_mutex_destroy(&alast->lock); - free(alast); - } - dlast = d; - d = d->next; - free(dlast); - } - devices=NULL; - ast_mutex_unlock(&devicelock); -} - -#if 0 -/* - * XXX This never worked properly anyways. - * Let's get rid of it, until we can fix it. - */ -static int reload(void) -{ - delete_devices(); - reload_config(); - restart_monitor(); - return 0; -} -#endif - -static int load_module(void) -{ - int res = 0; - - for (; res < (sizeof(soft_key_template_default) / sizeof(soft_key_template_default[0])); res++) { - soft_key_template_default[res].softKeyEvent = htolel(soft_key_template_default[res].softKeyEvent); - } - /* load and parse config */ - res = reload_config(); - if (res == -1) { - return AST_MODULE_LOAD_DECLINE; - } - - /* Make sure we can register our skinny channel type */ - if (ast_channel_register(&skinny_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class 'Skinny'\n"); - return -1; - } - - ast_rtp_proto_register(&skinny_rtp); - ast_cli_register_multiple(cli_skinny, sizeof(cli_skinny) / sizeof(struct ast_cli_entry)); - sched = sched_context_create(); - if (!sched) { - ast_log(LOG_WARNING, "Unable to create schedule context\n"); - } - io = io_context_create(); - if (!io) { - ast_log(LOG_WARNING, "Unable to create I/O context\n"); - } - /* And start the monitor for the first time */ - restart_monitor(); - - return res; -} - -static int unload_module(void) -{ - struct skinnysession *s, *slast; - struct skinny_device *d; - struct skinny_line *l; - struct skinny_subchannel *sub; - - ast_mutex_lock(&sessionlock); - /* Destroy all the interfaces and free their memory */ - s = sessions; - while(s) { - slast = s; - s = s->next; - for (d = slast->device; d; d = d->next) { - for (l = d->lines; l; l = l->next) { - ast_mutex_lock(&l->lock); - for (sub = l->sub; sub; sub = sub->next) { - ast_mutex_lock(&sub->lock); - if (sub->owner) { - sub->alreadygone = 1; - ast_softhangup(sub->owner, AST_SOFTHANGUP_APPUNLOAD); - } - ast_mutex_unlock(&sub->lock); - } - ast_mutex_unlock(&l->lock); - } - } - if (slast->fd > -1) - close(slast->fd); - ast_mutex_destroy(&slast->lock); - free(slast); - } - sessions = NULL; - ast_mutex_unlock(&sessionlock); - - delete_devices(); - - ast_mutex_lock(&monlock); - if ((monitor_thread != AST_PTHREADT_NULL) && (monitor_thread != AST_PTHREADT_STOP)) { - pthread_cancel(monitor_thread); - pthread_kill(monitor_thread, SIGURG); - pthread_join(monitor_thread, NULL); - } - monitor_thread = AST_PTHREADT_STOP; - ast_mutex_unlock(&monlock); - - ast_mutex_lock(&netlock); - if (accept_t && (accept_t != AST_PTHREADT_STOP)) { - pthread_cancel(accept_t); - pthread_kill(accept_t, SIGURG); - pthread_join(accept_t, NULL); - } - accept_t = AST_PTHREADT_STOP; - ast_mutex_unlock(&netlock); - - ast_rtp_proto_unregister(&skinny_rtp); - ast_channel_unregister(&skinny_tech); - ast_cli_unregister_multiple(cli_skinny, sizeof(cli_skinny) / sizeof(struct ast_cli_entry)); - - close(skinnysock); - if (sched) - sched_context_destroy(sched); - - return 0; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Skinny Client Control Protocol (Skinny)", - .load = load_module, - .unload = unload_module, - ); diff --git a/1.4.23-rc4/channels/chan_vpb.cc b/1.4.23-rc4/channels/chan_vpb.cc deleted file mode 100644 index 56467a6ce..000000000 --- a/1.4.23-rc4/channels/chan_vpb.cc +++ /dev/null @@ -1,2905 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2003, Paul Bagyenda - * Paul Bagyenda <bagyenda@dsmagic.com> - * Copyright (C) 2004 - 2005, Ben Kramer - * Ben Kramer <ben@voicetronix.com.au> - * - * Daniel Bichara <daniel@bichara.com.br> - Brazilian CallerID detection (c)2004 - * - * Welber Silveira - welberms@magiclink.com.br - (c)2004 - * Copying CLID string to propper structure after detection - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief VoiceTronix Interface driver - * - * \ingroup channel_drivers - */ - -/*** MODULEINFO - <depend>vpbapi</depend> - ***/ - -#include <vpbapi.h> - -extern "C" { - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdio.h> -#include <string.h> - -#include "asterisk/lock.h" -#include "asterisk/utils.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/callerid.h" -#include "asterisk/dsp.h" -#include "asterisk/features.h" -#include "asterisk/musiconhold.h" -} - -#include <sys/socket.h> -#include <sys/time.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> -#include <arpa/inet.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <ctype.h> - -#include <assert.h> - -#ifdef pthread_create -#undef pthread_create -#endif - -#define DEFAULT_GAIN 0 -#define DEFAULT_ECHO_CANCEL 1 - -#define VPB_SAMPLES 160 -#define VPB_MAX_BUF VPB_SAMPLES*4 + AST_FRIENDLY_OFFSET - -#define VPB_NULL_EVENT 200 - -#define VPB_WAIT_TIMEOUT 4000 - -#define MAX_VPB_GAIN 12.0 -#define MIN_VPB_GAIN -12.0 - -#define DTMF_CALLERID -#define DTMF_CID_START 'D' -#define DTMF_CID_STOP 'C' - -/**/ -#if defined(__cplusplus) || defined(c_plusplus) - extern "C" { -#endif -/**/ - -static const char desc[] = "VoiceTronix V6PCI/V12PCI/V4PCI API Support"; -static const char tdesc[] = "Standard VoiceTronix API Driver"; -static const char config[] = "vpb.conf"; - -/* Backwards compatibility from trunk */ -#define ast_verb(level, ...) do { \ - if (option_verbose >= level) { \ - if (level >= 4) \ - ast_verbose(VERBOSE_PREFIX_4 __VA_ARGS__); \ - else if (level == 3) \ - ast_verbose(VERBOSE_PREFIX_3 __VA_ARGS__); \ - else if (level == 2) \ - ast_verbose(VERBOSE_PREFIX_2 __VA_ARGS__); \ - else if (level == 1) \ - ast_verbose(VERBOSE_PREFIX_1 __VA_ARGS__); \ - else \ - ast_verbose(__VA_ARGS__); \ - } \ -} while (0) - -#define ast_debug(level, ...) do { \ - if (option_debug >= (level)) \ - ast_log(LOG_DEBUG, __VA_ARGS__); \ -} while (0) - -/* Default context for dialtone mode */ -static char context[AST_MAX_EXTENSION] = "default"; - -/* Default language */ -static char language[MAX_LANGUAGE] = ""; - -static int gruntdetect_timeout = 3600000; /* Grunt detect timeout is 1hr. */ - -static const int prefformat = AST_FORMAT_SLINEAR; - -/* Protect the interface list (of vpb_pvt's) */ -AST_MUTEX_DEFINE_STATIC(iflock); - -/* Protect the monitoring thread, so only one process can kill or start it, and not - when it's doing something critical. */ -AST_MUTEX_DEFINE_STATIC(monlock); - -/* This is the thread for the monitor which checks for input on the channels - which are not currently in use. */ -static pthread_t monitor_thread; - -static int mthreadactive = -1; /* Flag for monitoring monitorthread.*/ - - -static int restart_monitor(void); - -/* The private structures of the VPB channels are - linked for selecting outgoing channels */ - -#define MODE_DIALTONE 1 -#define MODE_IMMEDIATE 2 -#define MODE_FXO 3 - -/* Pick a country or add your own! */ -/* These are the tones that are played to the user */ -#define TONES_AU -/* #define TONES_USA */ - -#ifdef TONES_AU -static VPB_TONE Dialtone = {440, 440, 440, -10, -10, -10, 5000, 0 }; -static VPB_TONE Busytone = {470, 0, 0, -10, -100, -100, 5000, 0 }; -static VPB_TONE Ringbacktone = {400, 50, 440, -10, -10, -10, 1400, 800 }; -#endif -#ifdef TONES_USA -static VPB_TONE Dialtone = {350, 440, 0, -16, -16, -100, 10000, 0}; -static VPB_TONE Busytone = {480, 620, 0, -10, -10, -100, 500, 500}; -static VPB_TONE Ringbacktone = {440, 480, 0, -20, -20, -100, 2000, 4000}; -#endif - -/* grunt tone defn's */ -#if 0 -static VPB_DETECT toned_grunt = { 3, VPB_GRUNT, 1, 2000, 3000, 0, 0, -40, 0, 0, 0, 40, { { VPB_DELAY, 1000, 0, 0 }, { VPB_RISING, 0, 40, 0 }, { 0, 100, 0, 0 } } }; -#endif -static VPB_DETECT toned_ungrunt = { 2, VPB_GRUNT, 1, 2000, 1, 0, 0, -40, 0, 0, 30, 40, { { 0, 0, 0, 0 } } }; - -/* Use loop polarity detection for CID */ -static int UsePolarityCID=0; - -/* Use loop drop detection */ -static int UseLoopDrop=1; - -/* To use or not to use Native bridging */ -static int UseNativeBridge=1; - -/* Use Asterisk Indication or VPB */ -static int use_ast_ind=0; - -/* Use Asterisk DTMF detection or VPB */ -static int use_ast_dtmfdet=0; - -static int relaxdtmf=0; - -/* Use Asterisk DTMF play back or VPB */ -static int use_ast_dtmf=0; - -/* Break for DTMF on native bridge ? */ -static int break_for_dtmf=1; - -/* Set EC suppression threshold */ -static short ec_supp_threshold=-1; - -/* Inter Digit Delay for collecting DTMF's */ -static int dtmf_idd = 3000; - -#define TIMER_PERIOD_RINGBACK 2000 -#define TIMER_PERIOD_BUSY 700 -#define TIMER_PERIOD_RING 4000 -static int timer_period_ring = TIMER_PERIOD_RING; - -#define VPB_EVENTS_ALL (VPB_MRING|VPB_MDIGIT|VPB_MDTMF|VPB_MTONEDETECT|VPB_MTIMEREXP \ - |VPB_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \ - |VPB_MRING_OFF|VPB_MDROP|VPB_MSTATION_FLASH) -#define VPB_EVENTS_NODROP (VPB_MRING|VPB_MDIGIT|VPB_MDTMF|VPB_MTONEDETECT|VPB_MTIMEREXP \ - |VPB_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \ - |VPB_MRING_OFF|VPB_MSTATION_FLASH) -#define VPB_EVENTS_NODTMF (VPB_MRING|VPB_MDIGIT|VPB_MTONEDETECT|VPB_MTIMEREXP \ - |VPB_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \ - |VPB_MRING_OFF|VPB_MDROP|VPB_MSTATION_FLASH) -#define VPB_EVENTS_STAT (VPB_MRING|VPB_MDIGIT|VPB_MDTMF|VPB_MTONEDETECT|VPB_MTIMEREXP \ - |VPB_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \ - |VPB_MRING_OFF|VPB_MSTATION_FLASH) - - -/* Dialing parameters for Australia */ -/* #define DIAL_WITH_CALL_PROGRESS */ -VPB_TONE_MAP DialToneMap[] = { { VPB_BUSY, VPB_CALL_DISCONNECT, 0 }, - { VPB_DIAL, VPB_CALL_DIALTONE, 0 }, - { VPB_RINGBACK, VPB_CALL_RINGBACK, 0 }, - { VPB_BUSY, VPB_CALL_BUSY, 0 }, - { VPB_GRUNT, VPB_CALL_GRUNT, 0 }, - { 0, 0, 1 } }; -#define VPB_DIALTONE_WAIT 2000 /* Wait up to 2s for a dialtone */ -#define VPB_RINGWAIT 4000 /* Wait up to 4s for ring tone after dialing */ -#define VPB_CONNECTED_WAIT 4000 /* If no ring tone detected for 4s then consider call connected */ -#define TIMER_PERIOD_NOANSWER 120000 /* Let it ring for 120s before deciding theres noone there */ - -#define MAX_BRIDGES_V4PCI 2 -#define MAX_BRIDGES_V12PCI 128 - -/* port states */ -#define VPB_STATE_ONHOOK 0 -#define VPB_STATE_OFFHOOK 1 -#define VPB_STATE_DIALLING 2 -#define VPB_STATE_JOINED 3 -#define VPB_STATE_GETDTMF 4 -#define VPB_STATE_PLAYDIAL 5 -#define VPB_STATE_PLAYBUSY 6 -#define VPB_STATE_PLAYRING 7 - -#define VPB_GOT_RXHWG 1 -#define VPB_GOT_TXHWG 2 -#define VPB_GOT_RXSWG 4 -#define VPB_GOT_TXSWG 8 - -typedef struct { - int inuse; - struct ast_channel *c0, *c1, **rc; - struct ast_frame **fo; - int flags; - ast_mutex_t lock; - ast_cond_t cond; - int endbridge; -} vpb_bridge_t; - -static vpb_bridge_t * bridges; -static int max_bridges = MAX_BRIDGES_V4PCI; - -AST_MUTEX_DEFINE_STATIC(bridge_lock); - -typedef enum { - vpb_model_unknown = 0, - vpb_model_v4pci, - vpb_model_v12pci -} vpb_model_t; - -static struct vpb_pvt { - - ast_mutex_t owner_lock; /* Protect blocks that expect ownership to remain the same */ - struct ast_channel *owner; /* Channel who owns us, possibly NULL */ - - int golock; /* Got owner lock ? */ - - int mode; /* fxo/imediate/dialtone*/ - int handle; /* Handle for vpb interface */ - - int state; /* used to keep port state (internal to driver) */ - - int group; /* Which group this port belongs to */ - ast_group_t callgroup; /* Call group */ - ast_group_t pickupgroup; /* Pickup group */ - - - char dev[256]; /* Device name, eg vpb/1-1 */ - vpb_model_t vpb_model; /* card model */ - - struct ast_frame f, fr; /* Asterisk frame interface */ - char buf[VPB_MAX_BUF]; /* Static buffer for reading frames */ - - int dialtone; /* NOT USED */ - float txgain, rxgain; /* Hardware gain control */ - float txswgain, rxswgain; /* Software gain control */ - - int wantdtmf; /* Waiting for DTMF. */ - char context[AST_MAX_EXTENSION]; /* The context for this channel */ - - char ext[AST_MAX_EXTENSION]; /* DTMF buffer for the ext[ens] */ - char language[MAX_LANGUAGE]; /* language being used */ - char callerid[AST_MAX_EXTENSION]; /* CallerId used for directly connected phone */ - int callerid_type; /* Caller ID type: 0=>none 1=>vpb 2=>AstV23 3=>AstBell */ - char cid_num[AST_MAX_EXTENSION]; - char cid_name[AST_MAX_EXTENSION]; - - int dtmf_caller_pos; /* DTMF CallerID detection (Brazil)*/ - - int lastoutput; /* Holds the last Audio format output'ed */ - int lastinput; /* Holds the last Audio format input'ed */ - int last_ignore_dtmf; - - void *busy_timer; /* Void pointer for busy vpb_timer */ - int busy_timer_id; /* unique timer ID for busy timer */ - - void *ringback_timer; /* Void pointer for ringback vpb_timer */ - int ringback_timer_id; /* unique timer ID for ringback timer */ - - void *ring_timer; /* Void pointer for ring vpb_timer */ - int ring_timer_id; /* unique timer ID for ring timer */ - - void *dtmfidd_timer; /* Void pointer for DTMF IDD vpb_timer */ - int dtmfidd_timer_id; /* unique timer ID for DTMF IDD timer */ - - struct ast_dsp *vad; /* AST Voice Activation Detection dsp */ - - struct timeval lastgrunt; /* time stamp of last grunt event */ - - ast_mutex_t lock; /* This one just protects bridge ptr below */ - vpb_bridge_t *bridge; - - int stopreads; /* Stop reading...*/ - int read_state; /* Read state */ - int chuck_count; /* a count of packets weve chucked away!*/ - pthread_t readthread; /* For monitoring read channel. One per owned channel. */ - - ast_mutex_t record_lock; /* This one prevents reentering a record_buf block */ - ast_mutex_t play_lock; /* This one prevents reentering a play_buf block */ - int play_buf_time; /* How long the last play_buf took */ - struct timeval lastplay; /* Last play time */ - - ast_mutex_t play_dtmf_lock; - char play_dtmf[16]; - - int faxhandled; /* has a fax tone been handled ? */ - - struct vpb_pvt *next; /* Next channel in list */ - -} *iflist = NULL; - -static struct ast_channel *vpb_new(struct vpb_pvt *i, enum ast_channel_state state, const char *context); -static void *do_chanreads(void *pvt); -static struct ast_channel *vpb_request(const char *type, int format, void *data, int *cause); -static int vpb_digit_begin(struct ast_channel *ast, char digit); -static int vpb_digit_end(struct ast_channel *ast, char digit, unsigned int duration); -static int vpb_call(struct ast_channel *ast, char *dest, int timeout); -static int vpb_hangup(struct ast_channel *ast); -static int vpb_answer(struct ast_channel *ast); -static struct ast_frame *vpb_read(struct ast_channel *ast); -static int vpb_write(struct ast_channel *ast, struct ast_frame *frame); -static enum ast_bridge_result ast_vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); -static int vpb_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); -static int vpb_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); - -static struct ast_channel_tech vpb_tech = { - type: "vpb", - description: tdesc, - capabilities: AST_FORMAT_SLINEAR, - properties: 0, - requester: vpb_request, - devicestate: NULL, - send_digit_begin: vpb_digit_begin, - send_digit_end: vpb_digit_end, - call: vpb_call, - hangup: vpb_hangup, - answer: vpb_answer, - read: vpb_read, - write: vpb_write, - send_text: NULL, - send_image: NULL, - send_html: NULL, - exception: NULL, - bridge: ast_vpb_bridge, - indicate: vpb_indicate, - fixup: vpb_fixup, - setoption: NULL, - queryoption: NULL, - transfer: NULL, - write_video: NULL, - bridged_channel: NULL -}; - -static struct ast_channel_tech vpb_tech_indicate = { - type: "vpb", - description: tdesc, - capabilities: AST_FORMAT_SLINEAR, - properties: 0, - requester: vpb_request, - devicestate: NULL, - send_digit_begin: vpb_digit_begin, - send_digit_end: vpb_digit_end, - call: vpb_call, - hangup: vpb_hangup, - answer: vpb_answer, - read: vpb_read, - write: vpb_write, - send_text: NULL, - send_image: NULL, - send_html: NULL, - exception: NULL, - bridge: ast_vpb_bridge, - indicate: NULL, - fixup: vpb_fixup, - setoption: NULL, - queryoption: NULL, - transfer: NULL, - write_video: NULL, - bridged_channel: NULL -}; - -/* Can't get ast_vpb_bridge() working on v4pci without either a horrible -* high pitched feedback noise or bad hiss noise depending on gain settings -* Get asterisk to do the bridging -*/ -#define BAD_V4PCI_BRIDGE - -/* This one enables a half duplex bridge which may be required to prevent high pitched - * feedback when getting asterisk to do the bridging and when using certain gain settings. - */ -/* #define HALF_DUPLEX_BRIDGE */ - -/* This is the Native bridge code, which Asterisk will try before using its own bridging code */ -static enum ast_bridge_result ast_vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms) -{ - struct vpb_pvt *p0 = (struct vpb_pvt *)c0->tech_pvt; - struct vpb_pvt *p1 = (struct vpb_pvt *)c1->tech_pvt; - int i; - int res; - struct ast_channel *cs[3]; - struct ast_channel *who; - struct ast_frame *f; - - cs[0] = c0; - cs[1] = c1; - - #ifdef BAD_V4PCI_BRIDGE - if (p0->vpb_model==vpb_model_v4pci) - return AST_BRIDGE_FAILED_NOWARN; - #endif - if ( UseNativeBridge != 1){ - return AST_BRIDGE_FAILED_NOWARN; - } - -/* - ast_mutex_lock(&p0->lock); - ast_mutex_lock(&p1->lock); -*/ - - /* Bridge channels, check if we can. I believe we always can, so find a slot.*/ - - ast_mutex_lock(&bridge_lock); - for (i = 0; i < max_bridges; i++) - if (!bridges[i].inuse) - break; - if (i < max_bridges) { - bridges[i].inuse = 1; - bridges[i].endbridge = 0; - bridges[i].flags = flags; - bridges[i].rc = rc; - bridges[i].fo = fo; - bridges[i].c0 = c0; - bridges[i].c1 = c1; - } - ast_mutex_unlock(&bridge_lock); - - if (i == max_bridges) { - ast_log(LOG_WARNING, "%s: vpb_bridge: Failed to bridge %s and %s!\n", p0->dev, c0->name, c1->name); - ast_mutex_unlock(&p0->lock); - ast_mutex_unlock(&p1->lock); - return AST_BRIDGE_FAILED_NOWARN; - } else { - /* Set bridge pointers. You don't want to take these locks while holding bridge lock.*/ - ast_mutex_lock(&p0->lock); - p0->bridge = &bridges[i]; - ast_mutex_unlock(&p0->lock); - - ast_mutex_lock(&p1->lock); - p1->bridge = &bridges[i]; - ast_mutex_unlock(&p1->lock); - - ast_verb(2, "%s: vpb_bridge: Bridging call entered with [%s, %s]\n", p0->dev, c0->name, c1->name); - } - - ast_verb(3, "Native bridging %s and %s\n", c0->name, c1->name); - - #ifdef HALF_DUPLEX_BRIDGE - - ast_debug(2, "%s: vpb_bridge: Starting half-duplex bridge [%s, %s]\n", p0->dev, c0->name, c1->name); - - int dir = 0; - - memset(p0->buf, 0, sizeof(p0->buf)); - memset(p1->buf, 0, sizeof(p1->buf)); - - vpb_record_buf_start(p0->handle, VPB_ALAW); - vpb_record_buf_start(p1->handle, VPB_ALAW); - - vpb_play_buf_start(p0->handle, VPB_ALAW); - vpb_play_buf_start(p1->handle, VPB_ALAW); - - while (!bridges[i].endbridge) { - struct vpb_pvt *from, *to; - if (++dir % 2) { - from = p0; - to = p1; - } else { - from = p1; - to = p0; - } - vpb_record_buf_sync(from->handle, from->buf, VPB_SAMPLES); - vpb_play_buf_sync(to->handle, from->buf, VPB_SAMPLES); - } - - vpb_record_buf_finish(p0->handle); - vpb_record_buf_finish(p1->handle); - - vpb_play_buf_finish(p0->handle); - vpb_play_buf_finish(p1->handle); - - ast_debug(2, "%s: vpb_bridge: Finished half-duplex bridge [%s, %s]\n", p0->dev, c0->name, c1->name); - - res = VPB_OK; - - #else - - res = vpb_bridge(p0->handle, p1->handle, VPB_BRIDGE_ON); - if (res == VPB_OK) { - /* pthread_cond_wait(&bridges[i].cond, &bridges[i].lock);*/ /* Wait for condition signal. */ - while (!bridges[i].endbridge) { - /* Are we really ment to be doing nothing ?!?! */ - who = ast_waitfor_n(cs, 2, &timeoutms); - if (!who) { - if (!timeoutms) { - res = AST_BRIDGE_RETRY; - break; - } - ast_debug(1, "%s: vpb_bridge: Empty frame read...\n", p0->dev); - /* check for hangup / whentohangup */ - if (ast_check_hangup(c0) || ast_check_hangup(c1)) - break; - continue; - } - f = ast_read(who); - if (!f || ((f->frametype == AST_FRAME_DTMF) && - (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) || - ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) { - *fo = f; - *rc = who; - ast_debug(1, "%s: vpb_bridge: Got a [%s]\n", p0->dev, f ? "digit" : "hangup"); -#if 0 - if ((c0->tech_pvt == pvt0) && (!ast_check_hangup(c0))) { - if (pr0->set_rtp_peer(c0, NULL, NULL, 0)) - ast_log(LOG_WARNING, "Channel '%s' failed to revert\n", c0->name); - } - if ((c1->tech_pvt == pvt1) && (!ast_check_hangup(c1))) { - if (pr1->set_rtp_peer(c1, NULL, NULL, 0)) - ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name); - } - /* That's all we needed */ - return 0; -#endif - /* Check if we need to break */ - if (break_for_dtmf) { - break; - } else if ((f->frametype == AST_FRAME_DTMF) && ((f->subclass == '#') || (f->subclass == '*'))) { - break; - } - } else { - if ((f->frametype == AST_FRAME_DTMF) || - (f->frametype == AST_FRAME_VOICE) || - (f->frametype == AST_FRAME_VIDEO)) - { - /* Forward voice or DTMF frames if they happen upon us */ - /* Actually I dont think we want to forward on any frames! - if (who == c0) { - ast_write(c1, f); - } else if (who == c1) { - ast_write(c0, f); - } - */ - } - ast_frfree(f); - } - /* Swap priority not that it's a big deal at this point */ - cs[2] = cs[0]; - cs[0] = cs[1]; - cs[1] = cs[2]; - }; - vpb_bridge(p0->handle, p1->handle, VPB_BRIDGE_OFF); - } - - #endif - - ast_mutex_lock(&bridge_lock); - bridges[i].inuse = 0; - ast_mutex_unlock(&bridge_lock); - - p0->bridge = NULL; - p1->bridge = NULL; - - - ast_verb(2, "Bridging call done with [%s, %s] => %d\n", c0->name, c1->name, res); - -/* - ast_mutex_unlock(&p0->lock); - ast_mutex_unlock(&p1->lock); -*/ - return (res == VPB_OK) ? AST_BRIDGE_COMPLETE : AST_BRIDGE_FAILED; -} - -/* Caller ID can be located in different positions between the rings depending on your Telco - * Australian (Telstra) callerid starts 700ms after 1st ring and finishes 1.5s after first ring - * Use ANALYSE_CID to record rings and determine location of callerid - */ -/* #define ANALYSE_CID */ -#define RING_SKIP 300 -#define CID_MSECS 2000 - -static void get_callerid(struct vpb_pvt *p) -{ - short buf[CID_MSECS*8]; /* 8kHz sampling rate */ - struct timeval cid_record_time; - int rc; - struct ast_channel *owner = p->owner; -/* - char callerid[AST_MAX_EXTENSION] = ""; -*/ -#ifdef ANALYSE_CID - void * ws; - char * file="cidsams.wav"; -#endif - - - if (ast_mutex_trylock(&p->record_lock) == 0) { - - cid_record_time = ast_tvnow(); - ast_verb(4, "CID record - start\n"); - - /* Skip any trailing ringtone */ - if (UsePolarityCID != 1){ - vpb_sleep(RING_SKIP); - } - - ast_verb(4, "CID record - skipped %dms trailing ring\n", - ast_tvdiff_ms(ast_tvnow(), cid_record_time)); - cid_record_time = ast_tvnow(); - - /* Record bit between the rings which contains the callerid */ - vpb_record_buf_start(p->handle, VPB_LINEAR); - rc = vpb_record_buf_sync(p->handle, (char*)buf, sizeof(buf)); - vpb_record_buf_finish(p->handle); -#ifdef ANALYSE_CID - vpb_wave_open_write(&ws, file, VPB_LINEAR); - vpb_wave_write(ws, (char *)buf, sizeof(buf)); - vpb_wave_close_write(ws); -#endif - - ast_verb(4, "CID record - recorded %dms between rings\n", - ast_tvdiff_ms(ast_tvnow(), cid_record_time)); - - ast_mutex_unlock(&p->record_lock); - - if (rc != VPB_OK) { - ast_log(LOG_ERROR, "Failed to record caller id sample on %s\n", p->dev); - return; - } - - VPB_CID *cli_struct = new VPB_CID; - cli_struct->ra_cldn[0] = 0; - cli_struct->ra_cn[0] = 0; - /* This decodes FSK 1200baud type callerid */ - if ((rc = vpb_cid_decode2(cli_struct, buf, CID_MSECS * 8)) == VPB_OK ) { - /* - if (owner->cid.cid_num) - ast_free(owner->cid.cid_num); - owner->cid.cid_num=NULL; - if (owner->cid.cid_name) - ast_free(owner->cid.cid_name); - owner->cid.cid_name=NULL; - */ - - if (cli_struct->ra_cldn[0] == '\0') { - /* - owner->cid.cid_num = ast_strdup(cli_struct->cldn); - owner->cid.cid_name = ast_strdup(cli_struct->cn); - */ - if (owner) { - ast_set_callerid(owner, cli_struct->cldn, cli_struct->cn, cli_struct->cldn); - } else { - strcpy(p->cid_num, cli_struct->cldn); - strcpy(p->cid_name, cli_struct->cn); - } - ast_verb(4, "CID record - got [%s] [%s]\n", owner->cid.cid_num, owner->cid.cid_name); - snprintf(p->callerid, sizeof(p->callerid), "%s %s", cli_struct->cldn, cli_struct->cn); - } else { - ast_log(LOG_ERROR, "CID record - No caller id avalable on %s \n", p->dev); - } - - } else { - ast_log(LOG_ERROR, "CID record - Failed to decode caller id on %s - %d\n", p->dev, rc); - ast_copy_string(p->callerid, "unknown", sizeof(p->callerid)); - } - delete cli_struct; - - } else - ast_log(LOG_ERROR, "CID record - Failed to set record mode for caller id on %s\n", p->dev); -} - -static void get_callerid_ast(struct vpb_pvt *p) -{ - struct callerid_state *cs; - char buf[1024]; - char *name = NULL, *number = NULL; - int flags; - int rc = 0, vrc; - int sam_count = 0; - struct ast_channel *owner = p->owner; - int which_cid; -/* - float old_gain; -*/ -#ifdef ANALYSE_CID - void * ws; - char * file = "cidsams.wav"; -#endif - - if (p->callerid_type == 1) { - ast_verb(4, "Collected caller ID already\n"); - return; - } - else if (p->callerid_type == 2 ) { - which_cid = CID_SIG_V23; - ast_verb(4, "Collecting Caller ID v23...\n"); - } - else if (p->callerid_type == 3) { - which_cid = CID_SIG_BELL; - ast_verb(4, "Collecting Caller ID bell...\n"); - } else { - ast_verb(4, "Caller ID disabled\n"); - return; - } -/* vpb_sleep(RING_SKIP); */ -/* vpb_record_get_gain(p->handle, &old_gain); */ - cs = callerid_new(which_cid); - if (cs) { -#ifdef ANALYSE_CID - vpb_wave_open_write(&ws, file, VPB_MULAW); - vpb_record_set_gain(p->handle, 3.0); - vpb_record_set_hw_gain(p->handle, 12.0); -#endif - vpb_record_buf_start(p->handle, VPB_MULAW); - while ((rc == 0) && (sam_count < 8000 * 3)) { - vrc = vpb_record_buf_sync(p->handle, (char*)buf, sizeof(buf)); - if (vrc != VPB_OK) - ast_log(LOG_ERROR, "%s: Caller ID couldn't read audio buffer!\n", p->dev); - rc = callerid_feed(cs, (unsigned char *)buf, sizeof(buf), AST_FORMAT_ULAW); -#ifdef ANALYSE_CID - vpb_wave_write(ws, (char *)buf, sizeof(buf)); -#endif - sam_count += sizeof(buf); - ast_verb(4, "Collecting Caller ID samples [%d][%d]...\n", sam_count, rc); - } - vpb_record_buf_finish(p->handle); -#ifdef ANALYSE_CID - vpb_wave_close_write(ws); -#endif - if (rc == 1) { - callerid_get(cs, &name, &number, &flags); - ast_debug(1, "%s: Caller ID name [%s] number [%s] flags [%d]\n", p->dev, name, number, flags); - } else { - ast_log(LOG_ERROR, "%s: Failed to decode Caller ID \n", p->dev); - } -/* vpb_record_set_gain(p->handle, old_gain); */ -/* vpb_record_set_hw_gain(p->handle,6.0); */ - } else { - ast_log(LOG_ERROR, "%s: Failed to create Caller ID struct\n", p->dev); - } - if (owner->cid.cid_num) { - ast_free(owner->cid.cid_num); - owner->cid.cid_num = NULL; - } - if (owner->cid.cid_name) { - ast_free(owner->cid.cid_name); - owner->cid.cid_name = NULL; - } - if (number) - ast_shrink_phone_number(number); - ast_set_callerid(owner, - number, name, - owner->cid.cid_ani ? NULL : number); - if (!ast_strlen_zero(name)){ - snprintf(p->callerid, sizeof(p->callerid), "%s %s", number, name); - } else { - ast_copy_string(p->callerid, number, sizeof(p->callerid)); - } - if (cs) - callerid_free(cs); -} - -/* Terminate any tones we are presently playing */ -static void stoptone(int handle) -{ - int ret; - VPB_EVENT je; - while (vpb_playtone_state(handle) != VPB_OK) { - vpb_tone_terminate(handle); - ret = vpb_get_event_ch_async(handle, &je); - if ((ret == VPB_OK) && (je.type != VPB_DIALEND)) { - ast_verb(4, "Stop tone collected a wrong event!![%d]\n", je.type); -/* vpb_put_event(&je); */ - } - vpb_sleep(10); - } -} - -/* Safe vpb_playtone_async */ -static int playtone( int handle, VPB_TONE *tone) -{ - int ret = VPB_OK; - stoptone(handle); - ast_verb(4, "[%02d]: Playing tone\n", handle); - ret = vpb_playtone_async(handle, tone); - return ret; -} - -static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e) -{ - struct ast_frame f = {AST_FRAME_CONTROL}; /* default is control, Clear rest. */ - int endbridge = 0; - int res = 0; - - ast_verb(4, "%s: handle_owned: got event: [%d=>%d]\n", p->dev, e->type, e->data); - - f.src = "vpb"; - switch (e->type) { - case VPB_RING: - if (p->mode == MODE_FXO) { - f.subclass = AST_CONTROL_RING; - vpb_timer_stop(p->ring_timer); - vpb_timer_start(p->ring_timer); - } else - f.frametype = AST_FRAME_NULL; /* ignore ring on station port. */ - break; - - case VPB_RING_OFF: - f.frametype = AST_FRAME_NULL; - break; - - case VPB_TIMEREXP: - if (e->data == p->busy_timer_id) { - playtone(p->handle, &Busytone); - p->state = VPB_STATE_PLAYBUSY; - vpb_timer_stop(p->busy_timer); - vpb_timer_start(p->busy_timer); - f.frametype = AST_FRAME_NULL; - } else if (e->data == p->ringback_timer_id) { - playtone(p->handle, &Ringbacktone); - vpb_timer_stop(p->ringback_timer); - vpb_timer_start(p->ringback_timer); - f.frametype = AST_FRAME_NULL; - } else if (e->data == p->ring_timer_id) { - /* We didnt get another ring in time! */ - if (p->owner->_state != AST_STATE_UP) { - /* Assume caller has hung up */ - vpb_timer_stop(p->ring_timer); - f.subclass = AST_CONTROL_HANGUP; - } else { - vpb_timer_stop(p->ring_timer); - f.frametype = AST_FRAME_NULL; - } - - } else { - f.frametype = AST_FRAME_NULL; /* Ignore. */ - } - break; - - case VPB_DTMF_DOWN: - case VPB_DTMF: - if (use_ast_dtmfdet) { - f.frametype = AST_FRAME_NULL; - } else if (p->owner->_state == AST_STATE_UP) { - f.frametype = AST_FRAME_DTMF; - f.subclass = e->data; - } else - f.frametype = AST_FRAME_NULL; - break; - - case VPB_TONEDETECT: - if (e->data == VPB_BUSY || e->data == VPB_BUSY_308 || e->data == VPB_BUSY_AUST ) { - ast_debug(4, "%s: handle_owned: got event: BUSY\n", p->dev); - if (p->owner->_state == AST_STATE_UP) { - f.subclass = AST_CONTROL_HANGUP; - } else { - f.subclass = AST_CONTROL_BUSY; - } - } else if (e->data == VPB_FAX) { - if (!p->faxhandled) { - if (strcmp(p->owner->exten, "fax")) { - const char *target_context = S_OR(p->owner->macrocontext, p->owner->context); - - if (ast_exists_extension(p->owner, target_context, "fax", 1, p->owner->cid.cid_num)) { - ast_verb(3, "Redirecting %s to fax extension\n", p->owner->name); - /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ - pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", p->owner->exten); - if (ast_async_goto(p->owner, target_context, "fax", 1)) { - ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", p->owner->name, target_context); - } - } else { - ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n"); - } - } else { - ast_debug(1, "Already in a fax extension, not redirecting\n"); - } - } else { - ast_debug(1, "Fax already handled\n"); - } - } else if (e->data == VPB_GRUNT) { - if (ast_tvdiff_ms(ast_tvnow(), p->lastgrunt) > gruntdetect_timeout) { - /* Nothing heard on line for a very long time - * Timeout connection */ - ast_verb(3, "grunt timeout\n"); - ast_log(LOG_NOTICE, "%s: Line hangup due of lack of conversation\n", p->dev); - f.subclass = AST_CONTROL_HANGUP; - } else { - p->lastgrunt = ast_tvnow(); - f.frametype = AST_FRAME_NULL; - } - } else { - f.frametype = AST_FRAME_NULL; - } - break; - - case VPB_CALLEND: - #ifdef DIAL_WITH_CALL_PROGRESS - if (e->data == VPB_CALL_CONNECTED) { - f.subclass = AST_CONTROL_ANSWER; - } else if (e->data == VPB_CALL_NO_DIAL_TONE || e->data == VPB_CALL_NO_RING_BACK) { - f.subclass = AST_CONTROL_CONGESTION; - } else if (e->data == VPB_CALL_NO_ANSWER || e->data == VPB_CALL_BUSY) { - f.subclass = AST_CONTROL_BUSY; - } else if (e->data == VPB_CALL_DISCONNECTED) { - f.subclass = AST_CONTROL_HANGUP; - } - #else - ast_log(LOG_NOTICE, "%s: Got call progress callback but blind dialing \n", p->dev); - f.frametype = AST_FRAME_NULL; - #endif - break; - - case VPB_STATION_OFFHOOK: - f.subclass = AST_CONTROL_ANSWER; - break; - - case VPB_DROP: - if ((p->mode == MODE_FXO) && (UseLoopDrop)) { /* ignore loop drop on stations */ - if (p->owner->_state == AST_STATE_UP) { - f.subclass = AST_CONTROL_HANGUP; - } else { - f.frametype = AST_FRAME_NULL; - } - } - break; - case VPB_LOOP_ONHOOK: - if (p->owner->_state == AST_STATE_UP) { - f.subclass = AST_CONTROL_HANGUP; - } else { - f.frametype = AST_FRAME_NULL; - } - break; - case VPB_STATION_ONHOOK: - f.subclass = AST_CONTROL_HANGUP; - break; - - case VPB_STATION_FLASH: - f.subclass = AST_CONTROL_FLASH; - break; - - /* Called when dialing has finished and ringing starts - * No indication that call has really been answered when using blind dialing - */ - case VPB_DIALEND: - if (p->state < 5) { - f.subclass = AST_CONTROL_ANSWER; - ast_verb(2, "%s: Dialend\n", p->dev); - } else { - f.frametype = AST_FRAME_NULL; - } - break; - -/* case VPB_PLAY_UNDERFLOW: - f.frametype = AST_FRAME_NULL; - vpb_reset_play_fifo_alarm(p->handle); - break; - - case VPB_RECORD_OVERFLOW: - f.frametype = AST_FRAME_NULL; - vpb_reset_record_fifo_alarm(p->handle); - break; -*/ - default: - f.frametype = AST_FRAME_NULL; - break; - } - -/* - ast_verb(4, "%s: LOCKING in handle_owned [%d]\n", p->dev,res); - res = ast_mutex_lock(&p->lock); - ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner); -*/ - if (p->bridge) { /* Check what happened, see if we need to report it. */ - switch (f.frametype) { - case AST_FRAME_DTMF: - if ( !(p->bridge->c0 == p->owner && - (p->bridge->flags & AST_BRIDGE_DTMF_CHANNEL_0) ) && - !(p->bridge->c1 == p->owner && - (p->bridge->flags & AST_BRIDGE_DTMF_CHANNEL_1) )) { - /* Kill bridge, this is interesting. */ - endbridge = 1; - } - break; - - case AST_FRAME_CONTROL: - if (!(p->bridge->flags & AST_BRIDGE_IGNORE_SIGS)) { - #if 0 - if (f.subclass == AST_CONTROL_BUSY || - f.subclass == AST_CONTROL_CONGESTION || - f.subclass == AST_CONTROL_HANGUP || - f.subclass == AST_CONTROL_FLASH) - #endif - endbridge = 1; - } - break; - - default: - break; - } - - if (endbridge) { - if (p->bridge->fo) { - *p->bridge->fo = ast_frisolate(&f); - } - - if (p->bridge->rc) { - *p->bridge->rc = p->owner; - } - - ast_mutex_lock(&p->bridge->lock); - p->bridge->endbridge = 1; - ast_cond_signal(&p->bridge->cond); - ast_mutex_unlock(&p->bridge->lock); - } - } - - if (endbridge) { - res = ast_mutex_unlock(&p->lock); -/* - ast_verb(4, "%s: unLOCKING in handle_owned [%d]\n", p->dev,res); -*/ - return 0; - } - - ast_verb(4, "%s: handle_owned: Prepared frame type[%d]subclass[%d], bridge=%p owner=[%s]\n", - p->dev, f.frametype, f.subclass, (void *)p->bridge, p->owner->name); - - /* Trylock used here to avoid deadlock that can occur if we - * happen to be in here handling an event when hangup is called - * Problem is that hangup holds p->owner->lock - */ - if ((f.frametype >= 0) && (f.frametype != AST_FRAME_NULL) && (p->owner)) { - if (ast_channel_trylock(p->owner) == 0) { - ast_queue_frame(p->owner, &f); - ast_channel_unlock(p->owner); - ast_verb(4, "%s: handled_owned: Queued Frame to [%s]\n", p->dev, p->owner->name); - } else { - ast_verbose("%s: handled_owned: Missed event %d/%d \n", - p->dev, f.frametype, f.subclass); - } - } - res = ast_mutex_unlock(&p->lock); -/* - ast_verb(4, "%s: unLOCKING in handle_owned [%d]\n", p->dev,res); -*/ - - return 0; -} - -static inline int monitor_handle_notowned(struct vpb_pvt *p, VPB_EVENT *e) -{ - char s[2] = {0}; - struct ast_channel *owner = p->owner; - char cid_num[256]; - char cid_name[256]; -/* - struct ast_channel *c; -*/ - - char str[VPB_MAX_STR]; - - vpb_translate_event(e, str); - ast_verb(4, "%s: handle_notowned: mode=%d, event[%d][%s]=[%d]\n", p->dev, p->mode, e->type,str, e->data); - - switch (e->type) { - case VPB_LOOP_ONHOOK: - case VPB_LOOP_POLARITY: - if (UsePolarityCID == 1) { - ast_verb(4, "Polarity reversal\n"); - if (p->callerid_type == 1) { - ast_verb(4, "Using VPB Caller ID\n"); - get_callerid(p); /* UK CID before 1st ring*/ - } -/* get_callerid_ast(p); */ /* Caller ID using the ast functions */ - } - break; - case VPB_RING: - if (p->mode == MODE_FXO) /* FXO port ring, start * */ { - vpb_new(p, AST_STATE_RING, p->context); - if (UsePolarityCID != 1) { - if (p->callerid_type == 1) { - ast_verb(4, "Using VPB Caller ID\n"); - get_callerid(p); /* Australian CID only between 1st and 2nd ring */ - } - get_callerid_ast(p); /* Caller ID using the ast functions */ - } else { - ast_log(LOG_ERROR, "Setting caller ID: %s %s\n", p->cid_num, p->cid_name); - ast_set_callerid(p->owner, p->cid_num, p->cid_name, p->cid_num); - p->cid_num[0] = 0; - p->cid_name[0] = 0; - } - - vpb_timer_stop(p->ring_timer); - vpb_timer_start(p->ring_timer); - } - break; - - case VPB_RING_OFF: - break; - - case VPB_STATION_OFFHOOK: - if (p->mode == MODE_IMMEDIATE) { - vpb_new(p,AST_STATE_RING, p->context); - } else { - ast_verb(4, "%s: handle_notowned: playing dialtone\n", p->dev); - playtone(p->handle, &Dialtone); - p->state = VPB_STATE_PLAYDIAL; - p->wantdtmf = 1; - p->ext[0] = 0; /* Just to be sure & paranoid.*/ - } - break; - - case VPB_DIALEND: - if (p->mode == MODE_DIALTONE) { - if (p->state == VPB_STATE_PLAYDIAL) { - playtone(p->handle, &Dialtone); - p->wantdtmf = 1; - p->ext[0] = 0; /* Just to be sure & paranoid. */ - } -#if 0 - /* These are not needed as they have timers to restart them */ - else if (p->state == VPB_STATE_PLAYBUSY) { - playtone(p->handle, &Busytone); - p->wantdtmf = 1; - p->ext[0] = 0; - } else if (p->state == VPB_STATE_PLAYRING) { - playtone(p->handle, &Ringbacktone); - p->wantdtmf = 1; - p->ext[0] = 0; - } -#endif - } else { - ast_verb(4, "%s: handle_notowned: Got a DIALEND when not really expected\n",p->dev); - } - break; - - case VPB_STATION_ONHOOK: /* clear ext */ - stoptone(p->handle); - p->wantdtmf = 1 ; - p->ext[0] = 0; - p->state = VPB_STATE_ONHOOK; - break; - case VPB_TIMEREXP: - if (e->data == p->dtmfidd_timer_id) { - if (ast_exists_extension(NULL, p->context, p->ext, 1, p->callerid)){ - ast_verb(4, "%s: handle_notowned: DTMF IDD timer out, matching on [%s] in [%s]\n", p->dev, p->ext, p->context); - - vpb_new(p, AST_STATE_RING, p->context); - } - } else if (e->data == p->ring_timer_id) { - /* We didnt get another ring in time! */ - if (p->owner) { - if (p->owner->_state != AST_STATE_UP) { - /* Assume caller has hung up */ - vpb_timer_stop(p->ring_timer); - } - } else { - /* No owner any more, Assume caller has hung up */ - vpb_timer_stop(p->ring_timer); - } - } - break; - - case VPB_DTMF: - if (p->state == VPB_STATE_ONHOOK){ - /* DTMF's being passed while on-hook maybe Caller ID */ - if (p->mode == MODE_FXO) { - if (e->data == DTMF_CID_START) { /* CallerID Start signal */ - p->dtmf_caller_pos = 0; /* Leaves the first digit out */ - memset(p->callerid, 0, sizeof(p->callerid)); - } else if (e->data == DTMF_CID_STOP) { /* CallerID End signal */ - p->callerid[p->dtmf_caller_pos] = '\0'; - ast_verb(3, " %s: DTMF CallerID %s\n", p->dev, p->callerid); - if (owner) { - /* - if (owner->cid.cid_num) - ast_free(owner->cid.cid_num); - owner->cid.cid_num=NULL; - if (owner->cid.cid_name) - ast_free(owner->cid.cid_name); - owner->cid.cid_name=NULL; - owner->cid.cid_num = strdup(p->callerid); - */ - cid_name[0] = '\0'; - cid_num[0] = '\0'; - ast_callerid_split(p->callerid, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num)); - ast_set_callerid(owner, cid_num, cid_name, cid_num); - - } else { - ast_verb(3, " %s: DTMF CallerID: no owner to assign CID \n", p->dev); - } - } else if (p->dtmf_caller_pos < AST_MAX_EXTENSION) { - if (p->dtmf_caller_pos >= 0) { - p->callerid[p->dtmf_caller_pos] = e->data; - } - p->dtmf_caller_pos++; - } - } - break; - } - if (p->wantdtmf == 1) { - stoptone(p->handle); - p->wantdtmf = 0; - } - p->state = VPB_STATE_GETDTMF; - s[0] = e->data; - strncat(p->ext, s, sizeof(p->ext) - strlen(p->ext) - 1); - #if 0 - if (!strcmp(p->ext, ast_pickup_ext())) { - /* Call pickup has been dialled! */ - if (ast_pickup_call(c)) { - /* Call pickup wasnt possible */ - } - } else - #endif - if (ast_exists_extension(NULL, p->context, p->ext, 1, p->callerid)) { - if (ast_canmatch_extension(NULL, p->context, p->ext, 1, p->callerid)) { - ast_verb(4, "%s: handle_notowned: Multiple matches on [%s] in [%s]\n", p->dev, p->ext, p->context); - /* Start DTMF IDD timer */ - vpb_timer_stop(p->dtmfidd_timer); - vpb_timer_start(p->dtmfidd_timer); - } else { - ast_verb(4, "%s: handle_notowned: Matched on [%s] in [%s]\n", p->dev, p->ext , p->context); - vpb_new(p, AST_STATE_UP, p->context); - } - } else if (!ast_canmatch_extension(NULL, p->context, p->ext, 1, p->callerid)) { - if (ast_exists_extension(NULL, "default", p->ext, 1, p->callerid)) { - vpb_new(p, AST_STATE_UP, "default"); - } else if (!ast_canmatch_extension(NULL, "default", p->ext, 1, p->callerid)) { - ast_verb(4, "%s: handle_notowned: can't match anything in %s or default\n", p->dev, p->context); - playtone(p->handle, &Busytone); - vpb_timer_stop(p->busy_timer); - vpb_timer_start(p->busy_timer); - p->state = VPB_STATE_PLAYBUSY; - } - } - break; - - default: - /* Ignore.*/ - break; - } - - ast_verb(4, "%s: handle_notowned: mode=%d, [%d=>%d]\n", p->dev, p->mode, e->type, e->data); - - return 0; -} - -static void *do_monitor(void *unused) -{ - - /* Monitor thread, doesn't die until explicitly killed. */ - - ast_verb(2, "Starting vpb monitor thread[%ld]\n", pthread_self()); - - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - - for (;;) { - VPB_EVENT e; - VPB_EVENT je; - char str[VPB_MAX_STR]; - struct vpb_pvt *p; - - /* - ast_verb(4, "Monitor waiting for event\n"); - */ - - int res = vpb_get_event_sync(&e, VPB_WAIT_TIMEOUT); - if ((res == VPB_NO_EVENTS) || (res == VPB_TIME_OUT)) { - /* - if (res == VPB_NO_EVENTS) { - ast_verb(4, "No events....\n"); - } else { - ast_verb(4, "No events, timed out....\n"); - } - */ - continue; - } - - if (res != VPB_OK) { - ast_log(LOG_ERROR,"Monitor get event error %d\n", res ); - ast_verbose("Monitor get event error %d\n", res ); - continue; - } - - str[0] = 0; - - p = NULL; - - ast_mutex_lock(&monlock); - if (e.type == VPB_NULL_EVENT) { - ast_verb(4, "Monitor got null event\n"); - } else { - vpb_translate_event(&e, str); - if (*str && *(str + 1)) { - str[strlen(str) - 1] = '\0'; - } - - ast_mutex_lock(&iflock); - for (p = iflist; p && p->handle != e.handle; p = p->next); - ast_mutex_unlock(&iflock); - - if (p) { - ast_verb(4, "%s: Event [%d=>%s]\n", - p ? p->dev : "null", e.type, str); - } - } - - ast_mutex_unlock(&monlock); - - if (!p) { - if (e.type != VPB_NULL_EVENT) { - ast_log(LOG_WARNING, "Got event [%s][%d], no matching iface!\n", str, e.type); - ast_verb(4, "vpb/ERR: No interface for Event [%d=>%s] \n", e.type, str); - } - continue; - } - - /* flush the event from the channel event Q */ - vpb_get_event_ch_async(e.handle, &je); - vpb_translate_event(&je, str); - ast_verb(5, "%s: Flushing event [%d]=>%s\n", p->dev, je.type, str); - - /* Check for ownership and locks */ - if ((p->owner) && (!p->golock)) { - /* Need to get owner lock */ - /* Safely grab both p->lock and p->owner->lock so that there - cannot be a race with something from the other side */ - /* - ast_mutex_lock(&p->lock); - while (ast_mutex_trylock(&p->owner->lock)) { - ast_mutex_unlock(&p->lock); - usleep(1); - ast_mutex_lock(&p->lock); - if (!p->owner) - break; - } - if (p->owner) - p->golock = 1; - */ - } - /* Two scenarios: Are you owned or not. */ - if (p->owner) { - monitor_handle_owned(p, &e); - } else { - monitor_handle_notowned(p, &e); - } - /* if ((!p->owner)&&(p->golock)) { - ast_mutex_unlock(&p->owner->lock); - ast_mutex_unlock(&p->lock); - } - */ - - } - - return NULL; -} - -static int restart_monitor(void) -{ - int error = 0; - - /* If we're supposed to be stopped -- stay stopped */ - if (mthreadactive == -2) - return 0; - - ast_verb(4, "Restarting monitor\n"); - - ast_mutex_lock(&monlock); - if (monitor_thread == pthread_self()) { - ast_log(LOG_WARNING, "Cannot kill myself\n"); - error = -1; - ast_verb(4, "Monitor trying to kill monitor\n"); - } else { - if (mthreadactive != -1) { - /* Why do other drivers kill the thread? No need says I, simply awake thread with event. */ - VPB_EVENT e; - e.handle = 0; - e.type = VPB_EVT_NONE; - e.data = 0; - - ast_verb(4, "Trying to reawake monitor\n"); - - vpb_put_event(&e); - } else { - /* Start a new monitor */ - int pid = ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL); - ast_verb(4, "Created new monitor thread %d\n", pid); - if (pid < 0) { - ast_log(LOG_ERROR, "Unable to start monitor thread.\n"); - error = -1; - } else { - mthreadactive = 0; /* Started the thread!*/ - } - } - } - ast_mutex_unlock(&monlock); - - ast_verb(4, "Monitor restarted\n"); - - return error; -} - -/* Per board config that must be called after vpb_open() */ -static void mkbrd(vpb_model_t model, int echo_cancel) -{ - if (!bridges) { - if (model == vpb_model_v4pci) { - max_bridges = MAX_BRIDGES_V4PCI; - } - bridges = (vpb_bridge_t *)ast_calloc(1, max_bridges * sizeof(vpb_bridge_t)); - if (!bridges) { - ast_log(LOG_ERROR, "Failed to initialize bridges\n"); - } else { - int i; - for (i = 0; i < max_bridges; i++) { - ast_mutex_init(&bridges[i].lock); - ast_cond_init(&bridges[i].cond, NULL); - } - } - } - if (!echo_cancel) { - if (model == vpb_model_v4pci) { - vpb_echo_canc_disable(); - ast_log(LOG_NOTICE, "Voicetronix echo cancellation OFF\n"); - } else { - /* need to do it port by port for OpenSwitch */ - } - } else { - if (model == vpb_model_v4pci) { - vpb_echo_canc_enable(); - ast_log(LOG_NOTICE, "Voicetronix echo cancellation ON\n"); - if (ec_supp_threshold > -1) { - vpb_echo_canc_set_sup_thresh(0, &ec_supp_threshold); - ast_log(LOG_NOTICE, "Voicetronix EC Sup Thres set\n"); - } - } else { - /* need to do it port by port for OpenSwitch */ - } - } -} - -static struct vpb_pvt *mkif(int board, int channel, int mode, int gains, float txgain, float rxgain, - float txswgain, float rxswgain, int bal1, int bal2, int bal3, - char * callerid, int echo_cancel, int group, ast_group_t callgroup, ast_group_t pickupgroup ) -{ - struct vpb_pvt *tmp; - char buf[64]; - - tmp = (vpb_pvt *)ast_calloc(1, sizeof(*tmp)); - - if (!tmp) - return NULL; - - tmp->handle = vpb_open(board, channel); - - if (tmp->handle < 0) { - ast_log(LOG_WARNING, "Unable to create channel vpb/%d-%d: %s\n", - board, channel, strerror(errno)); - ast_free(tmp); - return NULL; - } - - snprintf(tmp->dev, sizeof(tmp->dev), "vpb/%d-%d", board, channel); - - tmp->mode = mode; - - tmp->group = group; - tmp->callgroup = callgroup; - tmp->pickupgroup = pickupgroup; - - /* Initilize dtmf caller ID position variable */ - tmp->dtmf_caller_pos = 0; - - ast_copy_string(tmp->language, language, sizeof(tmp->language)); - ast_copy_string(tmp->context, context, sizeof(tmp->context)); - - tmp->callerid_type = 0; - if (callerid) { - if (strcasecmp(callerid, "on") == 0) { - tmp->callerid_type = 1; - ast_copy_string(tmp->callerid, "unknown", sizeof(tmp->callerid)); - } else if (strcasecmp(callerid, "v23") == 0) { - tmp->callerid_type = 2; - ast_copy_string(tmp->callerid, "unknown", sizeof(tmp->callerid)); - } else if (strcasecmp(callerid, "bell") == 0) { - tmp->callerid_type = 3; - ast_copy_string(tmp->callerid, "unknown", sizeof(tmp->callerid)); - } else { - ast_copy_string(tmp->callerid, callerid, sizeof(tmp->callerid)); - } - } else { - ast_copy_string(tmp->callerid, "unknown", sizeof(tmp->callerid)); - } - - /* check if codec balances have been set in the config file */ - if (bal3 >= 0) { - if ((bal1>=0) && !(bal1 & 32)) bal1 |= 32; - vpb_set_codec_reg(tmp->handle, 0x42, bal3); - } - if (bal1 >= 0) { - vpb_set_codec_reg(tmp->handle, 0x32, bal1); - } - if (bal2 >= 0) { - vpb_set_codec_reg(tmp->handle, 0x3a, bal2); - } - - if (gains & VPB_GOT_TXHWG) { - if (txgain > MAX_VPB_GAIN) { - tmp->txgain = MAX_VPB_GAIN; - } else if (txgain < MIN_VPB_GAIN) { - tmp->txgain = MIN_VPB_GAIN; - } else { - tmp->txgain = txgain; - } - - ast_log(LOG_NOTICE, "VPB setting Tx Hw gain to [%f]\n", tmp->txgain); - vpb_play_set_hw_gain(tmp->handle, tmp->txgain); - } - - if (gains & VPB_GOT_RXHWG) { - if (rxgain > MAX_VPB_GAIN) { - tmp->rxgain = MAX_VPB_GAIN; - } else if (rxgain < MIN_VPB_GAIN) { - tmp->rxgain = MIN_VPB_GAIN; - } else { - tmp->rxgain = rxgain; - } - ast_log(LOG_NOTICE, "VPB setting Rx Hw gain to [%f]\n", tmp->rxgain); - vpb_record_set_hw_gain(tmp->handle, tmp->rxgain); - } - - if (gains & VPB_GOT_TXSWG) { - tmp->txswgain = txswgain; - ast_log(LOG_NOTICE, "VPB setting Tx Sw gain to [%f]\n", tmp->txswgain); - vpb_play_set_gain(tmp->handle, tmp->txswgain); - } - - if (gains & VPB_GOT_RXSWG) { - tmp->rxswgain = rxswgain; - ast_log(LOG_NOTICE, "VPB setting Rx Sw gain to [%f]\n", tmp->rxswgain); - vpb_record_set_gain(tmp->handle, tmp->rxswgain); - } - - tmp->vpb_model = vpb_model_unknown; - if (vpb_get_model(tmp->handle, buf) == VPB_OK) { - if (strcmp(buf, "V12PCI") == 0) { - tmp->vpb_model = vpb_model_v12pci; - } else if (strcmp(buf, "VPB4") == 0) { - tmp->vpb_model = vpb_model_v4pci; - } - } - - ast_mutex_init(&tmp->owner_lock); - ast_mutex_init(&tmp->lock); - ast_mutex_init(&tmp->record_lock); - ast_mutex_init(&tmp->play_lock); - ast_mutex_init(&tmp->play_dtmf_lock); - - /* set default read state */ - tmp->read_state = 0; - - tmp->golock = 0; - - tmp->busy_timer_id = vpb_timer_get_unique_timer_id(); - vpb_timer_open(&tmp->busy_timer, tmp->handle, tmp->busy_timer_id, TIMER_PERIOD_BUSY); - - tmp->ringback_timer_id = vpb_timer_get_unique_timer_id(); - vpb_timer_open(&tmp->ringback_timer, tmp->handle, tmp->ringback_timer_id, TIMER_PERIOD_RINGBACK); - - tmp->ring_timer_id = vpb_timer_get_unique_timer_id(); - vpb_timer_open(&tmp->ring_timer, tmp->handle, tmp->ring_timer_id, timer_period_ring); - - tmp->dtmfidd_timer_id = vpb_timer_get_unique_timer_id(); - vpb_timer_open(&tmp->dtmfidd_timer, tmp->handle, tmp->dtmfidd_timer_id, dtmf_idd); - - if (mode == MODE_FXO){ - if (use_ast_dtmfdet) - vpb_set_event_mask(tmp->handle, VPB_EVENTS_NODTMF); - else - vpb_set_event_mask(tmp->handle, VPB_EVENTS_ALL); - } else { -/* - if (use_ast_dtmfdet) - vpb_set_event_mask(tmp->handle, VPB_EVENTS_NODTMF); - else -*/ - vpb_set_event_mask(tmp->handle, VPB_EVENTS_STAT); - } - - if ((tmp->vpb_model == vpb_model_v12pci) && (echo_cancel)) { - vpb_hostecho_on(tmp->handle); - } - if (use_ast_dtmfdet) { - tmp->vad = ast_dsp_new(); - ast_dsp_set_features(tmp->vad, DSP_FEATURE_DTMF_DETECT); - ast_dsp_digitmode(tmp->vad, DSP_DIGITMODE_DTMF); - if (relaxdtmf) - ast_dsp_digitmode(tmp->vad, DSP_DIGITMODE_DTMF|DSP_DIGITMODE_RELAXDTMF); - } else { - tmp->vad = NULL; - } - - /* define grunt tone */ - vpb_settonedet(tmp->handle,&toned_ungrunt); - - ast_log(LOG_NOTICE,"Voicetronix %s channel %s initialized (rxsg=%f/txsg=%f/rxhg=%f/txhg=%f)(0x%x/0x%x/0x%x)\n", - (tmp->vpb_model == vpb_model_v4pci) ? "V4PCI" : - (tmp->vpb_model == vpb_model_v12pci) ? "V12PCI" : "[Unknown model]", - tmp->dev, tmp->rxswgain, tmp->txswgain, tmp->rxgain, tmp->txgain, bal1, bal2, bal3); - - return tmp; -} - -static int vpb_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen) -{ - struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt; - int res = 0; - int tmp = 0; - - if (use_ast_ind == 1) { - ast_verb(4, "%s: vpb_indicate called when using Ast Indications !?!\n", p->dev); - return 0; - } - - ast_verb(4, "%s: vpb_indicate [%d] state[%d]\n", p->dev, condition,ast->_state); -/* - if (ast->_state != AST_STATE_UP) { - ast_verb(4, "%s: vpb_indicate Not in AST_STATE_UP\n", p->dev, condition,ast->_state); - return res; - } -*/ - -/* - ast_verb(4, "%s: LOCKING in indicate \n", p->dev); - ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count, p->lock.__m_owner); -*/ - ast_mutex_lock(&p->lock); - switch (condition) { - case AST_CONTROL_BUSY: - case AST_CONTROL_CONGESTION: - if (ast->_state == AST_STATE_UP) { - playtone(p->handle, &Busytone); - p->state = VPB_STATE_PLAYBUSY; - vpb_timer_stop(p->busy_timer); - vpb_timer_start(p->busy_timer); - } - break; - case AST_CONTROL_RINGING: - if (ast->_state == AST_STATE_UP) { - playtone(p->handle, &Ringbacktone); - p->state = VPB_STATE_PLAYRING; - ast_verb(4, "%s: vpb indicate: setting ringback timer [%d]\n", p->dev,p->ringback_timer_id); - - vpb_timer_stop(p->ringback_timer); - vpb_timer_start(p->ringback_timer); - } - break; - case AST_CONTROL_ANSWER: - case -1: /* -1 means stop playing? */ - vpb_timer_stop(p->ringback_timer); - vpb_timer_stop(p->busy_timer); - stoptone(p->handle); - break; - case AST_CONTROL_HANGUP: - if (ast->_state == AST_STATE_UP) { - playtone(p->handle, &Busytone); - p->state = VPB_STATE_PLAYBUSY; - vpb_timer_stop(p->busy_timer); - vpb_timer_start(p->busy_timer); - } - break; - case AST_CONTROL_HOLD: - ast_moh_start(ast, (const char *) data, NULL); - break; - case AST_CONTROL_UNHOLD: - ast_moh_stop(ast); - break; - default: - res = 0; - break; - } - tmp = ast_mutex_unlock(&p->lock); -/* - ast_verb(4, "%s: unLOCKING in indicate [%d]\n", p->dev,tmp); -*/ - return res; -} - -static int vpb_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct vpb_pvt *p = (struct vpb_pvt *)newchan->tech_pvt; - int res = 0; - -/* - ast_verb(4, "%s: LOCKING in fixup \n", p->dev); - ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner); -*/ - ast_mutex_lock(&p->lock); - ast_debug(1, "New owner for channel %s is %s\n", p->dev, newchan->name); - - if (p->owner == oldchan) { - p->owner = newchan; - } - - if (newchan->_state == AST_STATE_RINGING){ - if (use_ast_ind == 1) { - ast_verb(4, "%s: vpb_fixup Calling ast_indicate\n", p->dev); - ast_indicate(newchan, AST_CONTROL_RINGING); - } else { - ast_verb(4, "%s: vpb_fixup Calling vpb_indicate\n", p->dev); - vpb_indicate(newchan, AST_CONTROL_RINGING, NULL, 0); - } - } - - res = ast_mutex_unlock(&p->lock); -/* - ast_verb(4, "%s: unLOCKING in fixup [%d]\n", p->dev,res); -*/ - return 0; -} - -static int vpb_digit_begin(struct ast_channel *ast, char digit) -{ - /* XXX Modify this callback to let Asterisk control the length of DTMF */ - return 0; -} -static int vpb_digit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ - struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt; - char s[2]; - int res = 0; - - if (use_ast_dtmf) { - ast_verb(4, "%s: vpb_digit: asked to play digit[%c] but we are using asterisk dtmf play back?!\n", p->dev, digit); - return 0; - } - -/* - ast_verb(4, "%s: LOCKING in digit \n", p->dev); - ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner); -*/ - ast_mutex_lock(&p->lock); - - - s[0] = digit; - s[1] = '\0'; - - ast_verb(4, "%s: vpb_digit: asked to play digit[%s]\n", p->dev, s); - - ast_mutex_lock(&p->play_dtmf_lock); - strncat(p->play_dtmf, s, sizeof(*p->play_dtmf) - strlen(p->play_dtmf) - 1); - ast_mutex_unlock(&p->play_dtmf_lock); - - res = ast_mutex_unlock(&p->lock); -/* - ast_verb(4, "%s: unLOCKING in digit [%d]\n", p->dev,res); -*/ - return 0; -} - -/* Places a call out of a VPB channel */ -static int vpb_call(struct ast_channel *ast, char *dest, int timeout) -{ - struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt; - int res = 0, i; - char *s = strrchr(dest, '/'); - char dialstring[254] = ""; - int tmp = 0; - -/* - ast_verb(4, "%s: LOCKING in call \n", p->dev); - ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner); -*/ - ast_mutex_lock(&p->lock); - ast_verb(4, "%s: starting call to [%s]\n", p->dev, dest); - - if (s) - s = s + 1; - else - s = dest; - ast_copy_string(dialstring, s, sizeof(dialstring)); - for (i = 0; dialstring[i] != '\0'; i++) { - if ((dialstring[i] == 'w') || (dialstring[i] == 'W')) - dialstring[i] = ','; - else if ((dialstring[i] == 'f') || (dialstring[i] == 'F')) - dialstring[i] = '&'; - } - - if (ast->_state != AST_STATE_DOWN && ast->_state != AST_STATE_RESERVED) { - ast_log(LOG_WARNING, "vpb_call on %s neither down nor reserved!\n", ast->name); - tmp = ast_mutex_unlock(&p->lock); -/* - ast_verb(4, "%s: unLOCKING in call [%d]\n", p->dev,tmp); -*/ - return -1; - } - if (p->mode != MODE_FXO) /* Station port, ring it. */ - vpb_ring_station_async(p->handle, 2); - else { - VPB_CALL call; - int j; - - /* Dial must timeout or it can leave channels unuseable */ - if (timeout == 0) { - timeout = TIMER_PERIOD_NOANSWER; - } else { - timeout = timeout * 1000; /* convert from secs to ms. */ - } - - /* These timeouts are only used with call progress dialing */ - call.dialtones = 1; /* Number of dialtones to get outside line */ - call.dialtone_timeout = VPB_DIALTONE_WAIT; /* Wait this long for dialtone (ms) */ - call.ringback_timeout = VPB_RINGWAIT; /* Wait this long for ringing after dialing (ms) */ - call.inter_ringback_timeout = VPB_CONNECTED_WAIT; /* If ringing stops for this long consider it connected (ms) */ - call.answer_timeout = timeout; /* Time to wait for answer after ringing starts (ms) */ - memcpy(&call.tone_map, DialToneMap, sizeof(DialToneMap)); - vpb_set_call(p->handle, &call); - - ast_verb(2, "%s: Calling %s on %s \n",p->dev, dialstring, ast->name); - - ast_verb(2, "%s: Dial parms for %s %d/%dms/%dms/%dms/%dms\n", p->dev, - ast->name, call.dialtones, call.dialtone_timeout, - call.ringback_timeout, call.inter_ringback_timeout, - call.answer_timeout); - for (j = 0; !call.tone_map[j].terminate; j++) { - ast_verb(2, "%s: Dial parms for %s tone %d->%d\n", p->dev, - ast->name, call.tone_map[j].tone_id, call.tone_map[j].call_id); - } - - ast_verb(4, "%s: Disabling Loop Drop detection\n", p->dev); - vpb_disable_event(p->handle, VPB_MDROP); - vpb_sethook_sync(p->handle, VPB_OFFHOOK); - p->state = VPB_STATE_OFFHOOK; - - #ifndef DIAL_WITH_CALL_PROGRESS - vpb_sleep(300); - ast_verb(4, "%s: Enabling Loop Drop detection\n", p->dev); - vpb_enable_event(p->handle, VPB_MDROP); - res = vpb_dial_async(p->handle, dialstring); - #else - ast_verb(4, "%s: Enabling Loop Drop detection\n", p->dev); - vpb_enable_event(p->handle, VPB_MDROP); - res = vpb_call_async(p->handle, dialstring); - #endif - - if (res != VPB_OK) { - ast_debug(1, "Call on %s to %s failed: %d\n", ast->name, s, res); - res = -1; - } else - res = 0; - } - - ast_verb(3, "%s: VPB Calling %s [t=%d] on %s returned %d\n", p->dev , s, timeout, ast->name, res); - if (res == 0) { - ast_setstate(ast, AST_STATE_RINGING); - ast_queue_control(ast, AST_CONTROL_RINGING); - } - - if (!p->readthread) { - ast_pthread_create(&p->readthread, NULL, do_chanreads, (void *)p); - } - - tmp = ast_mutex_unlock(&p->lock); -/* - ast_verb(4, "%s: unLOCKING in call [%d]\n", p->dev,tmp); -*/ - return res; -} - -static int vpb_hangup(struct ast_channel *ast) -{ - struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt; - VPB_EVENT je; - char str[VPB_MAX_STR]; - int res = 0; - -/* - ast_verb(4, "%s: LOCKING in hangup \n", p->dev); - ast_verb(4, "%s: LOCKING in hangup count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner); - ast_verb(4, "%s: LOCKING pthread_self(%d)\n", p->dev,pthread_self()); - ast_mutex_lock(&p->lock); -*/ - ast_verb(2, "%s: Hangup requested\n", ast->name); - - if (!ast->tech || !ast->tech_pvt) { - ast_log(LOG_WARNING, "%s: channel not connected?\n", ast->name); - res = ast_mutex_unlock(&p->lock); -/* - ast_verb(4, "%s: unLOCKING in hangup [%d]\n", p->dev,res); -*/ - /* Free up ast dsp if we have one */ - if (use_ast_dtmfdet && p->vad) { - ast_dsp_free(p->vad); - p->vad = NULL; - } - return 0; - } - - - - /* Stop record */ - p->stopreads = 1; - if (p->readthread) { - pthread_join(p->readthread, NULL); - ast_verb(4, "%s: stopped record thread \n", ast->name); - } - - /* Stop play */ - if (p->lastoutput != -1) { - ast_verb(2, "%s: Ending play mode \n", ast->name); - vpb_play_terminate(p->handle); - ast_mutex_lock(&p->play_lock); - vpb_play_buf_finish(p->handle); - ast_mutex_unlock(&p->play_lock); - } - - ast_verb(4, "%s: Setting state down\n", ast->name); - ast_setstate(ast, AST_STATE_DOWN); - - -/* - ast_verb(4, "%s: LOCKING in hangup \n", p->dev); - ast_verb(4, "%s: LOCKING in hangup count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner); - ast_verb(4, "%s: LOCKING pthread_self(%d)\n", p->dev,pthread_self()); -*/ - ast_mutex_lock(&p->lock); - - if (p->mode != MODE_FXO) { - /* station port. */ - vpb_ring_station_async(p->handle, 0); - if (p->state != VPB_STATE_ONHOOK) { - /* This is causing a "dial end" "play tone" loop - playtone(p->handle, &Busytone); - p->state = VPB_STATE_PLAYBUSY; - ast_verb(5, "%s: Station offhook[%d], playing busy tone\n", - ast->name,p->state); - */ - } else { - stoptone(p->handle); - } - #ifdef VPB_PRI - vpb_setloop_async(p->handle, VPB_OFFHOOK); - vpb_sleep(100); - vpb_setloop_async(p->handle, VPB_ONHOOK); - #endif - } else { - stoptone(p->handle); /* Terminates any dialing */ - vpb_sethook_sync(p->handle, VPB_ONHOOK); - p->state=VPB_STATE_ONHOOK; - } - while (VPB_OK == vpb_get_event_ch_async(p->handle, &je)) { - vpb_translate_event(&je, str); - ast_verb(4, "%s: Flushing event [%d]=>%s\n", ast->name, je.type, str); - } - - p->readthread = 0; - p->lastoutput = -1; - p->lastinput = -1; - p->last_ignore_dtmf = 1; - p->ext[0] = 0; - p->dialtone = 0; - - p->owner = NULL; - ast->tech_pvt = NULL; - - /* Free up ast dsp if we have one */ - if (use_ast_dtmfdet && p->vad) { - ast_dsp_free(p->vad); - p->vad = NULL; - } - - ast_verb(2, "%s: Hangup complete\n", ast->name); - - restart_monitor(); -/* - ast_verb(4, "%s: LOCKING in hangup count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner); -*/ - res = ast_mutex_unlock(&p->lock); -/* - ast_verb(4, "%s: unLOCKING in hangup [%d]\n", p->dev,res); - ast_verb(4, "%s: LOCKING in hangup count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner); -*/ - return 0; -} - -static int vpb_answer(struct ast_channel *ast) -{ - struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt; - int res = 0; -/* - VPB_EVENT je; - int ret; - ast_verb(4, "%s: LOCKING in answer \n", p->dev); - ast_verb(4, "%s: LOCKING count[%d] owner[%d] \n", p->dev, p->lock.__m_count,p->lock.__m_owner); -*/ - ast_mutex_lock(&p->lock); - - ast_verb(4, "%s: Answering channel\n", p->dev); - - if (p->mode == MODE_FXO) { - ast_verb(4, "%s: Disabling Loop Drop detection\n", p->dev); - vpb_disable_event(p->handle, VPB_MDROP); - } - - if (ast->_state != AST_STATE_UP) { - if (p->mode == MODE_FXO) { - vpb_sethook_sync(p->handle, VPB_OFFHOOK); - p->state = VPB_STATE_OFFHOOK; -/* vpb_sleep(500); - ret = vpb_get_event_ch_async(p->handle, &je); - if ((ret == VPB_OK) && ((je.type != VPB_DROP)&&(je.type != VPB_RING))){ - ast_verb(4, "%s: Answer collected a wrong event!!\n", p->dev); - vpb_put_event(&je); - } -*/ - } - ast_setstate(ast, AST_STATE_UP); - - ast_verb(2, "%s: Answered call on %s [%s]\n", p->dev, - ast->name, (p->mode == MODE_FXO) ? "FXO" : "FXS"); - - ast->rings = 0; - if (!p->readthread) { - /* res = ast_mutex_unlock(&p->lock); */ - /* ast_verbose("%s: unLOCKING in answer [%d]\n", p->dev,res); */ - ast_pthread_create(&p->readthread, NULL, do_chanreads, (void *)p); - } else { - ast_verb(4, "%s: Record thread already running!!\n", p->dev); - } - } else { - ast_verb(4, "%s: Answered state is up\n", p->dev); - /* res = ast_mutex_unlock(&p->lock); */ - /* ast_verbose("%s: unLOCKING in answer [%d]\n", p->dev,res); */ - } - vpb_sleep(500); - if (p->mode == MODE_FXO) { - ast_verb(4, "%s: Re-enabling Loop Drop detection\n", p->dev); - vpb_enable_event(p->handle, VPB_MDROP); - } - res = ast_mutex_unlock(&p->lock); -/* - ast_verb(4, "%s: unLOCKING in answer [%d]\n", p->dev,res); -*/ - return 0; -} - -static struct ast_frame *vpb_read(struct ast_channel *ast) -{ - struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt; - static struct ast_frame f = { AST_FRAME_NULL }; - - f.src = "vpb"; - ast_log(LOG_NOTICE, "%s: vpb_read: should never be called!\n", p->dev); - ast_verbose("%s: vpb_read: should never be called!\n", p->dev); - - return &f; -} - -static inline AudioCompress ast2vpbformat(int ast_format) -{ - switch (ast_format) { - case AST_FORMAT_ALAW: - return VPB_ALAW; - case AST_FORMAT_SLINEAR: - return VPB_LINEAR; - case AST_FORMAT_ULAW: - return VPB_MULAW; - case AST_FORMAT_ADPCM: - return VPB_OKIADPCM; - default: - return VPB_RAW; - } -} - -static inline const char * ast2vpbformatname(int ast_format) -{ - switch(ast_format) { - case AST_FORMAT_ALAW: - return "AST_FORMAT_ALAW:VPB_ALAW"; - case AST_FORMAT_SLINEAR: - return "AST_FORMAT_SLINEAR:VPB_LINEAR"; - case AST_FORMAT_ULAW: - return "AST_FORMAT_ULAW:VPB_MULAW"; - case AST_FORMAT_ADPCM: - return "AST_FORMAT_ADPCM:VPB_OKIADPCM"; - default: - return "UNKN:UNKN"; - } -} - -static inline int astformatbits(int ast_format) -{ - switch (ast_format) { - case AST_FORMAT_SLINEAR: - return 16; - case AST_FORMAT_ADPCM: - return 4; - case AST_FORMAT_ALAW: - case AST_FORMAT_ULAW: - default: - return 8; - } -} - -int a_gain_vector(float g, short *v, int n) -{ - int i; - float tmp; - for (i = 0; i < n; i++) { - tmp = g * v[i]; - if (tmp > 32767.0) - tmp = 32767.0; - if (tmp < -32768.0) - tmp = -32768.0; - v[i] = (short)tmp; - } - return i; -} - -/* Writes a frame of voice data to a VPB channel */ -static int vpb_write(struct ast_channel *ast, struct ast_frame *frame) -{ - struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt; - int res = 0; - AudioCompress fmt = VPB_RAW; - struct timeval play_buf_time_start; - int tdiff; - -/* ast_mutex_lock(&p->lock); */ - ast_verb(6, "%s: vpb_write: Writing to channel\n", p->dev); - - if (frame->frametype != AST_FRAME_VOICE) { - ast_verb(4, "%s: vpb_write: Don't know how to handle from type %d\n", ast->name, frame->frametype); -/* ast_mutex_unlock(&p->lock); */ - return 0; - } else if (ast->_state != AST_STATE_UP) { - ast_verb(4, "%s: vpb_write: Attempt to Write frame type[%d]subclass[%d] on not up chan(state[%d])\n",ast->name, frame->frametype, frame->subclass,ast->_state); - p->lastoutput = -1; -/* ast_mutex_unlock(&p->lock); */ - return 0; - } -/* ast_debug(1, "%s: vpb_write: Checked frame type..\n", p->dev); */ - - - fmt = ast2vpbformat(frame->subclass); - if (fmt < 0) { - ast_log(LOG_WARNING, "%s: vpb_write: Cannot handle frames of %d format!\n", ast->name, frame->subclass); - return -1; - } - - tdiff = ast_tvdiff_ms(ast_tvnow(), p->lastplay); - ast_debug(1, "%s: vpb_write: time since last play(%d) \n", p->dev, tdiff); - if (tdiff < (VPB_SAMPLES / 8 - 1)) { - ast_debug(1, "%s: vpb_write: Asked to play too often (%d) (%d)\n", p->dev, tdiff, frame->datalen); -/* return 0; */ - } - p->lastplay = ast_tvnow(); -/* - ast_debug(1, "%s: vpb_write: Checked frame format..\n", p->dev); -*/ - - ast_mutex_lock(&p->play_lock); - -/* - ast_debug(1, "%s: vpb_write: Got play lock..\n", p->dev); -*/ - - /* Check if we have set up the play_buf */ - if (p->lastoutput == -1) { - vpb_play_buf_start(p->handle, fmt); - ast_verb(2, "%s: vpb_write: Starting play mode (codec=%d)[%s]\n", p->dev, fmt, ast2vpbformatname(frame->subclass)); - p->lastoutput = fmt; - ast_mutex_unlock(&p->play_lock); - return 0; - } else if (p->lastoutput != fmt) { - vpb_play_buf_finish(p->handle); - vpb_play_buf_start(p->handle, fmt); - ast_verb(2, "%s: vpb_write: Changed play format (%d=>%d)\n", p->dev, p->lastoutput, fmt); - ast_mutex_unlock(&p->play_lock); - return 0; - } - p->lastoutput = fmt; - - - - /* Apply extra gain ! */ - if( p->txswgain > MAX_VPB_GAIN ) - a_gain_vector(p->txswgain - MAX_VPB_GAIN , (short*)frame->data, frame->datalen / sizeof(short)); - -/* ast_debug(1, "%s: vpb_write: Applied gain..\n", p->dev); */ -/* ast_debug(1, "%s: vpb_write: play_buf_time %d\n", p->dev, p->play_buf_time); */ - - if ((p->read_state == 1) && (p->play_buf_time < 5)){ - play_buf_time_start = ast_tvnow(); -/* res = vpb_play_buf_sync(p->handle, (char *)frame->data, tdiff * 8 * 2); */ - res = vpb_play_buf_sync(p->handle, (char *)frame->data, frame->datalen); - if(res == VPB_OK) { - short * data = (short*)frame->data; - ast_verb(6, "%s: vpb_write: Wrote chan (codec=%d) %d %d\n", p->dev, fmt, data[0], data[1]); - } - p->play_buf_time = ast_tvdiff_ms(ast_tvnow(), play_buf_time_start); - } else { - p->chuck_count++; - ast_debug(1, "%s: vpb_write: Tossed data away, tooooo much data!![%d]\n", p->dev, p->chuck_count); - p->play_buf_time = 0; - } - - ast_mutex_unlock(&p->play_lock); -/* ast_mutex_unlock(&p->lock); */ - ast_verb(6, "%s: vpb_write: Done Writing to channel\n", p->dev); - return 0; -} - -/* Read monitor thread function. */ -static void *do_chanreads(void *pvt) -{ - struct vpb_pvt *p = (struct vpb_pvt *)pvt; - struct ast_frame *fr = &p->fr; - char *readbuf = ((char *)p->buf) + AST_FRIENDLY_OFFSET; - int bridgerec = 0; - int afmt, readlen, res, trycnt=0; - AudioCompress fmt; - int ignore_dtmf; - const char * getdtmf_var = NULL; - - fr->frametype = AST_FRAME_VOICE; - fr->src = "vpb"; - fr->mallocd = 0; - fr->delivery.tv_sec = 0; - fr->delivery.tv_usec = 0; - fr->samples = VPB_SAMPLES; - fr->offset = AST_FRIENDLY_OFFSET; - memset(p->buf, 0, sizeof p->buf); - - ast_verb(3, "%s: chanreads: starting thread\n", p->dev); - ast_mutex_lock(&p->record_lock); - - p->stopreads = 0; - p->read_state = 1; - while (!p->stopreads && p->owner) { - - ast_verb(5, "%s: chanreads: Starting cycle ...\n", p->dev); - ast_verb(5, "%s: chanreads: Checking bridge \n", p->dev); - if (p->bridge) { - if (p->bridge->c0 == p->owner && (p->bridge->flags & AST_BRIDGE_REC_CHANNEL_0)) - bridgerec = 1; - else if (p->bridge->c1 == p->owner && (p->bridge->flags & AST_BRIDGE_REC_CHANNEL_1)) - bridgerec = 1; - else - bridgerec = 0; - } else { - ast_verb(5, "%s: chanreads: No native bridge.\n", p->dev); - if (p->owner->_bridge) { - ast_verb(5, "%s: chanreads: Got Asterisk bridge with [%s].\n", p->dev, p->owner->_bridge->name); - bridgerec = 1; - } else { - bridgerec = 0; - } - } - -/* if ((p->owner->_state != AST_STATE_UP) || !bridgerec) */ - if ((p->owner->_state != AST_STATE_UP)) { - if (p->owner->_state != AST_STATE_UP) { - ast_verb(5, "%s: chanreads: Im not up[%d]\n", p->dev, p->owner->_state); - } else { - ast_verb(5, "%s: chanreads: No bridgerec[%d]\n", p->dev, bridgerec); - } - vpb_sleep(10); - continue; - } - - /* Voicetronix DTMF detection can be triggered off ordinary speech - * This leads to annoying beeps during the conversation - * Avoid this problem by just setting VPB_GETDTMF when you want to listen for DTMF - */ - /* ignore_dtmf = 1; */ - ignore_dtmf = 0; /* set this to 1 to turn this feature on */ - getdtmf_var = pbx_builtin_getvar_helper(p->owner, "VPB_GETDTMF"); - if (getdtmf_var && strcasecmp(getdtmf_var, "yes") == 0) - ignore_dtmf = 0; - - if ((ignore_dtmf != p->last_ignore_dtmf) && (!use_ast_dtmfdet)){ - ast_verb(2, "%s:Now %s DTMF \n", - p->dev, ignore_dtmf ? "ignoring" : "listening for"); - vpb_set_event_mask(p->handle, ignore_dtmf ? VPB_EVENTS_NODTMF : VPB_EVENTS_ALL ); - } - p->last_ignore_dtmf = ignore_dtmf; - - /* Play DTMF digits here to avoid problem you get if playing a digit during - * a record operation - */ - ast_verb(6, "%s: chanreads: Checking dtmf's \n", p->dev); - ast_mutex_lock(&p->play_dtmf_lock); - if (p->play_dtmf[0]) { - /* Try to ignore DTMF event we get after playing digit */ - /* This DTMF is played by asterisk and leads to an annoying trailing beep on CISCO phones */ - if (!ignore_dtmf) { - vpb_set_event_mask(p->handle, VPB_EVENTS_NODTMF ); - } - if (p->bridge == NULL) { - vpb_dial_sync(p->handle, p->play_dtmf); - ast_verb(2, "%s: chanreads: Played DTMF %s\n", p->dev, p->play_dtmf); - } else { - ast_verb(2, "%s: chanreads: Not playing DTMF frame on native bridge\n", p->dev); - } - p->play_dtmf[0] = '\0'; - ast_mutex_unlock(&p->play_dtmf_lock); - vpb_sleep(700); /* Long enough to miss echo and DTMF event */ - if( !ignore_dtmf) - vpb_set_event_mask(p->handle, VPB_EVENTS_ALL); - continue; - } - ast_mutex_unlock(&p->play_dtmf_lock); - -/* afmt = (p->owner) ? p->owner->rawreadformat : AST_FORMAT_SLINEAR; */ - if (p->owner) { - afmt = p->owner->rawreadformat; -/* ast_debug(1,"%s: Record using owner format [%s]\n", p->dev, ast2vpbformatname(afmt)); */ - } else { - afmt = AST_FORMAT_SLINEAR; -/* ast_debug(1,"%s: Record using default format [%s]\n", p->dev, ast2vpbformatname(afmt)); */ - } - fmt = ast2vpbformat(afmt); - if (fmt < 0) { - ast_log(LOG_WARNING, "%s: Record failure (unsupported format %d)\n", p->dev, afmt); - return NULL; - } - readlen = VPB_SAMPLES * astformatbits(afmt) / 8; - - if (p->lastinput == -1) { - vpb_record_buf_start(p->handle, fmt); -/* vpb_reset_record_fifo_alarm(p->handle); */ - p->lastinput = fmt; - ast_verb(2, "%s: Starting record mode (codec=%d)[%s]\n", p->dev, fmt, ast2vpbformatname(afmt)); - continue; - } else if (p->lastinput != fmt) { - vpb_record_buf_finish(p->handle); - vpb_record_buf_start(p->handle, fmt); - p->lastinput = fmt; - ast_verb(2, "%s: Changed record format (%d=>%d)\n", p->dev, p->lastinput, fmt); - continue; - } - - /* Read only if up and not bridged, or a bridge for which we can read. */ - ast_verb(6, "%s: chanreads: getting buffer!\n", p->dev); - if( (res = vpb_record_buf_sync(p->handle, readbuf, readlen) ) == VPB_OK ) { - ast_verb(6, "%s: chanreads: got buffer!\n", p->dev); - /* Apply extra gain ! */ - if( p->rxswgain > MAX_VPB_GAIN ) - a_gain_vector(p->rxswgain - MAX_VPB_GAIN, (short *)readbuf, readlen / sizeof(short)); - ast_verb(6, "%s: chanreads: applied gain\n", p->dev); - - fr->subclass = afmt; - fr->data = readbuf; - fr->datalen = readlen; - fr->frametype = AST_FRAME_VOICE; - - if ((use_ast_dtmfdet)&&(p->vad)) { - fr = ast_dsp_process(p->owner,p->vad,fr); - if (fr && (fr->frametype == AST_FRAME_DTMF)) { - ast_debug(1, "%s: chanreads: Detected DTMF '%c'\n", p->dev, fr->subclass); - } else if (fr->subclass == 'f') { - } - } - /* Using trylock here to prevent deadlock when channel is hungup - * (ast_hangup() immediately gets lock) - */ - if (p->owner && !p->stopreads) { - ast_verb(6, "%s: chanreads: queueing buffer on read frame q (state[%d])\n", p->dev, p->owner->_state); - do { - res = ast_channel_trylock(p->owner); - trycnt++; - } while ((res !=0 ) && (trycnt < 300)); - if (res == 0) { - ast_queue_frame(p->owner, fr); - ast_channel_unlock(p->owner); - } else { - ast_verb(5, "%s: chanreads: Couldnt get lock after %d tries!\n", p->dev, trycnt); - } - trycnt = 0; - -/* - res = ast_mutex_trylock(&p->owner->lock); - if (res == 0) { - ast_queue_frame(p->owner, fr); - ast_mutex_unlock(&p->owner->lock); - } else { - if (res == EINVAL) - ast_verb(5, "%s: chanreads: try owner->lock gave me EINVAL[%d]\n", p->dev, res); - else if (res == EBUSY) - ast_verb(5, "%s: chanreads: try owner->lock gave me EBUSY[%d]\n", p->dev, res); - while (res != 0) { - res = ast_mutex_trylock(&p->owner->lock); - } - if (res == 0) { - ast_queue_frame(p->owner, fr); - ast_mutex_unlock(&p->owner->lock); - } else { - if (res == EINVAL) { - ast_verb(5, "%s: chanreads: try owner->lock gave me EINVAL[%d]\n", p->dev, res); - } else if (res == EBUSY) { - ast_verb(5, "%s: chanreads: try owner->lock gave me EBUSY[%d]\n", p->dev, res); - } - ast_verb(5, "%s: chanreads: Couldnt get lock on owner[%s][%d][%d] channel to send frame!\n", p->dev, p->owner->name, (int)p->owner->lock.__m_owner, (int)p->owner->lock.__m_count); - } - } -*/ - short *data = (short *)readbuf; - ast_verb(7, "%s: Read channel (codec=%d) %d %d\n", p->dev, fmt, data[0], data[1]); - } else { - ast_verb(5, "%s: p->stopreads[%d] p->owner[%p]\n", p->dev, p->stopreads, (void *)p->owner); - } - } - ast_verb(5, "%s: chanreads: Finished cycle...\n", p->dev); - } - p->read_state = 0; - - /* When stopreads seen, go away! */ - vpb_record_buf_finish(p->handle); - p->read_state = 0; - ast_mutex_unlock(&p->record_lock); - - ast_verb(2, "%s: Ending record mode (%d/%s)\n", - p->dev, p->stopreads, p->owner ? "yes" : "no"); - return NULL; -} - -static struct ast_channel *vpb_new(struct vpb_pvt *me, enum ast_channel_state state, const char *context) -{ - struct ast_channel *tmp; - char cid_num[256]; - char cid_name[256]; - - if (me->owner) { - ast_log(LOG_WARNING, "Called vpb_new on owned channel (%s) ?!\n", me->dev); - return NULL; - } - ast_verb(4, "%s: New call for context [%s]\n", me->dev, context); - - tmp = ast_channel_alloc(1, state, 0, 0, "", me->ext, me->context, 0, "%s", me->dev); - if (tmp) { - if (use_ast_ind == 1){ - tmp->tech = &vpb_tech_indicate; - } else { - tmp->tech = &vpb_tech; - } - - tmp->callgroup = me->callgroup; - tmp->pickupgroup = me->pickupgroup; - - /* Linear is the preferred format. Although Voicetronix supports other formats - * they are all converted to/from linear in the vpb code. Best for us to use - * linear since we can then adjust volume in this modules. - */ - tmp->nativeformats = prefformat; - tmp->rawreadformat = AST_FORMAT_SLINEAR; - tmp->rawwriteformat = AST_FORMAT_SLINEAR; - if (state == AST_STATE_RING) { - tmp->rings = 1; - cid_name[0] = '\0'; - cid_num[0] = '\0'; - ast_callerid_split(me->callerid, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num)); - ast_set_callerid(tmp, cid_num, cid_name, cid_num); - } - tmp->tech_pvt = me; - - ast_copy_string(tmp->context, context, sizeof(tmp->context)); - if (!ast_strlen_zero(me->ext)) - ast_copy_string(tmp->exten, me->ext, sizeof(tmp->exten)); - else - strcpy(tmp->exten, "s"); - if (!ast_strlen_zero(me->language)) - ast_string_field_set(tmp, language, me->language); - - me->owner = tmp; - - me->bridge = NULL; - me->lastoutput = -1; - me->lastinput = -1; - me->last_ignore_dtmf = 1; - me->readthread = 0; - me->play_dtmf[0] = '\0'; - me->faxhandled = 0; - - me->lastgrunt = ast_tvnow(); /* Assume at least one grunt tone seen now. */ - me->lastplay = ast_tvnow(); /* Assume at least one grunt tone seen now. */ - - if (state != AST_STATE_DOWN) { - if ((me->mode != MODE_FXO) && (state != AST_STATE_UP)) { - vpb_answer(tmp); - } - if (ast_pbx_start(tmp)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); - ast_hangup(tmp); - } - } - } else { - ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); - } - return tmp; -} - -static struct ast_channel *vpb_request(const char *type, int format, void *vdata, int *cause) -{ - int oldformat; - struct vpb_pvt *p; - struct ast_channel *tmp = NULL; - char *sepstr, *data = (char *)vdata, *name; - const char *s; - int group = -1; - - oldformat = format; - format &= prefformat; - if (!format) { - ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat); - return NULL; - } - - name = ast_strdup(S_OR(data, "")); - - sepstr = name; - s = strsep(&sepstr, "/"); /* Handle / issues */ - if (!s) - s = ""; - /* Check if we are looking for a group */ - if (toupper(name[0]) == 'G' || toupper(name[0]) == 'R') { - group = atoi(name + 1); - } - /* Search for an unowned channel */ - ast_mutex_lock(&iflock); - for (p = iflist; p; p = p->next) { - if (group == -1) { - if (strncmp(s, p->dev + 4, sizeof p->dev) == 0) { - if (!p->owner) { - tmp = vpb_new(p, AST_STATE_DOWN, p->context); - break; - } - } - } else { - if ((p->group == group) && (!p->owner)) { - tmp = vpb_new(p, AST_STATE_DOWN, p->context); - break; - } - } - } - ast_mutex_unlock(&iflock); - - - ast_verb(2, " %s requested, got: [%s]\n", name, tmp ? tmp->name : "None"); - - ast_free(name); - - restart_monitor(); - return tmp; -} - -static float parse_gain_value(const char *gain_type, const char *value) -{ - float gain; - - /* try to scan number */ - if (sscanf(value, "%f", &gain) != 1) { - ast_log(LOG_ERROR, "Invalid %s value '%s' in '%s' config\n", value, gain_type, config); - return DEFAULT_GAIN; - } - - - /* percentage? */ - /*if (value[strlen(value) - 1] == '%') */ - /* return gain / (float)100; */ - - return gain; -} - - -static int unload_module(void) -{ - struct vpb_pvt *p; - /* First, take us out of the channel loop */ - if (use_ast_ind == 1){ - ast_channel_unregister(&vpb_tech_indicate); - } else { - ast_channel_unregister(&vpb_tech); - } - - ast_mutex_lock(&iflock); - /* Hangup all interfaces if they have an owner */ - for (p = iflist; p; p = p->next) { - if (p->owner) - ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD); - } - iflist = NULL; - ast_mutex_unlock(&iflock); - - ast_mutex_lock(&monlock); - if (mthreadactive > -1) { - pthread_cancel(monitor_thread); - pthread_join(monitor_thread, NULL); - } - mthreadactive = -2; - ast_mutex_unlock(&monlock); - - ast_mutex_lock(&iflock); - /* Destroy all the interfaces and free their memory */ - - while (iflist) { - p = iflist; - ast_mutex_destroy(&p->lock); - pthread_cancel(p->readthread); - ast_mutex_destroy(&p->owner_lock); - ast_mutex_destroy(&p->record_lock); - ast_mutex_destroy(&p->play_lock); - ast_mutex_destroy(&p->play_dtmf_lock); - p->readthread = 0; - - vpb_close(p->handle); - - iflist = iflist->next; - - ast_free(p); - } - iflist = NULL; - ast_mutex_unlock(&iflock); - - if (bridges) { - ast_mutex_lock(&bridge_lock); - memset(bridges, 0, sizeof bridges); - ast_mutex_unlock(&bridge_lock); - ast_mutex_destroy(&bridge_lock); - for (int i = 0; i < max_bridges; i++) { - ast_mutex_destroy(&bridges[i].lock); - ast_cond_destroy(&bridges[i].cond); - } - ast_free(bridges); - } - - return 0; -} - -static enum ast_module_load_result load_module() -{ - struct ast_config *cfg; - struct ast_variable *v; - struct vpb_pvt *tmp; - int board = 0, group = 0; - ast_group_t callgroup = 0; - ast_group_t pickupgroup = 0; - int mode = MODE_IMMEDIATE; - float txgain = DEFAULT_GAIN, rxgain = DEFAULT_GAIN; - float txswgain = 0, rxswgain = 0; - int got_gain=0; - int first_channel = 1; - int echo_cancel = DEFAULT_ECHO_CANCEL; - enum ast_module_load_result error = AST_MODULE_LOAD_SUCCESS; /* Error flag */ - int bal1 = -1; /* Special value - means do not set */ - int bal2 = -1; - int bal3 = -1; - char * callerid = NULL; - - int num_cards = 0; - try { - num_cards = vpb_get_num_cards(); - } catch (VpbException e) { - ast_log(LOG_ERROR, "No Voicetronix cards detected\n"); - return AST_MODULE_LOAD_DECLINE; - } - - int ports_per_card[num_cards]; - for (int i = 0; i < num_cards; ++i) - ports_per_card[i] = vpb_get_ports_per_card(i); - - cfg = ast_config_load(config); - - /* We *must* have a config file otherwise stop immediately */ - if (!cfg) { - ast_log(LOG_ERROR, "Unable to load config %s\n", config); - return AST_MODULE_LOAD_DECLINE; - } - - ast_mutex_lock(&iflock); - v = ast_variable_browse(cfg, "general"); - while (v){ - if (strcasecmp(v->name, "cards") == 0) { - ast_log(LOG_NOTICE, "VPB Driver configured to use [%d] cards\n", atoi(v->value)); - } else if (strcasecmp(v->name, "indication") == 0) { - use_ast_ind = 1; - ast_log(LOG_NOTICE, "VPB driver using Asterisk Indication functions!\n"); - } else if (strcasecmp(v->name, "break-for-dtmf") == 0) { - if (ast_true(v->value)) { - break_for_dtmf = 1; - } else { - break_for_dtmf = 0; - ast_log(LOG_NOTICE, "VPB driver not stopping for DTMF's in native bridge\n"); - } - } else if (strcasecmp(v->name, "ast-dtmf") == 0) { - use_ast_dtmf = 1; - ast_log(LOG_NOTICE, "VPB driver using Asterisk DTMF play functions!\n"); - } else if (strcasecmp(v->name, "ast-dtmf-det") == 0) { - use_ast_dtmfdet = 1; - ast_log(LOG_NOTICE, "VPB driver using Asterisk DTMF detection functions!\n"); - } else if (strcasecmp(v->name, "relaxdtmf") == 0) { - relaxdtmf = 1; - ast_log(LOG_NOTICE, "VPB driver using Relaxed DTMF with Asterisk DTMF detections functions!\n"); - } else if (strcasecmp(v->name, "timer_period_ring") == 0) { - timer_period_ring = atoi(v->value); - } else if (strcasecmp(v->name, "ecsuppthres") == 0) { - ec_supp_threshold = (short)atoi(v->value); - } else if (strcasecmp(v->name, "dtmfidd") == 0) { - dtmf_idd = atoi(v->value); - ast_log(LOG_NOTICE, "VPB Driver setting DTMF IDD to [%d]ms\n", dtmf_idd); - } - v = v->next; - } - - v = ast_variable_browse(cfg, "interfaces"); - while (v) { - /* Create the interface list */ - if (strcasecmp(v->name, "board") == 0) { - board = atoi(v->value); - } else if (strcasecmp(v->name, "group") == 0) { - group = atoi(v->value); - } else if (strcasecmp(v->name, "callgroup") == 0) { - callgroup = ast_get_group(v->value); - } else if (strcasecmp(v->name, "pickupgroup") == 0) { - pickupgroup = ast_get_group(v->value); - } else if (strcasecmp(v->name, "usepolaritycid") == 0) { - UsePolarityCID = atoi(v->value); - } else if (strcasecmp(v->name, "useloopdrop") == 0) { - UseLoopDrop = atoi(v->value); - } else if (strcasecmp(v->name, "usenativebridge") == 0) { - UseNativeBridge = atoi(v->value); - } else if (strcasecmp(v->name, "channel") == 0) { - int channel = atoi(v->value); - if (board >= num_cards || board < 0 || channel < 0 || channel >= ports_per_card[board]) { - ast_log(LOG_ERROR, "Invalid board/channel (%d/%d) for channel '%s'\n", board, channel, v->value); - error = AST_MODULE_LOAD_FAILURE; - goto done; - } - tmp = mkif(board, channel, mode, got_gain, txgain, rxgain, txswgain, rxswgain, bal1, bal2, bal3, callerid, echo_cancel,group,callgroup,pickupgroup); - if (tmp) { - if (first_channel) { - mkbrd(tmp->vpb_model, echo_cancel); - first_channel = 0; - } - tmp->next = iflist; - iflist = tmp; - } else { - ast_log(LOG_ERROR, "Unable to register channel '%s'\n", v->value); - error = AST_MODULE_LOAD_FAILURE; - goto done; - } - } else if (strcasecmp(v->name, "language") == 0) { - ast_copy_string(language, v->value, sizeof(language)); - } else if (strcasecmp(v->name, "callerid") == 0) { - callerid = ast_strdup(v->value); - } else if (strcasecmp(v->name, "mode") == 0) { - if (strncasecmp(v->value, "di", 2) == 0) { - mode = MODE_DIALTONE; - } else if (strncasecmp(v->value, "im", 2) == 0) { - mode = MODE_IMMEDIATE; - } else if (strncasecmp(v->value, "fx", 2) == 0) { - mode = MODE_FXO; - } else { - ast_log(LOG_WARNING, "Unknown mode: %s\n", v->value); - } - } else if (!strcasecmp(v->name, "context")) { - ast_copy_string(context, v->value, sizeof(context)); - } else if (!strcasecmp(v->name, "echocancel")) { - if (!strcasecmp(v->value, "off")) { - echo_cancel = 0; - } - } else if (strcasecmp(v->name, "txgain") == 0) { - txswgain = parse_gain_value(v->name, v->value); - got_gain |= VPB_GOT_TXSWG; - } else if (strcasecmp(v->name, "rxgain") == 0) { - rxswgain = parse_gain_value(v->name, v->value); - got_gain |= VPB_GOT_RXSWG; - } else if (strcasecmp(v->name, "txhwgain") == 0) { - txgain = parse_gain_value(v->name, v->value); - got_gain |= VPB_GOT_TXHWG; - } else if (strcasecmp(v->name, "rxhwgain") == 0) { - rxgain = parse_gain_value(v->name, v->value); - got_gain |= VPB_GOT_RXHWG; - } else if (strcasecmp(v->name, "bal1") == 0) { - bal1 = strtol(v->value, NULL, 16); - if (bal1 < 0 || bal1 > 255) { - ast_log(LOG_WARNING, "Bad bal1 value: %d\n", bal1); - bal1 = -1; - } - } else if (strcasecmp(v->name, "bal2") == 0) { - bal2 = strtol(v->value, NULL, 16); - if (bal2 < 0 || bal2 > 255) { - ast_log(LOG_WARNING, "Bad bal2 value: %d\n", bal2); - bal2 = -1; - } - } else if (strcasecmp(v->name, "bal3") == 0) { - bal3 = strtol(v->value, NULL, 16); - if (bal3 < 0 || bal3 > 255) { - ast_log(LOG_WARNING, "Bad bal3 value: %d\n", bal3); - bal3 = -1; - } - } else if (strcasecmp(v->name, "grunttimeout") == 0) { - gruntdetect_timeout = 1000 * atoi(v->value); - } - v = v->next; - } - - if (gruntdetect_timeout < 1000) - gruntdetect_timeout = 1000; - - done: (void)0; - ast_mutex_unlock(&iflock); - - ast_config_destroy(cfg); - - if (use_ast_ind == 1) { - if (error == AST_MODULE_LOAD_SUCCESS && ast_channel_register(&vpb_tech_indicate) != 0) { - ast_log(LOG_ERROR, "Unable to register channel class 'vpb'\n"); - error = AST_MODULE_LOAD_FAILURE; - } else { - ast_log(LOG_NOTICE, "VPB driver Registered (w/AstIndication)\n"); - } - } else { - if (error == AST_MODULE_LOAD_SUCCESS && ast_channel_register(&vpb_tech) != 0) { - ast_log(LOG_ERROR, "Unable to register channel class 'vpb'\n"); - error = AST_MODULE_LOAD_FAILURE; - } else { - ast_log(LOG_NOTICE, "VPB driver Registered )\n"); - } - } - - - if (error != AST_MODULE_LOAD_SUCCESS) - unload_module(); - else - restart_monitor(); /* And start the monitor for the first time */ - - return error; -} - -/**/ -#if defined(__cplusplus) || defined(c_plusplus) - } -#endif -/**/ - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Voicetronix API driver"); diff --git a/1.4.23-rc4/channels/gentone-ulaw.c b/1.4.23-rc4/channels/gentone-ulaw.c deleted file mode 100644 index b290d76c7..000000000 --- a/1.4.23-rc4/channels/gentone-ulaw.c +++ /dev/null @@ -1,157 +0,0 @@ -/* Generate a header file for a particular - single or double frequency */ - -#include <stdio.h> -#include <math.h> -#include <string.h> -#include <unistd.h> -#include <stdlib.h> -#define CLIP 32635 -#define BIAS 0x84 -static float loudness=16384.0; - -static int calc_samples(int freq) -{ - int x, samples; - /* Calculate the number of samples at 8000hz sampling - we need to have this wave form */ - samples = 8000; - /* Take out common 2's up to six times */ - for (x=0;x<6;x++) - if (!(freq % 2)) { - freq /= 2; - samples /= 2; - } - /* Take out common 5's (up to three times */ - for (x=0;x<3;x++) - if (!(freq % 5)) { - freq /= 5; - samples /=5; - } - /* No more common factors. */ - return samples; -} - -/* -** This routine converts from linear to ulaw -** -** Craig Reese: IDA/Supercomputing Research Center -** Joe Campbell: Department of Defense -** 29 September 1989 -** -** References: -** 1) CCITT Recommendation G.711 (very difficult to follow) -** 2) "A New Digital Technique for Implementation of Any -** Continuous PCM Companding Law," Villeret, Michel, -** et al. 1973 IEEE Int. Conf. on Communications, Vol 1, -** 1973, pg. 11.12-11.17 -** 3) MIL-STD-188-113,"Interoperability and Performance Standards -** for Analog-to_Digital Conversion Techniques," -** 17 February 1987 -** -** Input: Signed 16 bit linear sample -** Output: 8 bit ulaw sample -*/ - -#define ZEROTRAP /* turn on the trap as per the MIL-STD */ -#define BIAS 0x84 /* define the add-in bias for 16 bit samples */ -#define CLIP 32635 - -static unsigned char linear2ulaw(short sample) { -static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; - int sign, exponent, mantissa; - unsigned char ulawbyte; - - /* Get the sample into sign-magnitude. */ - sign = (sample >> 8) & 0x80; /* set aside the sign */ - if (sign != 0) sample = -sample; /* get magnitude */ - if (sample > CLIP) sample = CLIP; /* clip the magnitude */ - - /* Convert from 16 bit linear to ulaw. */ - sample = sample + BIAS; - exponent = exp_lut[(sample >> 7) & 0xFF]; - mantissa = (sample >> (exponent + 3)) & 0x0F; - ulawbyte = ~(sign | (exponent << 4) | mantissa); -#ifdef ZEROTRAP - if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */ -#endif - - return(ulawbyte); -} - -int main(int argc, char *argv[]) -{ - FILE *f; - int freq1, freq2; - float wlen1, wlen2; - float val; - int x, samples1, samples2, samples=0; - char fn[256]; - if (argc < 3) { - fprintf(stderr, "Usage: gensound <name> <freq1> [freq2]\n"); - exit(1); - } - freq1 = atoi(argv[2]); - if (argc > 3) - freq2 = atoi(argv[3]); - else - freq2 = 0; - wlen1 = 8000.0/(float)freq1; - samples1 = calc_samples(freq1); - printf("Wavelength 1 (in samples): %10.5f\n", wlen1); - printf("Minimum samples (1): %d (%f.3 wavelengths)\n", samples1, samples1 / wlen1); - if (freq2) { - wlen2 = 8000.0/(float)freq2; - samples2 = calc_samples(freq2); - printf("Wavelength 1 (in samples): %10.5f\n", wlen2); - printf("Minimum samples (1): %d (%f.3 wavelengths)\n", samples2, samples2 / wlen2); - } - samples = samples1; - if (freq2) { - while(samples % samples2) - samples += samples1; - } - printf("Need %d samples\n", samples); - snprintf(fn, sizeof(fn), "%s.h", argv[1]); - if ((f = fopen(fn, "w"))) { - if (freq2) - fprintf(f, "/* %s: Generated from frequencies %d and %d \n" - " by gentone. %d samples */\n", fn, freq1, freq2, samples); - else - fprintf(f, "/* %s: Generated from frequency %d\n" - " by gentone. %d samples */\n", fn, freq1, samples); - fprintf(f, "static unsigned char %s[%d] = {\n\t", argv[1], samples); - for (x=0;x<samples;x++) { - val = loudness * sin((freq1 * 2.0 * M_PI * x)/8000.0); - if (freq2) - val += loudness * sin((freq2 * 2.0 * M_PI * x)/8000.0); - fprintf(f, "%3d, ", (int) linear2ulaw(val)); - if (!((x+1) % 8)) - fprintf(f, "\n\t"); - } - if (x % 15) - fprintf(f, "\n"); - fprintf(f, "};\n"); - fclose(f); - printf("Wrote %s\n", fn); - } else { - fprintf(stderr, "Unable to open %s for writing\n", fn); - return 1; - } - return 0; -} diff --git a/1.4.23-rc4/channels/gentone.c b/1.4.23-rc4/channels/gentone.c deleted file mode 100644 index 29bd88e91..000000000 --- a/1.4.23-rc4/channels/gentone.c +++ /dev/null @@ -1,95 +0,0 @@ -/* Generate a header file for a particular - single or double frequency */ - -#include <stdio.h> -#include <math.h> -#include <string.h> -#include <unistd.h> -#include <stdlib.h> -#define CLIP 32635 -#define BIAS 0x84 -static float loudness=16384.0; - -static int calc_samples(int freq) -{ - int x, samples; - /* Calculate the number of samples at 8000hz sampling - we need to have this wave form */ - samples = 8000; - /* Take out common 2's up to six times */ - for (x=0;x<6;x++) - if (!(freq % 2)) { - freq /= 2; - samples /= 2; - } - /* Take out common 5's (up to three times */ - for (x=0;x<3;x++) - if (!(freq % 5)) { - freq /= 5; - samples /=5; - } - /* No more common factors. */ - return samples; -} - -int main(int argc, char *argv[]) -{ - FILE *f; - int freq1, freq2; - float wlen1, wlen2; - float val; - int x, samples1, samples2=0, samples=0; - char fn[256]; - if (argc < 3) { - fprintf(stderr, "Usage: gensound <name> <freq1> [freq2]\n"); - exit(1); - } - freq1 = atoi(argv[2]); - if (argc > 3) - freq2 = atoi(argv[3]); - else - freq2 = 0; - wlen1 = 8000.0/(float)freq1; - samples1 = calc_samples(freq1); - printf("Wavelength 1 (in samples): %10.5f\n", wlen1); - printf("Minimum samples (1): %d (%f.3 wavelengths)\n", samples1, samples1 / wlen1); - if (freq2) { - wlen2 = 8000.0/(float)freq2; - samples2 = calc_samples(freq2); - printf("Wavelength 1 (in samples): %10.5f\n", wlen2); - printf("Minimum samples (1): %d (%f.3 wavelengths)\n", samples2, samples2 / wlen2); - } - samples = samples1; - if (freq2) { - while(samples % samples2) - samples += samples1; - } - printf("Need %d samples\n", samples); - snprintf(fn, sizeof(fn), "%s.h", argv[1]); - if ((f = fopen(fn, "w"))) { - if (freq2) - fprintf(f, "/* %s: Generated from frequencies %d and %d \n" - " by gentone. %d samples */\n", fn, freq1, freq2, samples); - else - fprintf(f, "/* %s: Generated from frequency %d\n" - " by gentone. %d samples */\n", fn, freq1, samples); - fprintf(f, "static short %s[%d] = {\n\t", argv[1], samples); - for (x=0;x<samples;x++) { - val = loudness * sin((freq1 * 2.0 * M_PI * x)/8000.0); - if (freq2) - val += loudness * sin((freq2 * 2.0 * M_PI * x)/8000.0); - fprintf(f, "%5d, ", (int)val); - if (!((x+1) % 8)) - fprintf(f, "\n\t"); - } - if (x % 15) - fprintf(f, "\n"); - fprintf(f, "};\n"); - fclose(f); - printf("Wrote %s\n", fn); - } else { - fprintf(stderr, "Unable to open %s for writing\n", fn); - return 1; - } - return 0; -} diff --git a/1.4.23-rc4/channels/h323/ChangeLog b/1.4.23-rc4/channels/h323/ChangeLog deleted file mode 100644 index ddbf08193..000000000 --- a/1.4.23-rc4/channels/h323/ChangeLog +++ /dev/null @@ -1,43 +0,0 @@ -Build - -- Hold lock when creating new H.323 channel to sync the audio channels - -- Decrement usage counter when appropriate - -- Actually unregister everything in unload_module - -- Add IP based authentication using 'host'in type=user's -0.1.0 - -- Intergration into the mainline Asterisk codebase - -- Remove reduandant debug info - -- Add Caller*id support - -- Inband DTMF - -- Retool port usage (to avoid possible seg fault condition) -0.0.6 - -- Configurable support for user-input (DTMF) - -- Reworked Gatekeeper support - -- Native bridging (but is still broken, help!) - -- Locally implement a non-broken G.723.1 Capability - -- Utilize the cleaner RTP method implemented by Mark - -- AllowGkRouted, thanks to Panny from http://hotlinks.co.uk - -- Clened up inbound call flow - -- Prefix, E.164 and Gateway support - -- Multi-homed support - -- Killed more seg's -0.0.5 - -- Added H.323 Alias support - -- Clened up inbound call flow - -- Fixed RTP port logic - -- Stomped on possible seg fault conditions thanks to Iain Stevenson -0.0.4 - -- Fixed one-way audio on inbound calls. Found - race condition in monitor thread. - -0.0.3 - -- Changed name to chan_h323 - -- Also renamed file names to futher avoid confusion - -0.0.2 - -- First public offering - -- removed most hardcoded values - -- lots of changes to alias/exension operation - -0.0.1 - -- initial build, lots of hardcoded crap - -- Proof of concept for External RTP diff --git a/1.4.23-rc4/channels/h323/INSTALL.openh323 b/1.4.23-rc4/channels/h323/INSTALL.openh323 deleted file mode 100644 index f46c37905..000000000 --- a/1.4.23-rc4/channels/h323/INSTALL.openh323 +++ /dev/null @@ -1,18 +0,0 @@ -To build Open H.323 see: - -http://www.openh323.org/build.html#unix - -You only need to do 'make opt'. Anything else you will be simply waisting time and HD space. -Also, you will notice they never tell you to 'make install' so don't do it. - - -On FreeBSD, the Makefiles are configured to -locate the compiled openh323 port, if it has -been built. Here is one way to build -openh323 and ptlib on such that the Makefiles -find it: - # cd /usr/ports/net/openh323 - # make -It is not necessary to install the port. The -asterisk makefiles do not use any files -installed by the port. diff --git a/1.4.23-rc4/channels/h323/Makefile.in b/1.4.23-rc4/channels/h323/Makefile.in deleted file mode 100644 index 083250f55..000000000 --- a/1.4.23-rc4/channels/h323/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# Makefile -# -# Make file for OpenH323 support layer -# - -.PHONY: Makefile.ast clean - -default:: @OPENH323_BUILD@ - -# Verify those options with main Makefile -STDCCFLAGS = -DNDEBUG -STDCCFLAGS += -I../../include -include ../../include/asterisk/autoconfig.h -STDCCFLAGS += -fPIC -#OPTCCFLAGS += -CFLAGS = -pipe -TARGET = libchanh323.a -TARGET += Makefile.ast -SOURCES = ast_h323.cxx compat_h323.cxx cisco-h225.cxx caps_h323.cxx -OBJDIR = . -OBJS = - -ifndef OPENH323DIR -OPENH323DIR=@OPENH323DIR@ -endif - -include $(OPENH323DIR)/openh323u.mak - -notrace:: - $(MAKE) NOTRACE=1 opt - -$(SOURCES):: Makefile ../../Makefile - touch $@ - -libchanh323.a: $(OBJS) - ar crv $@ $(OBJS) - -cisco-h225.cxx:: cisco-h225.asn - asnparser -m CISCO_H225 -c $< - -Makefile.ast: - @echo H323CFLAGS = $(STDCCFLAGS) $(OPTCCFLAGS) $(CFLAGS) >$@.tmp - @echo H323LDFLAGS = $(CFLAGS) $(LDFLAGS) >>$@.tmp - @echo H323LDLIBS = $(LDLIBS) $(ENDLDLIBS) $(ENDLDFLAGS) >>$@.tmp - @if [ -r $@ ] && cmp -s $@ $@.tmp; then rm -f $@.tmp; else mv -f $@.tmp $@; fi - -clean:: - rm -f $(TARGET) $(OBJS) Makefile.ast *.dep diff --git a/1.4.23-rc4/channels/h323/README b/1.4.23-rc4/channels/h323/README deleted file mode 100644 index 875bf3668..000000000 --- a/1.4.23-rc4/channels/h323/README +++ /dev/null @@ -1,144 +0,0 @@ - Open H.323 Channel Driver for Asterisk - By Jeremy McNamara - For The NuFone Network - - First public release on November 10th, 2002 - - Dependancies (based on OpenH323/PWLib ones): - openssl-0.9.6b+ - openssl-devel-0.9.6b+ - expat-1.95+ - expat-dev-1.95+ - -Tested with Open H.323 version v1.18.0, PWLib v1.10.0 and GCC v3.2.2. Usage of any -other (especially prior OpenH323 v1.17.3 and PWLib v1.9.2) versions is not -supported. - -NOTICE: Whatever you do, DO NOT USE distrubution specific installs -of Open H.323 and PWLib. In fact, you should check to make sure -your distro did not install them for you without your knowledge. - - -To compile this code --------------------- -Once PWLib and Open H.323 have been compiled per their specific build -instructions, issue a make in the asterisk/channels/h323 directory with -argument used to build PWLib and OpenH323 (for example, make opt), then go -back to the Asterisk source top level directory and issue a make install. - - -The most common compile error ----------------------------- -If you receive ANYTHING that says 'undefined symbol' you are experiencing -typical version skew. For example: - -libh323_linux_x86_r.so.1: undefined symbol: GetNumberValueAt__C14PAbstractArrayi - -You need to search and destroy every version of libh323 and libpt then -completely recompile everything - -Example commands to make sure everything gets cleaned and then -rebult in proper order: - -cd /path/to/pwlib -./configure -make clean opt -cd /path/to/openh323 -./configure -make clean opt -cd /path/to/asterisk/channels/h323 -make opt -cd /path/to/asterisk -make install - - -Most common run-time error -------------------------- -libpt_linux_x86_r.so.1: cannot open shared object file: No such -file or directory - -You have not set the LD_LIBRARY_PATH environment variable. - -Example environment for sh/bash: - -PWLIBDIR=$HOME/pwlib -export PWLIBDIR -OPENH323DIR=$HOME/openh323 -export OPENH323DIR -LD_LIBRARY_PATH=$PWLIBDIR/lib:$OPENH323DIR/lib -export LD_LIBRARY_PATH - -We recomend puting the above directives into your /etc/profile so -you do not have to remember to export those values every time you -want to recompile. Make sure to logout and log back in, so your -envrionment can pick up the new variables. - - -Upgrading Asterisk ------------------ -After you cvs update (or make update) Asterisk you have to go into -asterisk/channels/h323 and issue a make clean all, before compiling the -rest of asterisk. Doing this process every time you upgrade Asterisk -will ensure a sane build. - - -Dialing an H.323 channel ------------------------- -Without a gatekeeper: -exten => _1NXXNXXXXXX,1,Dial,H323/${EXTEN}@peer -or -exten => _1NXXNXXXXXX,1,Dial,H323/${EXTEN}@ip.or.hostname - -'peer' is defined in h323.conf as: - -[peer] -type=peer -host=1.2.3.4 -disallow=all -allow=ulaw - -Using a gatekeeper: -exten => _1NXXNXXXXXX,1,Dial,H323/${EXTEN} - -When using a gatekeeper you cannot utilize the type=peer features, -since the H.323 spec states that when a Gatekeeper is part of an H.323 network, -the Gatekeeper shall be used for all communication. - - -Developer Contact ----------------- -If you have trouble contact 'JerJer' in #Asterisk on -irc.freenode.net and/or send reasonable debug information to support@nufone.net. - -If are lucky enough to segfault this code please run a -backtrace and send the gory details. Segmentation faults are not -tolerated, no matter what Distro you run (even debian)! - -a simple bt example: - -# /usr/sbin/asterisk -vvvgc -... -[chan_h323.so] -Segmentation Fault (core dumped) - -# ls core.* -core.1976 - -# gdb /usr/sbin/asterisk core.1976 -...lots of useless garbage here... -(gdb) bt - -Send whatever shows up right after the 'bt' - -Also, a full debug screen output is almost needed. Make sure you are -in the full console mode (-c) and turn on 'h.323 debug' or worst case -senerio 'h.323 trace 4'. A nice way to capture debug info is with -script (man script). - -If you are motivated to update/fix this code please submit a -disclaimer along with the patch to the Asterisk bug -tracker: http://bugs.digium.com/ - - -Jeremy McNamara -The NuFone Network diff --git a/1.4.23-rc4/channels/h323/TODO b/1.4.23-rc4/channels/h323/TODO deleted file mode 100644 index 1e114ca3b..000000000 --- a/1.4.23-rc4/channels/h323/TODO +++ /dev/null @@ -1,9 +0,0 @@ -The NuFone Network's Open H.323 Channel Driver for Asterisk - - TODO: - - - H.323 Native Bridging - - - Gatekeeping support (started) - - - Acutally implement the options for broken H.323 stacks diff --git a/1.4.23-rc4/channels/h323/ast_h323.cxx b/1.4.23-rc4/channels/h323/ast_h323.cxx deleted file mode 100644 index 32b674dec..000000000 --- a/1.4.23-rc4/channels/h323/ast_h323.cxx +++ /dev/null @@ -1,2487 +0,0 @@ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -/* - * ast_h323.cpp - * - * OpenH323 Channel Driver for ASTERISK PBX. - * By Jeremy McNamara - * For The NuFone Network - * - * chan_h323 has been derived from code created by - * Michael Manousos and Mark Spencer - * - * This file is part of the chan_h323 driver for Asterisk - * - * chan_h323 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. - * - * chan_h323 is distributed WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Version Info: $Id$ - */ -#include <arpa/inet.h> - -#include <list> -#include <string> -#include <algorithm> - -#include <ptlib.h> -#include <h323.h> -#include <h323pdu.h> -#include <h323neg.h> -#include <mediafmt.h> -#include <lid.h> -#ifdef H323_H450 -#include "h4501.h" -#include "h4504.h" -#include "h45011.h" -#include "h450pdu.h" -#endif - -#ifdef __cplusplus -extern "C" { -#endif -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/astobj.h" -#ifdef __cplusplus -} -#endif - -#include "chan_h323.h" -#include "ast_h323.h" -#include "cisco-h225.h" -#include "caps_h323.h" - -#include <ptbuildopts.h> - -#if PWLIB_MAJOR * 10000 + PWLIB_MINOR * 100 + PWLIB_BUILD >= 1 * 10000 + 12 * 100 + 0 -#define SKIP_PWLIB_PIPE_BUG_WORKAROUND 1 -#endif - -/* PWlib Required Components */ -#define MAJOR_VERSION 1 -#define MINOR_VERSION 0 -#define BUILD_TYPE ReleaseCode -#define BUILD_NUMBER 0 - -/** Counter for the number of connections */ -static int channelsOpen; - -/** - * We assume that only one endPoint should exist. - * The application cannot run the h323_end_point_create() more than once - * FIXME: Singleton this, for safety - */ -static MyH323EndPoint *endPoint = NULL; - -/** PWLib entry point */ -static MyProcess *localProcess = NULL; - -#ifndef SKIP_PWLIB_PIPE_BUG_WORKAROUND -static int _timerChangePipe[2]; -#endif - -static unsigned traceOptions = PTrace::Timestamp | PTrace::Thread | PTrace::FileAndLine; - -class PAsteriskLog : public PObject, public iostream { - PCLASSINFO(PAsteriskLog, PObject); - - public: - PAsteriskLog() : iostream(cout.rdbuf()) { init(&buffer); } - ~PAsteriskLog() { flush(); } - - private: - PAsteriskLog(const PAsteriskLog &) : iostream(cout.rdbuf()) { } - PAsteriskLog & operator=(const PAsteriskLog &) { return *this; } - - class Buffer : public streambuf { - public: - virtual int overflow(int=EOF); - virtual int underflow(); - virtual int sync(); - PString string; - } buffer; - friend class Buffer; -}; - -static PAsteriskLog *logstream = NULL; - -int PAsteriskLog::Buffer::overflow(int c) -{ - if (pptr() >= epptr()) { - int ppos = pptr() - pbase(); - char *newptr = string.GetPointer(string.GetSize() + 2000); - setp(newptr, newptr + string.GetSize() - 1); - pbump(ppos); - } - if (c != EOF) { - *pptr() = (char)c; - pbump(1); - } - return 0; -} - -int PAsteriskLog::Buffer::underflow() -{ - return EOF; -} - -int PAsteriskLog::Buffer::sync() -{ - char *str = strdup(string); - char *s, *s1; - char c; - - /* Pass each line with different ast_verbose() call */ - for (s = str; s && *s; s = s1) { - s1 = strchr(s, '\n'); - if (!s1) - s1 = s + strlen(s); - else - s1++; - c = *s1; - *s1 = '\0'; - ast_verbose("%s", s); - *s1 = c; - } - free(str); - - string = PString(); - char *base = string.GetPointer(2000); - setp(base, base + string.GetSize() - 1); - return 0; -} - -static ostream &my_endl(ostream &os) -{ - if (logstream) { - PTrace::SetOptions(traceOptions); - return PTrace::End(os); - } - return endl(os); -} - -#define cout \ - (logstream ? (PTrace::ClearOptions((unsigned)-1), PTrace::Begin(0, __FILE__, __LINE__)) : std::cout) -#define endl my_endl - -/* Special class designed to call cleanup code on module destruction */ -class MyH323_Shutdown { - public: - MyH323_Shutdown() { }; - ~MyH323_Shutdown() - { - h323_end_process(); - }; -}; - -MyProcess::MyProcess(): PProcess("The NuFone Networks", - "H.323 Channel Driver for Asterisk", - MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER) -{ - /* Call shutdown when module being unload or asterisk has been stopped */ - static MyH323_Shutdown x; - - /* Fix missed one in PWLib */ - PX_firstTimeStart = FALSE; - Resume(); -} - -MyProcess::~MyProcess() -{ -#ifndef SKIP_PWLIB_PIPE_BUG_WORKAROUND - _timerChangePipe[0] = timerChangePipe[0]; - _timerChangePipe[1] = timerChangePipe[1]; -#endif -} - -void MyProcess::Main() -{ - PTrace::Initialise(PTrace::GetLevel(), NULL, traceOptions); - PTrace::SetStream(logstream); - - cout << " == Creating H.323 Endpoint" << endl; - if (endPoint) { - cout << " == ENDPOINT ALREADY CREATED" << endl; - return; - } - endPoint = new MyH323EndPoint(); - /* Due to a bug in the H.323 recomendation/stack we should request a sane - amount of bandwidth from the GK - this function is ignored if not using a GK - We are requesting 128 (64k in each direction), which is the worst case codec. */ - endPoint->SetInitialBandwidth(1280); -} - -void PAssertFunc(const char *msg) -{ - ast_log(LOG_ERROR, "%s\n", msg); - /* XXX: Probably we need to crash here */ -} - - -/** MyH323EndPoint - */ -MyH323EndPoint::MyH323EndPoint() - : H323EndPoint() -{ - /* Capabilities will be negotiated on per-connection basis */ - capabilities.RemoveAll(); - - /* Reset call setup timeout to some more reasonable value than 1 minute */ - signallingChannelCallTimeout = PTimeInterval(0, 0, 10); /* 10 minutes */ -} - -/** The fullAddress parameter is used directly in the MakeCall method so - * the General form for the fullAddress argument is : - * [alias@][transport$]host[:port] - * default values: alias = the same value as host. - * transport = ip. - * port = 1720. - */ -int MyH323EndPoint::MyMakeCall(const PString & dest, PString & token, void *_callReference, void *_opts) -{ - PString fullAddress; - MyH323Connection * connection; - H323Transport *transport = NULL; - unsigned int *callReference = (unsigned int *)_callReference; - call_options_t *opts = (call_options_t *)_opts; - - /* Determine whether we are using a gatekeeper or not. */ - if (GetGatekeeper()) { - fullAddress = dest; - if (h323debug) { - cout << " -- Making call to " << fullAddress << " using gatekeeper." << endl; - } - } else { - fullAddress = dest; - if (h323debug) { - cout << " -- Making call to " << fullAddress << " without gatekeeper." << endl; - } - /* Use bindaddr for outgoing calls too if we don't use gatekeeper */ - if (listeners.GetSize() > 0) { - H323TransportAddress taddr = listeners[0].GetTransportAddress(); - PIPSocket::Address addr; - WORD port; - if (taddr.GetIpAndPort(addr, port)) { - /* Create own transport for specific addresses only */ - if (addr) { - if (h323debug) - cout << "Using " << addr << " for outbound call" << endl; - transport = new MyH323TransportTCP(*this, addr); - if (!transport) - cout << "Unable to create transport for outgoing call" << endl; - } - } else - cout << "Unable to get address and port" << endl; - } - } - if (!(connection = (MyH323Connection *)H323EndPoint::MakeCallLocked(fullAddress, token, opts, transport))) { - if (h323debug) { - cout << "Error making call to \"" << fullAddress << '"' << endl; - } - return 1; - } - *callReference = connection->GetCallReference(); - - if (h323debug) { - cout << "\t-- " << GetLocalUserName() << " is calling host " << fullAddress << endl; - cout << "\t-- Call token is " << (const char *)token << endl; - cout << "\t-- Call reference is " << *callReference << endl; -#ifdef PTRACING - cout << "\t-- DTMF Payload is " << connection->dtmfCodec << endl; -#endif - } - connection->Unlock(); - return 0; -} - -void MyH323EndPoint::SetEndpointTypeInfo( H225_EndpointType & info ) const -{ - H323EndPoint::SetEndpointTypeInfo(info); - - if (terminalType == e_GatewayOnly){ - info.RemoveOptionalField(H225_EndpointType::e_terminal); - info.IncludeOptionalField(H225_EndpointType::e_gateway); - } - - info.m_gateway.IncludeOptionalField(H225_GatewayInfo::e_protocol); - info.m_gateway.m_protocol.SetSize(1); - H225_SupportedProtocols &protocol=info.m_gateway.m_protocol[0]; - protocol.SetTag(H225_SupportedProtocols::e_voice); - PINDEX as=SupportedPrefixes.GetSize(); - ((H225_VoiceCaps &)protocol).m_supportedPrefixes.SetSize(as); - for (PINDEX p=0; p<as; p++) { - H323SetAliasAddress(SupportedPrefixes[p], ((H225_VoiceCaps &)protocol).m_supportedPrefixes[p].m_prefix, H225_AliasAddress::e_dialedDigits); - } -} - -void MyH323EndPoint::SetGateway(void) -{ - terminalType = e_GatewayOnly; -} - -BOOL MyH323EndPoint::ClearCall(const PString & token, H323Connection::CallEndReason reason) -{ - if (h323debug) { -#ifdef PTRACING - cout << "\t-- ClearCall: Request to clear call with token " << token << ", cause " << reason << endl; -#else - cout << "\t-- ClearCall: Request to clear call with token " << token << ", cause [" << (int)reason << "]" << endl; -#endif - } - return H323EndPoint::ClearCall(token, reason); -} - -BOOL MyH323EndPoint::ClearCall(const PString & token) -{ - if (h323debug) { - cout << "\t-- ClearCall: Request to clear call with token " << token << endl; - } - return H323EndPoint::ClearCall(token, H323Connection::EndedByLocalUser); -} - -void MyH323EndPoint::SendUserTone(const PString &token, char tone) -{ - H323Connection *connection = NULL; - - connection = FindConnectionWithLock(token); - if (connection != NULL) { - connection->SendUserInputTone(tone, 500); - connection->Unlock(); - } -} - -void MyH323EndPoint::OnClosedLogicalChannel(H323Connection & connection, const H323Channel & channel) -{ - channelsOpen--; - if (h323debug) { - cout << "\t\tchannelsOpen = " << channelsOpen << endl; - } - H323EndPoint::OnClosedLogicalChannel(connection, channel); -} - -BOOL MyH323EndPoint::OnConnectionForwarded(H323Connection & connection, - const PString & forwardParty, - const H323SignalPDU & pdu) -{ - if (h323debug) { - cout << "\t-- Call Forwarded to " << forwardParty << endl; - } - return FALSE; -} - -BOOL MyH323EndPoint::ForwardConnection(H323Connection & connection, - const PString & forwardParty, - const H323SignalPDU & pdu) -{ - if (h323debug) { - cout << "\t-- Forwarding call to " << forwardParty << endl; - } - return H323EndPoint::ForwardConnection(connection, forwardParty, pdu); -} - -void MyH323EndPoint::OnConnectionEstablished(H323Connection & connection, const PString & estCallToken) -{ - if (h323debug) { - cout << "\t=-= In OnConnectionEstablished for call " << connection.GetCallReference() << endl; - cout << "\t\t-- Connection Established with \"" << connection.GetRemotePartyName() << "\"" << endl; - } - on_connection_established(connection.GetCallReference(), (const char *)connection.GetCallToken()); -} - -/** OnConnectionCleared callback function is called upon the dropping of an established - * H323 connection. - */ -void MyH323EndPoint::OnConnectionCleared(H323Connection & connection, const PString & clearedCallToken) -{ - PString remoteName = connection.GetRemotePartyName(); - - switch (connection.GetCallEndReason()) { - case H323Connection::EndedByCallForwarded: - if (h323debug) { - cout << "-- " << remoteName << " has forwarded the call" << endl; - } - break; - case H323Connection::EndedByRemoteUser: - if (h323debug) { - cout << "-- " << remoteName << " has cleared the call" << endl; - } - break; - case H323Connection::EndedByCallerAbort: - if (h323debug) { - cout << "-- " << remoteName << " has stopped calling" << endl; - } - break; - case H323Connection::EndedByRefusal: - if (h323debug) { - cout << "-- " << remoteName << " did not accept your call" << endl; - } - break; - case H323Connection::EndedByRemoteBusy: - if (h323debug) { - cout << "-- " << remoteName << " was busy" << endl; - } - break; - case H323Connection::EndedByRemoteCongestion: - if (h323debug) { - cout << "-- Congested link to " << remoteName << endl; - } - break; - case H323Connection::EndedByNoAnswer: - if (h323debug) { - cout << "-- " << remoteName << " did not answer your call" << endl; - } - break; - case H323Connection::EndedByTransportFail: - if (h323debug) { - cout << "-- Call with " << remoteName << " ended abnormally" << endl; - } - break; - case H323Connection::EndedByCapabilityExchange: - if (h323debug) { - cout << "-- Could not find common codec with " << remoteName << endl; - } - break; - case H323Connection::EndedByNoAccept: - if (h323debug) { - cout << "-- Did not accept incoming call from " << remoteName << endl; - } - break; - case H323Connection::EndedByAnswerDenied: - if (h323debug) { - cout << "-- Refused incoming call from " << remoteName << endl; - } - break; - case H323Connection::EndedByNoUser: - if (h323debug) { - cout << "-- Remote endpoint could not find user: " << remoteName << endl; - } - break; - case H323Connection::EndedByNoBandwidth: - if (h323debug) { - cout << "-- Call to " << remoteName << " aborted, insufficient bandwidth." << endl; - } - break; - case H323Connection::EndedByUnreachable: - if (h323debug) { - cout << "-- " << remoteName << " could not be reached." << endl; - } - break; - case H323Connection::EndedByHostOffline: - if (h323debug) { - cout << "-- " << remoteName << " is not online." << endl; - } - break; - case H323Connection::EndedByNoEndPoint: - if (h323debug) { - cout << "-- No phone running for " << remoteName << endl; - } - break; - case H323Connection::EndedByConnectFail: - if (h323debug) { - cout << "-- Transport error calling " << remoteName << endl; - } - break; - default: - if (h323debug) { -#ifdef PTRACING - cout << " -- Call with " << remoteName << " completed (" << connection.GetCallEndReason() << ")" << endl; -#else - cout << " -- Call with " << remoteName << " completed ([" << (int)connection.GetCallEndReason() << "])" << endl; -#endif - } - } - - if (connection.IsEstablished()) { - if (h323debug) { - cout << "\t-- Call duration " << setprecision(0) << setw(5) << (PTime() - connection.GetConnectionStartTime()) << endl; - } - } - /* Invoke the PBX application registered callback */ - on_connection_cleared(connection.GetCallReference(), clearedCallToken); - return; -} - -H323Connection * MyH323EndPoint::CreateConnection(unsigned callReference, void *userData, H323Transport *transport, H323SignalPDU *setupPDU) -{ - unsigned options = 0; - call_options_t *opts = (call_options_t *)userData; - MyH323Connection *conn; - - if (opts && opts->fastStart) { - options |= H323Connection::FastStartOptionEnable; - } else { - options |= H323Connection::FastStartOptionDisable; - } - if (opts && opts->h245Tunneling) { - options |= H323Connection::H245TunnelingOptionEnable; - } else { - options |= H323Connection::H245TunnelingOptionDisable; - } -/* Disable until I can figure out the proper way to deal with this */ -#if 0 - if (opts->silenceSuppression) { - options |= H323Connection::SilenceSuppresionOptionEnable; - } else { - options |= H323Connection::SilenceSUppressionOptionDisable; - } -#endif - conn = new MyH323Connection(*this, callReference, options); - if (conn) { - if (opts) - conn->SetCallOptions(opts, (setupPDU ? TRUE : FALSE)); - } - return conn; -} - -/* MyH323Connection Implementation */ -MyH323Connection::MyH323Connection(MyH323EndPoint & ep, unsigned callReference, - unsigned options) - : H323Connection(ep, callReference, options) -{ - cause = -1; - sessionId = 0; - bridging = FALSE; - progressSetup = progressAlert = 0; - dtmfMode = 0; - dtmfCodec = (RTP_DataFrame::PayloadTypes)0; - redirect_reason = -1; - transfer_capability = -1; -#ifdef TUNNELLING - tunnelOptions = remoteTunnelOptions = 0; -#endif - if (h323debug) { - cout << " == New H.323 Connection created." << endl; - } - return; -} - -MyH323Connection::~MyH323Connection() -{ - if (h323debug) { - cout << " == H.323 Connection deleted." << endl; - } - return; -} - -BOOL MyH323Connection::OnReceivedProgress(const H323SignalPDU &pdu) -{ - BOOL isInband; - unsigned pi; - - if (!H323Connection::OnReceivedProgress(pdu)) { - return FALSE; - } - - if (!pdu.GetQ931().GetProgressIndicator(pi)) - pi = 0; - if (h323debug) { - cout << "\t- Progress Indicator: " << pi << endl; - } - - switch(pi) { - case Q931::ProgressNotEndToEndISDN: - case Q931::ProgressInbandInformationAvailable: - isInband = TRUE; - break; - default: - isInband = FALSE; - } - on_progress(GetCallReference(), (const char *)GetCallToken(), isInband); - - return connectionState != ShuttingDownConnection; -} - -BOOL MyH323Connection::MySendProgress() -{ - /* The code taken from H323Connection::AnsweringCall() but ALWAYS send - PROGRESS message, including slow start operations */ - H323SignalPDU progressPDU; - H225_Progress_UUIE &prog = progressPDU.BuildProgress(*this); - - if (!mediaWaitForConnect) { - if (SendFastStartAcknowledge(prog.m_fastStart)) - prog.IncludeOptionalField(H225_Progress_UUIE::e_fastStart); - else { - if (connectionState == ShuttingDownConnection) - return FALSE; - - /* Do early H.245 start */ - earlyStart = TRUE; - if (!h245Tunneling) { - if (!H323Connection::StartControlChannel()) - return FALSE; - prog.IncludeOptionalField(H225_Progress_UUIE::e_h245Address); - controlChannel->SetUpTransportPDU(prog.m_h245Address, TRUE); - } - } - } - progressPDU.GetQ931().SetProgressIndicator(Q931::ProgressInbandInformationAvailable); - -#ifdef TUNNELLING - EmbedTunneledInfo(progressPDU); -#endif - HandleTunnelPDU(&progressPDU); - WriteSignalPDU(progressPDU); - - return TRUE; -} - -H323Connection::AnswerCallResponse MyH323Connection::OnAnswerCall(const PString & caller, - const H323SignalPDU & setupPDU, - H323SignalPDU & connectPDU) -{ - unsigned pi; - - if (h323debug) { - cout << "\t=-= In OnAnswerCall for call " << GetCallReference() << endl; - } - - if (connectionState == ShuttingDownConnection) - return H323Connection::AnswerCallDenied; - - if (!setupPDU.GetQ931().GetProgressIndicator(pi)) { - pi = 0; - } - if (h323debug) { - cout << "\t\t- Progress Indicator: " << pi << endl; - } - if (progressAlert) { - pi = progressAlert; - } else if (pi == Q931::ProgressOriginNotISDN) { - pi = Q931::ProgressInbandInformationAvailable; - } - if (pi && alertingPDU) { - alertingPDU->GetQ931().SetProgressIndicator(pi); - } - if (h323debug) { - cout << "\t\t- Inserting PI of " << pi << " into ALERTING message" << endl; - } - -#ifdef TUNNELLING - if (alertingPDU) - EmbedTunneledInfo(*alertingPDU); - EmbedTunneledInfo(connectPDU); -#endif - - if (!on_answer_call(GetCallReference(), (const char *)GetCallToken())) { - return H323Connection::AnswerCallDenied; - } - /* The call will be answered later with "AnsweringCall()" function. - */ - return ((pi || (fastStartState != FastStartDisabled)) ? AnswerCallDeferredWithMedia : AnswerCallDeferred); -} - -BOOL MyH323Connection::OnAlerting(const H323SignalPDU & alertingPDU, const PString & username) -{ - if (h323debug) { - cout << "\t=-= In OnAlerting for call " << GetCallReference() - << ": sessionId=" << sessionId << endl; - cout << "\t-- Ringing phone for \"" << username << "\"" << endl; - } - - if (on_progress) { - BOOL isInband; - unsigned alertingPI; - - if (!alertingPDU.GetQ931().GetProgressIndicator(alertingPI)) { - alertingPI = 0; - } - if (h323debug) { - cout << "\t\t- Progress Indicator: " << alertingPI << endl; - } - - switch(alertingPI) { - case Q931::ProgressNotEndToEndISDN: - case Q931::ProgressInbandInformationAvailable: - isInband = TRUE; - break; - default: - isInband = FALSE; - } - on_progress(GetCallReference(), (const char *)GetCallToken(), isInband); - } - on_chan_ringing(GetCallReference(), (const char *)GetCallToken() ); - return connectionState != ShuttingDownConnection; -} - -void MyH323Connection::SetCallOptions(void *o, BOOL isIncoming) -{ - call_options_t *opts = (call_options_t *)o; - - progressSetup = opts->progress_setup; - progressAlert = opts->progress_alert; - dtmfCodec = (RTP_DataFrame::PayloadTypes)opts->dtmfcodec; - dtmfMode = opts->dtmfmode; - - if (isIncoming) { - fastStartState = (opts->fastStart ? FastStartInitiate : FastStartDisabled); - h245Tunneling = (opts->h245Tunneling ? TRUE : FALSE); - } else { - sourceE164 = PString(opts->cid_num); - SetLocalPartyName(PString(opts->cid_name)); - SetDisplayName(PString(opts->cid_name)); - if (opts->redirect_reason >= 0) { - rdnis = PString(opts->cid_rdnis); - redirect_reason = opts->redirect_reason; - } - cid_presentation = opts->presentation; - cid_ton = opts->type_of_number; - if (opts->transfer_capability >= 0) { - transfer_capability = opts->transfer_capability; - } - } - tunnelOptions = opts->tunnelOptions; -} - -void MyH323Connection::SetCallDetails(void *callDetails, const H323SignalPDU &setupPDU, BOOL isIncoming) -{ - PString sourceE164; - PString destE164; - PString sourceAliases; - PString destAliases; - char *s, *s1; - call_details_t *cd = (call_details_t *)callDetails; - - memset(cd, 0, sizeof(*cd)); - cd->call_reference = GetCallReference(); - cd->call_token = strdup((const char *)GetCallToken()); - - sourceE164 = ""; - setupPDU.GetSourceE164(sourceE164); - cd->call_source_e164 = strdup((const char *)sourceE164); - - destE164 = ""; - setupPDU.GetDestinationE164(destE164); - cd->call_dest_e164 = strdup((const char *)destE164); - - /* XXX Is it possible to have this information for outgoing calls too? XXX */ - if (isIncoming) { - PString sourceName; - PIPSocket::Address Ip; - WORD sourcePort; - PString redirect_number; - unsigned redirect_reason; - unsigned plan, type, screening, presentation; - Q931::InformationTransferCapability capability; - unsigned transferRate, codingStandard, userInfoLayer1; - - /* Fetch presentation and type information about calling party's number */ - if (setupPDU.GetQ931().GetCallingPartyNumber(sourceName, &plan, &type, &presentation, &screening, 0, 0)) { - /* Construct fields back */ - cd->type_of_number = (type << 4) | plan; - cd->presentation = (presentation << 5) | screening; - } else if (cd->call_source_e164[0]) { - cd->type_of_number = 0; /* UNKNOWN */ - cd->presentation = 0x03; /* ALLOWED NETWORK NUMBER - Default */ - if (setupPDU.GetQ931().HasIE(Q931::UserUserIE)) { - const H225_Setup_UUIE &setup_uuie = setupPDU.m_h323_uu_pdu.m_h323_message_body; - if (setup_uuie.HasOptionalField(H225_Setup_UUIE::e_presentationIndicator)) - cd->presentation = (cd->presentation & 0x9f) | (((unsigned int)setup_uuie.m_presentationIndicator.GetTag()) << 5); - if (setup_uuie.HasOptionalField(H225_Setup_UUIE::e_screeningIndicator)) - cd->presentation = (cd->presentation & 0xe0) | (((unsigned int)setup_uuie.m_screeningIndicator.GetValue()) & 0x1f); - } - } else { - cd->type_of_number = 0; /* UNKNOWN */ - cd->presentation = 0x43; /* NUMBER NOT AVAILABLE */ - } - - sourceName = setupPDU.GetQ931().GetDisplayName(); - cd->call_source_name = strdup((const char *)sourceName); - - GetSignallingChannel()->GetRemoteAddress().GetIpAndPort(Ip, sourcePort); - cd->sourceIp = strdup((const char *)Ip.AsString()); - - if (setupPDU.GetQ931().GetRedirectingNumber(redirect_number, NULL, NULL, NULL, NULL, &redirect_reason, 0, 0, 0)) { - cd->redirect_number = strdup((const char *)redirect_number); - cd->redirect_reason = redirect_reason; - } - else - cd->redirect_reason = -1; - - /* Fetch Q.931's transfer capability */ - if (((Q931 &)setupPDU.GetQ931()).GetBearerCapabilities(capability, transferRate, &codingStandard, &userInfoLayer1)) - cd->transfer_capability = ((unsigned int)capability & 0x1f) | (codingStandard << 5); - else - cd->transfer_capability = 0x00; /* ITU coding of Speech */ - - /* Don't show local username as called party name */ - SetDisplayName(cd->call_dest_e164); - } - - /* Convert complex strings */ - // FIXME: deal more than one source alias - sourceAliases = setupPDU.GetSourceAliases(); - s1 = strdup((const char *)sourceAliases); - if ((s = strchr(s1, ' ')) != NULL) - *s = '\0'; - if ((s = strchr(s1, '\t')) != NULL) - *s = '\0'; - cd->call_source_aliases = s1; - - destAliases = setupPDU.GetDestinationAlias(); - s1 = strdup((const char *)destAliases); - if ((s = strchr(s1, ' ')) != NULL) - *s = '\0'; - if ((s = strchr(s1, '\t')) != NULL) - *s = '\0'; - cd->call_dest_alias = s1; -} - -#ifdef TUNNELLING -static BOOL FetchInformationElements(Q931 &q931, const PBYTEArray &data) -{ - PINDEX offset = 0; - - while (offset < data.GetSize()) { - // Get field discriminator - int discriminator = data[offset++]; - -#if 0 - /* Do not overwrite existing IEs */ - if (q931.HasIE((Q931::InformationElementCodes)discriminator)) { - if ((discriminatir & 0x80) == 0) - offset += data[offset++]; - if (offset > data.GetSize()) - return FALSE; - continue; - } -#endif - - PBYTEArray * item = new PBYTEArray; - - // For discriminator with high bit set there is no data - if ((discriminator & 0x80) == 0) { - int len = data[offset++]; - -#if 0 // That is not H.225 but regular Q.931 (ISDN) IEs - if (discriminator == UserUserIE) { - // Special case of User-user field. See 7.2.2.31/H.225.0v4. - len <<= 8; - len |= data[offset++]; - - // we also have a protocol discriminator, which we ignore - offset++; - - // before decrementing the length, make sure it is not zero - if (len == 0) - return FALSE; - - // adjust for protocol discriminator - len--; - } -#endif - - if (offset + len > data.GetSize()) { - delete item; - return FALSE; - } - - memcpy(item->GetPointer(len), (const BYTE *)data+offset, len); - offset += len; - } - - q931.SetIE((Q931::InformationElementCodes)discriminator, *item); - delete item; - } - return TRUE; -} - -static BOOL FetchCiscoTunneledInfo(Q931 &q931, const H323SignalPDU &pdu) -{ - BOOL res = FALSE; - const H225_H323_UU_PDU &uuPDU = pdu.m_h323_uu_pdu; - - if(uuPDU.HasOptionalField(H225_H323_UU_PDU::e_nonStandardControl)) { - for(int i = 0; i < uuPDU.m_nonStandardControl.GetSize(); ++i) { - const H225_NonStandardParameter &np = uuPDU.m_nonStandardControl[i]; - const H225_NonStandardIdentifier &id = np.m_nonStandardIdentifier; - if (id.GetTag() == H225_NonStandardIdentifier::e_h221NonStandard) { - const H225_H221NonStandard &ni = id; - /* Check for Cisco */ - if ((ni.m_t35CountryCode == 181) && (ni.m_t35Extension == 0) && (ni.m_manufacturerCode == 18)) { - const PBYTEArray &data = np.m_data; - if (h323debug) - cout << setprecision(0) << "Received non-standard Cisco extension data " << np.m_data << endl; - CISCO_H225_H323_UU_NonStdInfo c; - PPER_Stream strm(data); - if (c.Decode(strm)) { - BOOL haveIEs = FALSE; - if (h323debug) - cout << setprecision(0) << "H323_UU_NonStdInfo = " << c << endl; - if (c.HasOptionalField(CISCO_H225_H323_UU_NonStdInfo::e_protoParam)) { - FetchInformationElements(q931, c.m_protoParam.m_qsigNonStdInfo.m_rawMesg); - haveIEs = TRUE; - } - if (c.HasOptionalField(CISCO_H225_H323_UU_NonStdInfo::e_commonParam)) { - FetchInformationElements(q931, c.m_commonParam.m_redirectIEinfo.m_redirectIE); - haveIEs = TRUE; - } - if (haveIEs && h323debug) - cout << setprecision(0) << "Information elements collected:" << q931 << endl; - res = TRUE; - } else { - cout << "ERROR while decoding non-standard Cisco extension" << endl; - return FALSE; - } - } - } - } - } - return res; -} - -static BOOL EmbedCiscoTunneledInfo(H323SignalPDU &pdu) -{ - const static struct { - Q931::InformationElementCodes ie; - BOOL dontDelete; - } codes[] = { - { Q931::RedirectingNumberIE, }, - { Q931::FacilityIE, }, -// { Q931::CallingPartyNumberIE, TRUE }, - }; - - BOOL res = FALSE; - BOOL notRedirOnly = FALSE; - Q931 tmpQ931; - Q931 &q931 = pdu.GetQ931(); - - for(unsigned i = 0; i < (sizeof(codes) / sizeof(codes[0])); ++i) { - if (q931.HasIE(codes[i].ie)) { - tmpQ931.SetIE(codes[i].ie, q931.GetIE(codes[i].ie)); - if (!codes[i].dontDelete) - q931.RemoveIE(codes[i].ie); - if (codes[i].ie != Q931::RedirectingNumberIE) - notRedirOnly = TRUE; - res = TRUE; - } - } - /* Have something to embed */ - if (res) { - PBYTEArray msg; - if (!tmpQ931.Encode(msg)) - return FALSE; - PBYTEArray ies(msg.GetPointer() + 5, msg.GetSize() - 5); - - H225_H323_UU_PDU &uuPDU = pdu.m_h323_uu_pdu; - if(!uuPDU.HasOptionalField(H225_H323_UU_PDU::e_nonStandardControl)) { - uuPDU.IncludeOptionalField(H225_H323_UU_PDU::e_nonStandardControl); - uuPDU.m_nonStandardControl.SetSize(0); - } - H225_NonStandardParameter *np = new H225_NonStandardParameter; - uuPDU.m_nonStandardControl.Append(np); - H225_NonStandardIdentifier &nsi = (*np).m_nonStandardIdentifier; - nsi.SetTag(H225_NonStandardIdentifier::e_h221NonStandard); - H225_H221NonStandard &ns = nsi; - ns.m_t35CountryCode = 181; - ns.m_t35Extension = 0; - ns.m_manufacturerCode = 18; - - CISCO_H225_H323_UU_NonStdInfo c; - c.IncludeOptionalField(CISCO_H225_H323_UU_NonStdInfo::e_version); - c.m_version = 0; - - if (notRedirOnly) { - c.IncludeOptionalField(CISCO_H225_H323_UU_NonStdInfo::e_protoParam); - CISCO_H225_QsigNonStdInfo &qsigInfo = c.m_protoParam.m_qsigNonStdInfo; - qsigInfo.m_iei = ies[0]; - qsigInfo.m_rawMesg = ies; - } else { - c.IncludeOptionalField(CISCO_H225_H323_UU_NonStdInfo::e_commonParam); - c.m_commonParam.m_redirectIEinfo.m_redirectIE = ies; - } - PPER_Stream stream; - c.Encode(stream); - stream.CompleteEncoding(); - (*np).m_data = stream; - } - return res; -} - -static const char OID_QSIG[] = "1.3.12.9"; - -static BOOL FetchQSIGTunneledInfo(Q931 &q931, const H323SignalPDU &pdu) -{ - BOOL res = FALSE; - const H225_H323_UU_PDU &uuPDU = pdu.m_h323_uu_pdu; - if (uuPDU.HasOptionalField(H225_H323_UU_PDU::e_tunnelledSignallingMessage)) { - const H225_H323_UU_PDU_tunnelledSignallingMessage &sig = uuPDU.m_tunnelledSignallingMessage; - const H225_TunnelledProtocol_id &proto = sig.m_tunnelledProtocolID.m_id; - if ((proto.GetTag() == H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID) && - (((const PASN_ObjectId &)proto).AsString() == OID_QSIG)) { - const H225_ArrayOf_PASN_OctetString &sigs = sig.m_messageContent; - for(int i = 0; i < sigs.GetSize(); ++i) { - const PASN_OctetString &msg = sigs[i]; - if (h323debug) - cout << setprecision(0) << "Q.931 message data is " << msg << endl; - if(!q931.Decode((const PBYTEArray &)msg)) { - cout << "Error while decoding Q.931 message" << endl; - return FALSE; - } - res = TRUE; - if (h323debug) - cout << setprecision(0) << "Received QSIG message " << q931 << endl; - } - } - } - return res; -} - -static H225_EndpointType *GetEndpointType(H323SignalPDU &pdu) -{ - if (!pdu.GetQ931().HasIE(Q931::UserUserIE)) - return NULL; - - H225_H323_UU_PDU_h323_message_body &body = pdu.m_h323_uu_pdu.m_h323_message_body; - switch (body.GetTag()) { - case H225_H323_UU_PDU_h323_message_body::e_setup: - return &((H225_Setup_UUIE &)body).m_sourceInfo; - case H225_H323_UU_PDU_h323_message_body::e_callProceeding: - return &((H225_CallProceeding_UUIE &)body).m_destinationInfo; - case H225_H323_UU_PDU_h323_message_body::e_connect: - return &((H225_Connect_UUIE &)body).m_destinationInfo; - case H225_H323_UU_PDU_h323_message_body::e_alerting: - return &((H225_Alerting_UUIE &)body).m_destinationInfo; - case H225_H323_UU_PDU_h323_message_body::e_facility: - return &((H225_Facility_UUIE &)body).m_destinationInfo; - case H225_H323_UU_PDU_h323_message_body::e_progress: - return &((H225_Progress_UUIE &)body).m_destinationInfo; - } - return NULL; -} - -static BOOL QSIGTunnelRequested(H323SignalPDU &pdu) -{ - H225_EndpointType *epType = GetEndpointType(pdu); - if (epType) { - if (!(*epType).HasOptionalField(H225_EndpointType::e_supportedTunnelledProtocols)) { - return FALSE; - } - H225_ArrayOf_TunnelledProtocol &protos = (*epType).m_supportedTunnelledProtocols; - for (int i = 0; i < protos.GetSize(); ++i) - { - if ((protos[i].GetTag() == H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID) && - (((const PASN_ObjectId &)protos[i]).AsString() == OID_QSIG)) { - return TRUE; - } - } - } - return FALSE; -} - -static BOOL EmbedQSIGTunneledInfo(H323SignalPDU &pdu) -{ - const static Q931::InformationElementCodes codes[] = - { Q931::RedirectingNumberIE, Q931::FacilityIE }; - - Q931 &q931 = pdu.GetQ931(); - PBYTEArray message; - - q931.Encode(message); - - /* Remove non-standard IEs */ - for(unsigned i = 0; i < (sizeof(codes) / sizeof(codes[0])); ++i) { - if (q931.HasIE(codes[i])) { - q931.RemoveIE(codes[i]); - } - } - - H225_H323_UU_PDU &uuPDU = pdu.m_h323_uu_pdu; - H225_EndpointType *epType = GetEndpointType(pdu); - if (epType) { - if (!(*epType).HasOptionalField(H225_EndpointType::e_supportedTunnelledProtocols)) { - (*epType).IncludeOptionalField(H225_EndpointType::e_supportedTunnelledProtocols); - (*epType).m_supportedTunnelledProtocols.SetSize(0); - } - H225_ArrayOf_TunnelledProtocol &protos = (*epType).m_supportedTunnelledProtocols; - BOOL addQSIG = TRUE; - for (int i = 0; i < protos.GetSize(); ++i) - { - if ((protos[i].GetTag() == H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID) && - (((PASN_ObjectId &)protos[i]).AsString() == OID_QSIG)) { - addQSIG = FALSE; - break; - } - } - if (addQSIG) { - H225_TunnelledProtocol *proto = new H225_TunnelledProtocol; - (*proto).m_id.SetTag(H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID); - (PASN_ObjectId &)(proto->m_id) = OID_QSIG; - protos.Append(proto); - } - } - if (!uuPDU.HasOptionalField(H225_H323_UU_PDU::e_tunnelledSignallingMessage)) - uuPDU.IncludeOptionalField(H225_H323_UU_PDU::e_tunnelledSignallingMessage); - H225_H323_UU_PDU_tunnelledSignallingMessage &sig = uuPDU.m_tunnelledSignallingMessage; - H225_TunnelledProtocol_id &proto = sig.m_tunnelledProtocolID.m_id; - if ((proto.GetTag() != H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID) || - (((const PASN_ObjectId &)proto).AsString() != OID_QSIG)) { - proto.SetTag(H225_TunnelledProtocol_id::e_tunnelledProtocolObjectID); - (PASN_ObjectId &)proto = OID_QSIG; - sig.m_messageContent.SetSize(0); - } - PASN_OctetString *msg = new PASN_OctetString; - sig.m_messageContent.Append(msg); - *msg = message; - return TRUE; -} - -BOOL MyH323Connection::EmbedTunneledInfo(H323SignalPDU &pdu) -{ - if ((tunnelOptions & H323_TUNNEL_QSIG) || (remoteTunnelOptions & H323_TUNNEL_QSIG)) - EmbedQSIGTunneledInfo(pdu); - if ((tunnelOptions & H323_TUNNEL_CISCO) || (remoteTunnelOptions & H323_TUNNEL_CISCO)) - EmbedCiscoTunneledInfo(pdu); - - return TRUE; -} - -/* Handle tunneled messages */ -BOOL MyH323Connection::HandleSignalPDU(H323SignalPDU &pdu) -{ - if (pdu.GetQ931().HasIE(Q931::UserUserIE)) { - Q931 tunneledInfo; - const Q931 *q931Info; - - q931Info = NULL; - if (FetchCiscoTunneledInfo(tunneledInfo, pdu)) { - q931Info = &tunneledInfo; - remoteTunnelOptions |= H323_TUNNEL_CISCO; - } - if (FetchQSIGTunneledInfo(tunneledInfo, pdu)) { - q931Info = &tunneledInfo; - remoteTunnelOptions |= H323_TUNNEL_QSIG; - } - if (!(remoteTunnelOptions & H323_TUNNEL_QSIG) && QSIGTunnelRequested(pdu)) { - remoteTunnelOptions |= H323_TUNNEL_QSIG; - } - if (q931Info) { - if (q931Info->HasIE(Q931::RedirectingNumberIE)) { - pdu.GetQ931().SetIE(Q931::RedirectingNumberIE, q931Info->GetIE(Q931::RedirectingNumberIE)); - if (h323debug) { - PString number; - unsigned reason; - if(q931Info->GetRedirectingNumber(number, NULL, NULL, NULL, NULL, &reason, 0, 0, 0)) - cout << "Got redirection from " << number << ", reason " << reason << endl; - } - } - } - } - - return H323Connection::HandleSignalPDU(pdu); -} -#endif - -BOOL MyH323Connection::OnReceivedSignalSetup(const H323SignalPDU & setupPDU) -{ - call_details_t cd; - - if (h323debug) { - cout << "\t--Received SETUP message" << endl; - } - - if (connectionState == ShuttingDownConnection) - return FALSE; - - SetCallDetails(&cd, setupPDU, TRUE); - - /* Notify Asterisk of the request */ - call_options_t *res = on_incoming_call(&cd); - - if (!res) { - if (h323debug) { - cout << "\t-- Call Failed" << endl; - } - return FALSE; - } - - SetCallOptions(res, TRUE); - - /* Disable fastStart if requested by remote side */ - if (h245Tunneling && !setupPDU.m_h323_uu_pdu.m_h245Tunneling) { - masterSlaveDeterminationProcedure->Stop(); - capabilityExchangeProcedure->Stop(); - PTRACE(3, "H225\tFast Start DISABLED!"); - h245Tunneling = FALSE; - } - - return H323Connection::OnReceivedSignalSetup(setupPDU); -} - -BOOL MyH323Connection::OnSendSignalSetup(H323SignalPDU & setupPDU) -{ - call_details_t cd; - - if (h323debug) { - cout << "\t-- Sending SETUP message" << endl; - } - - if (connectionState == ShuttingDownConnection) - return FALSE; - - if (progressSetup) - setupPDU.GetQ931().SetProgressIndicator(progressSetup); - - if (redirect_reason >= 0) { - setupPDU.GetQ931().SetRedirectingNumber(rdnis, 0, 0, 0, 0, redirect_reason); - /* OpenH323 incorrectly fills number IE when redirecting reason is specified - fix it */ - PBYTEArray IE(setupPDU.GetQ931().GetIE(Q931::RedirectingNumberIE)); - IE[0] = IE[0] & 0x7f; - IE[1] = IE[1] & 0x7f; - setupPDU.GetQ931().SetIE(Q931::RedirectingNumberIE, IE); - } - - if (transfer_capability) - setupPDU.GetQ931().SetBearerCapabilities((Q931::InformationTransferCapability)(transfer_capability & 0x1f), 1, ((transfer_capability >> 5) & 3)); - - SetCallDetails(&cd, setupPDU, FALSE); - - int res = on_outgoing_call(&cd); - if (!res) { - if (h323debug) { - cout << "\t-- Call Failed" << endl; - } - return FALSE; - } - - /* OpenH323 will build calling party information with default - type and presentation information, so build it to be recorded - by embedding routines */ - setupPDU.GetQ931().SetCallingPartyNumber(sourceE164, (cid_ton >> 4) & 0x07, - cid_ton & 0x0f, (cid_presentation >> 5) & 0x03, cid_presentation & 0x1f); - setupPDU.GetQ931().SetDisplayName(GetDisplayName()); - -#ifdef TUNNELLING - EmbedTunneledInfo(setupPDU); -#endif - - return H323Connection::OnSendSignalSetup(setupPDU); -} - -static BOOL BuildFastStartList(const H323Channel & channel, - H225_ArrayOf_PASN_OctetString & array, - H323Channel::Directions reverseDirection) -{ - H245_OpenLogicalChannel open; - const H323Capability & capability = channel.GetCapability(); - - if (channel.GetDirection() != reverseDirection) { - if (!capability.OnSendingPDU(open.m_forwardLogicalChannelParameters.m_dataType)) - return FALSE; - } - else { - if (!capability.OnSendingPDU(open.m_reverseLogicalChannelParameters.m_dataType)) - return FALSE; - - open.m_forwardLogicalChannelParameters.m_multiplexParameters.SetTag( - H245_OpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters::e_none); - open.m_forwardLogicalChannelParameters.m_dataType.SetTag(H245_DataType::e_nullData); - open.IncludeOptionalField(H245_OpenLogicalChannel::e_reverseLogicalChannelParameters); - } - - if (!channel.OnSendingPDU(open)) - return FALSE; - - PTRACE(4, "H225\tBuild fastStart:\n " << setprecision(2) << open); - PINDEX last = array.GetSize(); - array.SetSize(last+1); - array[last].EncodeSubType(open); - - PTRACE(3, "H225\tBuilt fastStart for " << capability); - return TRUE; -} - -H323Connection::CallEndReason MyH323Connection::SendSignalSetup(const PString & alias, - const H323TransportAddress & address) -{ - // Start the call, first state is asking gatekeeper - connectionState = AwaitingGatekeeperAdmission; - - // Indicate the direction of call. - if (alias.IsEmpty()) - remotePartyName = remotePartyAddress = address; - else { - remotePartyName = alias; - remotePartyAddress = alias + '@' + address; - } - - // Start building the setup PDU to get various ID's - H323SignalPDU setupPDU; - H225_Setup_UUIE & setup = setupPDU.BuildSetup(*this, address); - -#ifdef H323_H450 - h450dispatcher->AttachToSetup(setupPDU); -#endif - - // Save the identifiers generated by BuildSetup - setupPDU.GetQ931().GetCalledPartyNumber(remotePartyNumber); - - H323TransportAddress gatekeeperRoute = address; - - // Check for gatekeeper and do admission check if have one - H323Gatekeeper * gatekeeper = endpoint.GetGatekeeper(); - H225_ArrayOf_AliasAddress newAliasAddresses; - if (gatekeeper != NULL) { - H323Gatekeeper::AdmissionResponse response; - response.transportAddress = &gatekeeperRoute; - response.aliasAddresses = &newAliasAddresses; - if (!gkAccessTokenOID) - response.accessTokenData = &gkAccessTokenData; - while (!gatekeeper->AdmissionRequest(*this, response, alias.IsEmpty())) { - PTRACE(1, "H225\tGatekeeper refused admission: " - << (response.rejectReason == UINT_MAX - ? PString("Transport error") - : H225_AdmissionRejectReason(response.rejectReason).GetTagName())); -#ifdef H323_H450 - h4502handler->onReceivedAdmissionReject(H4501_GeneralErrorList::e_notAvailable); -#endif - - switch (response.rejectReason) { - case H225_AdmissionRejectReason::e_calledPartyNotRegistered: - return EndedByNoUser; - case H225_AdmissionRejectReason::e_requestDenied: - return EndedByNoBandwidth; - case H225_AdmissionRejectReason::e_invalidPermission: - case H225_AdmissionRejectReason::e_securityDenial: - return EndedBySecurityDenial; - case H225_AdmissionRejectReason::e_resourceUnavailable: - return EndedByRemoteBusy; - case H225_AdmissionRejectReason::e_incompleteAddress: - if (OnInsufficientDigits()) - break; - // Then default case - default: - return EndedByGatekeeper; - } - - PString lastRemotePartyName = remotePartyName; - while (lastRemotePartyName == remotePartyName) { - Unlock(); // Release the mutex as can deadlock trying to clear call during connect. - digitsWaitFlag.Wait(); - if (!Lock()) // Lock while checking for shutting down. - return EndedByCallerAbort; - } - } - mustSendDRQ = TRUE; - if (response.gatekeeperRouted) { - setup.IncludeOptionalField(H225_Setup_UUIE::e_endpointIdentifier); - setup.m_endpointIdentifier = gatekeeper->GetEndpointIdentifier(); - gatekeeperRouted = TRUE; - } - } - -#ifdef H323_TRANSNEXUS_OSP - // check for OSP server (if not using GK) - if (gatekeeper == NULL) { - OpalOSP::Provider * ospProvider = endpoint.GetOSPProvider(); - if (ospProvider != NULL) { - OpalOSP::Transaction * transaction = new OpalOSP::Transaction(); - if (transaction->Open(*ospProvider) != 0) { - PTRACE(1, "H225\tCannot create OSP transaction"); - return EndedByOSPRefusal; - } - - OpalOSP::Transaction::DestinationInfo destInfo; - if (!AuthoriseOSPTransaction(*transaction, destInfo)) { - delete transaction; - return EndedByOSPRefusal; - } - - // save the transaction for use by the call - ospTransaction = transaction; - - // retreive the call information - gatekeeperRoute = destInfo.destinationAddress; - newAliasAddresses.Append(new H225_AliasAddress(destInfo.calledNumber)); - - // insert the token - setup.IncludeOptionalField(H225_Setup_UUIE::e_tokens); - destInfo.InsertToken(setup.m_tokens); - } - } -#endif - - // Update the field e_destinationAddress in the SETUP PDU to reflect the new - // alias received in the ACF (m_destinationInfo). - if (newAliasAddresses.GetSize() > 0) { - setup.IncludeOptionalField(H225_Setup_UUIE::e_destinationAddress); - setup.m_destinationAddress = newAliasAddresses; - - // Update the Q.931 Information Element (if is an E.164 address) - PString e164 = H323GetAliasAddressE164(newAliasAddresses); - if (!e164) - remotePartyNumber = e164; - } - - if (addAccessTokenToSetup && !gkAccessTokenOID && !gkAccessTokenData.IsEmpty()) { - PString oid1, oid2; - PINDEX comma = gkAccessTokenOID.Find(','); - if (comma == P_MAX_INDEX) - oid1 = oid2 = gkAccessTokenOID; - else { - oid1 = gkAccessTokenOID.Left(comma); - oid2 = gkAccessTokenOID.Mid(comma+1); - } - setup.IncludeOptionalField(H225_Setup_UUIE::e_tokens); - PINDEX last = setup.m_tokens.GetSize(); - setup.m_tokens.SetSize(last+1); - setup.m_tokens[last].m_tokenOID = oid1; - setup.m_tokens[last].IncludeOptionalField(H235_ClearToken::e_nonStandard); - setup.m_tokens[last].m_nonStandard.m_nonStandardIdentifier = oid2; - setup.m_tokens[last].m_nonStandard.m_data = gkAccessTokenData; - } - - if (!signallingChannel->SetRemoteAddress(gatekeeperRoute)) { - PTRACE(1, "H225\tInvalid " - << (gatekeeperRoute != address ? "gatekeeper" : "user") - << " supplied address: \"" << gatekeeperRoute << '"'); - connectionState = AwaitingTransportConnect; - return EndedByConnectFail; - } - - // Do the transport connect - connectionState = AwaitingTransportConnect; - - // Release the mutex as can deadlock trying to clear call during connect. - Unlock(); - - signallingChannel->SetWriteTimeout(100); - - BOOL connectFailed = !signallingChannel->Connect(); - - // Lock while checking for shutting down. - if (!Lock()) - return EndedByCallerAbort; - - // See if transport connect failed, abort if so. - if (connectFailed) { - connectionState = NoConnectionActive; - switch (signallingChannel->GetErrorNumber()) { - case ENETUNREACH : - return EndedByUnreachable; - case ECONNREFUSED : - return EndedByNoEndPoint; - case ETIMEDOUT : - return EndedByHostOffline; - } - return EndedByConnectFail; - } - - PTRACE(3, "H225\tSending Setup PDU"); - connectionState = AwaitingSignalConnect; - - // Put in all the signalling addresses for link - setup.IncludeOptionalField(H225_Setup_UUIE::e_sourceCallSignalAddress); - signallingChannel->SetUpTransportPDU(setup.m_sourceCallSignalAddress, TRUE); - if (!setup.HasOptionalField(H225_Setup_UUIE::e_destCallSignalAddress)) { - setup.IncludeOptionalField(H225_Setup_UUIE::e_destCallSignalAddress); - signallingChannel->SetUpTransportPDU(setup.m_destCallSignalAddress, FALSE); - } - - // If a standard call do Fast Start (if required) - if (setup.m_conferenceGoal.GetTag() == H225_Setup_UUIE_conferenceGoal::e_create) { - - // Get the local capabilities before fast start is handled - OnSetLocalCapabilities(); - - // Ask the application what channels to open - PTRACE(3, "H225\tCheck for Fast start by local endpoint"); - fastStartChannels.RemoveAll(); - OnSelectLogicalChannels(); - - // If application called OpenLogicalChannel, put in the fastStart field - if (!fastStartChannels.IsEmpty()) { - PTRACE(3, "H225\tFast start begun by local endpoint"); - for (PINDEX i = 0; i < fastStartChannels.GetSize(); i++) - BuildFastStartList(fastStartChannels[i], setup.m_fastStart, H323Channel::IsReceiver); - if (setup.m_fastStart.GetSize() > 0) - setup.IncludeOptionalField(H225_Setup_UUIE::e_fastStart); - } - - // Search the capability set and see if we have video capability - for (PINDEX i = 0; i < localCapabilities.GetSize(); i++) { - switch (localCapabilities[i].GetMainType()) { - case H323Capability::e_Audio: - case H323Capability::e_UserInput: - break; - - default: // Is video or other data (eg T.120) - setupPDU.GetQ931().SetBearerCapabilities(Q931::TransferUnrestrictedDigital, 6); - i = localCapabilities.GetSize(); // Break out of the for loop - break; - } - } - } - - if (!OnSendSignalSetup(setupPDU)) - return EndedByNoAccept; - - // Do this again (was done when PDU was constructed) in case - // OnSendSignalSetup() changed something. -// setupPDU.SetQ931Fields(*this, TRUE); - setupPDU.GetQ931().GetCalledPartyNumber(remotePartyNumber); - - fastStartState = FastStartDisabled; - BOOL set_lastPDUWasH245inSETUP = FALSE; - - if (h245Tunneling && doH245inSETUP) { - h245TunnelTxPDU = &setupPDU; - - // Try and start the master/slave and capability exchange through the tunnel - // Note: this used to be disallowed but is now allowed as of H323v4 - BOOL ok = StartControlNegotiations(); - - h245TunnelTxPDU = NULL; - - if (!ok) - return EndedByTransportFail; - - if (setup.m_fastStart.GetSize() > 0) { - // Now if fast start as well need to put this in setup specific field - // and not the generic H.245 tunneling field - setup.IncludeOptionalField(H225_Setup_UUIE::e_parallelH245Control); - setup.m_parallelH245Control = setupPDU.m_h323_uu_pdu.m_h245Control; - setupPDU.m_h323_uu_pdu.RemoveOptionalField(H225_H323_UU_PDU::e_h245Control); - set_lastPDUWasH245inSETUP = TRUE; - } - } - - // Send the initial PDU - setupTime = PTime(); - if (!WriteSignalPDU(setupPDU)) - return EndedByTransportFail; - - // WriteSignalPDU always resets lastPDUWasH245inSETUP. - // So set it here if required - if (set_lastPDUWasH245inSETUP) - lastPDUWasH245inSETUP = TRUE; - - // Set timeout for remote party to answer the call - signallingChannel->SetReadTimeout(endpoint.GetSignallingChannelCallTimeout()); - - return NumCallEndReasons; -} - - -BOOL MyH323Connection::OnSendReleaseComplete(H323SignalPDU & releaseCompletePDU) -{ - if (h323debug) { - cout << "\t-- Sending RELEASE COMPLETE" << endl; - } - if (cause > 0) - releaseCompletePDU.GetQ931().SetCause((Q931::CauseValues)cause); - -#ifdef TUNNELLING - EmbedTunneledInfo(releaseCompletePDU); -#endif - - return H323Connection::OnSendReleaseComplete(releaseCompletePDU); -} - -BOOL MyH323Connection::OnReceivedFacility(const H323SignalPDU & pdu) -{ - if (h323debug) { - cout << "\t-- Received Facility message... " << endl; - } - return H323Connection::OnReceivedFacility(pdu); -} - -void MyH323Connection::OnReceivedReleaseComplete(const H323SignalPDU & pdu) -{ - if (h323debug) { - cout << "\t-- Received RELEASE COMPLETE message..." << endl; - } - if (on_hangup) - on_hangup(GetCallReference(), (const char *)GetCallToken(), pdu.GetQ931().GetCause()); - return H323Connection::OnReceivedReleaseComplete(pdu); -} - -BOOL MyH323Connection::OnClosingLogicalChannel(H323Channel & channel) -{ - if (h323debug) { - cout << "\t-- Closing logical channel..." << endl; - } - return H323Connection::OnClosingLogicalChannel(channel); -} - -void MyH323Connection::SendUserInputTone(char tone, unsigned duration, unsigned logicalChannel, unsigned rtpTimestamp) -{ - SendUserInputModes mode = GetRealSendUserInputMode(); -// That is recursive call... Why? -// on_receive_digit(GetCallReference(), tone, (const char *)GetCallToken()); - if ((tone != ' ') || (mode == SendUserInputAsTone) || (mode == SendUserInputAsInlineRFC2833)) { - if (h323debug) { - cout << "\t-- Sending user input tone (" << tone << ") to remote" << endl; - } - H323Connection::SendUserInputTone(tone, duration); - } -} - -void MyH323Connection::OnUserInputTone(char tone, unsigned duration, unsigned logicalChannel, unsigned rtpTimestamp) -{ - if (dtmfMode == H323_DTMF_RFC2833) { - if (h323debug) { - cout << "\t-- Received user input tone (" << tone << ") from remote" << endl; - } - on_receive_digit(GetCallReference(), tone, (const char *)GetCallToken(), duration); - } -} - -void MyH323Connection::OnUserInputString(const PString &value) -{ - if (h323debug) { - cout << "\t-- Received user input string (" << value << ") from remote." << endl; - } - on_receive_digit(GetCallReference(), value[0], (const char *)GetCallToken(), 0); -} - -void MyH323Connection::OnSendCapabilitySet(H245_TerminalCapabilitySet & pdu) -{ - PINDEX i; - - H323Connection::OnSendCapabilitySet(pdu); - - H245_ArrayOf_CapabilityTableEntry & tables = pdu.m_capabilityTable; - for(i = 0; i < tables.GetSize(); i++) - { - H245_CapabilityTableEntry & entry = tables[i]; - if (entry.HasOptionalField(H245_CapabilityTableEntry::e_capability)) { - H245_Capability & cap = entry.m_capability; - if (cap.GetTag() == H245_Capability::e_receiveRTPAudioTelephonyEventCapability) { - H245_AudioTelephonyEventCapability & atec = cap; - atec.m_dynamicRTPPayloadType = dtmfCodec; -// on_set_rfc2833_payload(GetCallReference(), (const char *)GetCallToken(), (int)dtmfCodec); -#ifdef PTRACING - if (h323debug) { - cout << "\t-- Transmitting RFC2833 on payload " << - atec.m_dynamicRTPPayloadType << endl; - } -#endif - } - } - } -} - -void MyH323Connection::OnSetLocalCapabilities() -{ - if (on_setcapabilities) - on_setcapabilities(GetCallReference(), (const char *)callToken); -} - -BOOL MyH323Connection::OnReceivedCapabilitySet(const H323Capabilities & remoteCaps, - const H245_MultiplexCapability * muxCap, - H245_TerminalCapabilitySetReject & reject) -{ - struct __codec__ { - unsigned int asterisk_codec; - unsigned int h245_cap; - const char *oid; - }; - static const struct __codec__ codecs[] = { - { AST_FORMAT_G723_1, H245_AudioCapability::e_g7231 }, - { AST_FORMAT_GSM, H245_AudioCapability::e_gsmFullRate }, - { AST_FORMAT_ULAW, H245_AudioCapability::e_g711Ulaw64k }, - { AST_FORMAT_ALAW, H245_AudioCapability::e_g711Alaw64k }, - { AST_FORMAT_G729A, H245_AudioCapability::e_g729AnnexA }, - { AST_FORMAT_G729A, H245_AudioCapability::e_g729 }, -#ifdef AST_FORMAT_MODEM - { AST_FORMAT_MODEM, H245_DataApplicationCapability_application::e_t38fax }, -#endif - { 0 } - }; - -#if 0 - static const struct __codec__ vcodecs[] = { -#ifdef HAVE_H261 - { AST_FORMAT_H261, H245_VideoCapability::e_h261VideoCapability }, -#endif -#ifdef HAVE_H263 - { AST_FORMAT_H263, H245_VideoCapability::e_h263VideoCapability }, -#endif -#ifdef HAVE_H264 - { AST_FORMAT_H264, H245_VideoCapability::e_genericVideoCapability, "0.0.8.241.0.0.1" }, -#endif - { 0 } - }; -#endif - struct ast_codec_pref prefs; - - if (!H323Connection::OnReceivedCapabilitySet(remoteCaps, muxCap, reject)) { - return FALSE; - } - - const H323Capability * cap = remoteCaps.FindCapability(H323_UserInputCapability::SubTypeNames[H323_UserInputCapability::SignalToneRFC2833]); - if (cap != NULL) { - RTP_DataFrame::PayloadTypes pt = ((H323_UserInputCapability*)cap)->GetPayloadType(); - on_set_rfc2833_payload(GetCallReference(), (const char *)GetCallToken(), (int)pt); - if ((dtmfMode == H323_DTMF_RFC2833) && (sendUserInputMode == SendUserInputAsTone)) - sendUserInputMode = SendUserInputAsInlineRFC2833; -#ifdef PTRACING - if (h323debug) { - cout << "\t-- Inbound RFC2833 on payload " << pt << endl; - } -#endif - } - memset(&prefs, 0, sizeof(prefs)); - int peer_capabilities = 0; - for (int i = 0; i < remoteCapabilities.GetSize(); ++i) { - unsigned int subType = remoteCapabilities[i].GetSubType(); - if (h323debug) { - cout << "Peer capability is " << remoteCapabilities[i] << endl; - } - switch(remoteCapabilities[i].GetMainType()) { - case H323Capability::e_Audio: - for (int x = 0; codecs[x].asterisk_codec > 0; ++x) { - if (subType == codecs[x].h245_cap) { - int ast_codec = codecs[x].asterisk_codec; - int ms = 0; - if (!(peer_capabilities & ast_codec)) { - struct ast_format_list format; - ast_codec_pref_append(&prefs, ast_codec); - format = ast_codec_pref_getsize(&prefs, ast_codec); - if ((ast_codec == AST_FORMAT_ALAW) || (ast_codec == AST_FORMAT_ULAW)) { - ms = remoteCapabilities[i].GetTxFramesInPacket(); - if (ms > 60) - ms = format.cur_ms; - } else - ms = remoteCapabilities[i].GetTxFramesInPacket() * format.inc_ms; - ast_codec_pref_setsize(&prefs, ast_codec, ms); - } - if (h323debug) { - cout << "Found peer capability " << remoteCapabilities[i] << ", Asterisk code is " << ast_codec << ", frame size (in ms) is " << ms << endl; - } - peer_capabilities |= ast_codec; - } - } - break; -#if 0 - case H323Capability::e_Video: - for (int x = 0; vcodecs[x].asterisk_codec > 0; ++x) { - if (subType == vcodecs[x].h245_cap) { - H245_CapabilityIdentifier *cap = NULL; - H245_GenericCapability y; - if (vcodecs[x].oid) { - cap = new H245_CapabilityIdentifier(H245_CapabilityIdentifier::e_standard); - PASN_ObjectId &object_id = *cap; - object_id = vcodecs[x].oid; - y.m_capabilityIdentifier = *cap; - } - if ((subType != H245_VideoCapability::e_genericVideoCapability) || - (vcodecs[x].oid && ((const H323GenericVideoCapability &)remoteCapabilities[i]).IsGenericMatch((const H245_GenericCapability)y))) { - if (h323debug) { - cout << "Found peer video capability " << remoteCapabilities[i] << ", Asterisk code is " << vcodecs[x].asterisk_codec << endl; - } - peer_capabilities |= vcodecs[x].asterisk_codec; - } - if (cap) - delete(cap); - } - } - break; -#endif - default: - break; - } - } - if (h323debug) { - char caps_str[1024], caps2_str[1024]; - ast_codec_pref_string(&prefs, caps2_str, sizeof(caps2_str)); - cout << "Peer capabilities = " << ast_getformatname_multiple(caps_str, sizeof(caps_str), peer_capabilities) - << ", ordered list is " << caps2_str << endl; - } -#if 0 - redir_capabilities &= peer_capabilities; -#endif - if (on_setpeercapabilities) - on_setpeercapabilities(GetCallReference(), (const char *)callToken, peer_capabilities, &prefs); - - return TRUE; -} - -H323Channel * MyH323Connection::CreateRealTimeLogicalChannel(const H323Capability & capability, - H323Channel::Directions dir, - unsigned sessionID, - const H245_H2250LogicalChannelParameters * /*param*/, - RTP_QOS * /*param*/ ) -{ - /* Do not open tx channel when transmitter has been paused by empty TCS */ - if ((dir == H323Channel::IsTransmitter) && transmitterSidePaused) - return NULL; - - return new MyH323_ExternalRTPChannel(*this, capability, dir, sessionID); -} - -/** This callback function is invoked once upon creation of each - * channel for an H323 session - */ -BOOL MyH323Connection::OnStartLogicalChannel(H323Channel & channel) -{ - /* Increase the count of channels we have open */ - channelsOpen++; - - if (h323debug) { - cout << "\t-- Started logical channel: " - << ((channel.GetDirection() == H323Channel::IsTransmitter) ? "sending " : ((channel.GetDirection() == H323Channel::IsReceiver) ? "receiving " : " ")) - << (const char *)(channel.GetCapability()).GetFormatName() << endl; - cout << "\t\t-- channelsOpen = " << channelsOpen << endl; - } - return connectionState != ShuttingDownConnection; -} - -void MyH323Connection::SetCapabilities(int cap, int dtmf_mode, void *_prefs, int pref_codec) -{ - PINDEX lastcap = -1; /* last common capability index */ - int alreadysent = 0; - int codec; - int x, y; - char caps_str[1024]; - struct ast_codec_pref *prefs = (struct ast_codec_pref *)_prefs; - struct ast_format_list format; - int frames_per_packet; - int max_frames_per_packet; - - localCapabilities.RemoveAll(); - - if (h323debug) { - cout << "Setting capabilities to " << ast_getformatname_multiple(caps_str, sizeof(caps_str), cap) << endl; - ast_codec_pref_string(prefs, caps_str, sizeof(caps_str)); - cout << "Capabilities in preference order is " << caps_str << endl; - } - /* Add audio codecs in preference order first, then - audio codecs without preference as allowed by mask */ - for (y = 0, x = -1; x < 32 + 32; ++x) { - if (x < 0) - codec = pref_codec; - else if (y || (!(codec = ast_codec_pref_index(prefs, x)))) { - if (!y) - y = 1; - else if (y == AST_FORMAT_MAX_AUDIO) - break; - else - y <<= 1; - codec = y; - } - if (!(cap & codec) || (alreadysent & codec) || !(codec & AST_FORMAT_AUDIO_MASK)) - continue; - alreadysent |= codec; - format = ast_codec_pref_getsize(prefs, codec); - frames_per_packet = (format.inc_ms ? format.cur_ms / format.inc_ms : format.cur_ms); - max_frames_per_packet = (format.inc_ms ? format.max_ms / format.inc_ms : 0); - switch(codec) { -#if 0 - case AST_FORMAT_SPEEX: - /* Not real sure if Asterisk acutally supports all - of the various different bit rates so add them - all and figure it out later*/ - - lastcap = localCapabilities.SetCapability(0, 0, new SpeexNarrow2AudioCapability()); - lastcap = localCapabilities.SetCapability(0, 0, new SpeexNarrow3AudioCapability()); - lastcap = localCapabilities.SetCapability(0, 0, new SpeexNarrow4AudioCapability()); - lastcap = localCapabilities.SetCapability(0, 0, new SpeexNarrow5AudioCapability()); - lastcap = localCapabilities.SetCapability(0, 0, new SpeexNarrow6AudioCapability()); - break; -#endif - case AST_FORMAT_G729A: - AST_G729ACapability *g729aCap; - AST_G729Capability *g729Cap; - lastcap = localCapabilities.SetCapability(0, 0, g729aCap = new AST_G729ACapability(frames_per_packet)); - lastcap = localCapabilities.SetCapability(0, 0, g729Cap = new AST_G729Capability(frames_per_packet)); - if (max_frames_per_packet) { - g729aCap->SetTxFramesInPacket(max_frames_per_packet); - g729Cap->SetTxFramesInPacket(max_frames_per_packet); - } - break; - case AST_FORMAT_G723_1: - AST_G7231Capability *g7231Cap; - lastcap = localCapabilities.SetCapability(0, 0, g7231Cap = new AST_G7231Capability(frames_per_packet, TRUE)); - if (max_frames_per_packet) - g7231Cap->SetTxFramesInPacket(max_frames_per_packet); - lastcap = localCapabilities.SetCapability(0, 0, g7231Cap = new AST_G7231Capability(frames_per_packet, FALSE)); - if (max_frames_per_packet) - g7231Cap->SetTxFramesInPacket(max_frames_per_packet); - break; - case AST_FORMAT_GSM: - AST_GSM0610Capability *gsmCap; - lastcap = localCapabilities.SetCapability(0, 0, gsmCap = new AST_GSM0610Capability(frames_per_packet)); - if (max_frames_per_packet) - gsmCap->SetTxFramesInPacket(max_frames_per_packet); - break; - case AST_FORMAT_ULAW: - AST_G711Capability *g711uCap; - lastcap = localCapabilities.SetCapability(0, 0, g711uCap = new AST_G711Capability(format.cur_ms, H323_G711Capability::muLaw)); - if (format.max_ms) - g711uCap->SetTxFramesInPacket(format.max_ms); - break; - case AST_FORMAT_ALAW: - AST_G711Capability *g711aCap; - lastcap = localCapabilities.SetCapability(0, 0, g711aCap = new AST_G711Capability(format.cur_ms, H323_G711Capability::ALaw)); - if (format.max_ms) - g711aCap->SetTxFramesInPacket(format.max_ms); - break; - default: - alreadysent &= ~codec; - break; - } - } - - lastcap++; - lastcap = localCapabilities.SetCapability(0, lastcap, new H323_UserInputCapability(H323_UserInputCapability::HookFlashH245)); - - lastcap++; - dtmfMode = dtmf_mode; - if (dtmf_mode == H323_DTMF_INBAND) { - localCapabilities.SetCapability(0, lastcap, new H323_UserInputCapability(H323_UserInputCapability::BasicString)); - sendUserInputMode = SendUserInputAsString; - } else { - lastcap = localCapabilities.SetCapability(0, lastcap, new H323_UserInputCapability(H323_UserInputCapability::SignalToneRFC2833)); - /* Cisco sends DTMF only through h245-alphanumeric or h245-signal, no support for RFC2833 */ - lastcap = localCapabilities.SetCapability(0, lastcap, new H323_UserInputCapability(H323_UserInputCapability::SignalToneH245)); - sendUserInputMode = SendUserInputAsTone; /* RFC2833 transmission handled at Asterisk level */ - } - - if (h323debug) { - cout << "Allowed Codecs:\n\t" << setprecision(2) << localCapabilities << endl; - } -} - -BOOL MyH323Connection::StartControlChannel(const H225_TransportAddress & h245Address) -{ - // Check that it is an IP address, all we support at the moment - if (h245Address.GetTag() != H225_TransportAddress::e_ipAddress -#if P_HAS_IPV6 - && h245Address.GetTag() != H225_TransportAddress::e_ip6Address -#endif - ) { - PTRACE(1, "H225\tConnect of H245 failed: Unsupported transport"); - return FALSE; - } - - // Already have the H245 channel up. - if (controlChannel != NULL) - return TRUE; - - PIPSocket::Address addr; - WORD port; - GetSignallingChannel()->GetLocalAddress().GetIpAndPort(addr, port); - if (addr) { - if (h323debug) - cout << "Using " << addr << " for outbound H.245 transport" << endl; - controlChannel = new MyH323TransportTCP(endpoint, addr); - } else - controlChannel = new H323TransportTCP(endpoint); - if (!controlChannel->SetRemoteAddress(h245Address)) { - PTRACE(1, "H225\tCould not extract H245 address"); - delete controlChannel; - controlChannel = NULL; - return FALSE; - } - if (!controlChannel->Connect()) { - PTRACE(1, "H225\tConnect of H245 failed: " << controlChannel->GetErrorText()); - delete controlChannel; - controlChannel = NULL; - return FALSE; - } - - controlChannel->StartControlChannel(*this); - return TRUE; -} - -/* MyH323_ExternalRTPChannel */ -MyH323_ExternalRTPChannel::MyH323_ExternalRTPChannel(MyH323Connection & connection, - const H323Capability & capability, - Directions direction, - unsigned id) - : H323_ExternalRTPChannel::H323_ExternalRTPChannel(connection, capability, direction, id) -{ - struct rtp_info *info; - - /* Determine the Local (A side) IP Address and port */ - info = on_external_rtp_create(connection.GetCallReference(), (const char *)connection.GetCallToken()); - if (!info) { - cout << "\tERROR: on_external_rtp_create failure" << endl; - return; - } else { - localIpAddr = info->addr; - localPort = info->port; - /* tell the H.323 stack */ - SetExternalAddress(H323TransportAddress(localIpAddr, localPort), H323TransportAddress(localIpAddr, localPort + 1)); - /* clean up allocated memory */ - free(info); - } - - /* Get the payload code */ - OpalMediaFormat format(capability.GetFormatName(), FALSE); - payloadCode = format.GetPayloadType(); -} - -MyH323_ExternalRTPChannel::~MyH323_ExternalRTPChannel() -{ - if (h323debug) { - cout << "\tExternalRTPChannel Destroyed" << endl; - } -} - -BOOL MyH323_ExternalRTPChannel::Start(void) -{ - /* Call ancestor first */ - if (!H323_ExternalRTPChannel::Start()) { - return FALSE; - } - - if (h323debug) { - cout << "\t\tExternal RTP Session Starting" << endl; - cout << "\t\tRTP channel id " << sessionID << " parameters:" << endl; - } - - /* Collect the remote information */ - H323_ExternalRTPChannel::GetRemoteAddress(remoteIpAddr, remotePort); - - if (h323debug) { - cout << "\t\t-- remoteIpAddress: " << remoteIpAddr << endl; - cout << "\t\t-- remotePort: " << remotePort << endl; - cout << "\t\t-- ExternalIpAddress: " << localIpAddr << endl; - cout << "\t\t-- ExternalPort: " << localPort << endl; - } - /* Notify Asterisk of remote RTP information */ - on_start_rtp_channel(connection.GetCallReference(), (const char *)remoteIpAddr.AsString(), remotePort, - (const char *)connection.GetCallToken(), (int)payloadCode); - return TRUE; -} - -BOOL MyH323_ExternalRTPChannel::OnReceivedAckPDU(const H245_H2250LogicalChannelAckParameters & param) -{ - if (h323debug) { - cout << " MyH323_ExternalRTPChannel::OnReceivedAckPDU" << endl; - } - - if (H323_ExternalRTPChannel::OnReceivedAckPDU(param)) { - GetRemoteAddress(remoteIpAddr, remotePort); - if (h323debug) { - cout << " -- remoteIpAddress: " << remoteIpAddr << endl; - cout << " -- remotePort: " << remotePort << endl; - } - on_start_rtp_channel(connection.GetCallReference(), (const char *)remoteIpAddr.AsString(), - remotePort, (const char *)connection.GetCallToken(), (int)payloadCode); - return TRUE; - } - return FALSE; -} - - -/** IMPLEMENTATION OF C FUNCTIONS */ - -/** - * The extern "C" directive takes care for - * the ANSI-C representation of linkable symbols - */ - -extern "C" { - -int h323_end_point_exist(void) -{ - if (!endPoint) { - return 0; - } - return 1; -} - -void h323_end_point_create(void) -{ - channelsOpen = 0; - logstream = new PAsteriskLog(); - localProcess = new MyProcess(); - localProcess->Main(); -} - -void h323_gk_urq(void) -{ - if (!h323_end_point_exist()) { - cout << " ERROR: [h323_gk_urq] No Endpoint, this is bad" << endl; - return; - } - endPoint->RemoveGatekeeper(); -} - -void h323_debug(int flag, unsigned level) -{ - if (flag) { - PTrace:: SetLevel(level); - } else { - PTrace:: SetLevel(0); - } -} - -/** Installs the callback functions on behalf of the PBX application */ -void h323_callback_register(setup_incoming_cb ifunc, - setup_outbound_cb sfunc, - on_rtp_cb rtpfunc, - start_rtp_cb lfunc, - clear_con_cb clfunc, - chan_ringing_cb rfunc, - con_established_cb efunc, - receive_digit_cb dfunc, - answer_call_cb acfunc, - progress_cb pgfunc, - rfc2833_cb dtmffunc, - hangup_cb hangupfunc, - setcapabilities_cb capabilityfunc, - setpeercapabilities_cb peercapabilityfunc) -{ - on_incoming_call = ifunc; - on_outgoing_call = sfunc; - on_external_rtp_create = rtpfunc; - on_start_rtp_channel = lfunc; - on_connection_cleared = clfunc; - on_chan_ringing = rfunc; - on_connection_established = efunc; - on_receive_digit = dfunc; - on_answer_call = acfunc; - on_progress = pgfunc; - on_set_rfc2833_payload = dtmffunc; - on_hangup = hangupfunc; - on_setcapabilities = capabilityfunc; - on_setpeercapabilities = peercapabilityfunc; -} - -/** - * Add capability to the capability table of the end point. - */ -int h323_set_capabilities(const char *token, int cap, int dtmf_mode, struct ast_codec_pref *prefs, int pref_codec) -{ - MyH323Connection *conn; - - if (!h323_end_point_exist()) { - cout << " ERROR: [h323_set_capablities] No Endpoint, this is bad" << endl; - return 1; - } - if (!token || !*token) { - cout << " ERROR: [h323_set_capabilities] Invalid call token specified." << endl; - return 1; - } - - PString myToken(token); - conn = (MyH323Connection *)endPoint->FindConnectionWithLock(myToken); - if (!conn) { - cout << " ERROR: [h323_set_capabilities] Unable to find connection " << token << endl; - return 1; - } - conn->SetCapabilities((/*conn->bridging ? conn->redir_capabilities :*/ cap), dtmf_mode, prefs, pref_codec); - conn->Unlock(); - - return 0; -} - -/** Start the H.323 listener */ -int h323_start_listener(int listenPort, struct sockaddr_in bindaddr) -{ - - if (!h323_end_point_exist()) { - cout << "ERROR: [h323_start_listener] No Endpoint, this is bad!" << endl; - return 1; - } - - PIPSocket::Address interfaceAddress(bindaddr.sin_addr); - if (!listenPort) { - listenPort = 1720; - } - /** H.323 listener */ - H323ListenerTCP *tcpListener; - tcpListener = new H323ListenerTCP(*endPoint, interfaceAddress, (WORD)listenPort); - if (!endPoint->StartListener(tcpListener)) { - cout << "ERROR: Could not open H.323 listener port on " << ((H323ListenerTCP *) tcpListener)->GetListenerPort() << endl; - delete tcpListener; - return 1; - } - cout << " == H.323 listener started" << endl; - return 0; -}; - -int h323_set_alias(struct oh323_alias *alias) -{ - char *p; - char *num; - PString h323id(alias->name); - PString e164(alias->e164); - char *prefix; - - if (!h323_end_point_exist()) { - cout << "ERROR: [h323_set_alias] No Endpoint, this is bad!" << endl; - return 1; - } - - cout << "== Adding alias \"" << h323id << "\" to endpoint" << endl; - endPoint->AddAliasName(h323id); - endPoint->RemoveAliasName(localProcess->GetUserName()); - - if (!e164.IsEmpty()) { - cout << "== Adding E.164 \"" << e164 << "\" to endpoint" << endl; - endPoint->AddAliasName(e164); - } - if (strlen(alias->prefix)) { - p = prefix = strdup(alias->prefix); - while((num = strsep(&p, ",")) != (char *)NULL) { - cout << "== Adding Prefix \"" << num << "\" to endpoint" << endl; - endPoint->SupportedPrefixes += PString(num); - endPoint->SetGateway(); - } - if (prefix) - free(prefix); - } - return 0; -} - -void h323_set_id(char *id) -{ - PString h323id(id); - - if (h323debug) { - cout << " == Using '" << h323id << "' as our H.323ID for this call" << endl; - } - /* EVIL HACK */ - endPoint->SetLocalUserName(h323id); -} - -void h323_show_tokens(void) -{ - cout << "Current call tokens: " << setprecision(2) << endPoint->GetAllConnections() << endl; -} - -/** Establish Gatekeeper communiations, if so configured, - * register aliases for the H.323 endpoint to respond to. - */ -int h323_set_gk(int gatekeeper_discover, char *gatekeeper, char *secret) -{ - PString gkName = PString(gatekeeper); - PString pass = PString(secret); - H323TransportUDP *rasChannel; - - if (!h323_end_point_exist()) { - cout << "ERROR: [h323_set_gk] No Endpoint, this is bad!" << endl; - return 1; - } - - if (!gatekeeper) { - cout << "Error: Gatekeeper cannot be NULL" << endl; - return 1; - } - if (strlen(secret)) { - endPoint->SetGatekeeperPassword(pass); - } - if (gatekeeper_discover) { - /* discover the gk using multicast */ - if (endPoint->DiscoverGatekeeper(new MyH323TransportUDP(*endPoint))) { - cout << "== Using " << (endPoint->GetGatekeeper())->GetName() << " as our Gatekeeper." << endl; - } else { - cout << "Warning: Could not find a gatekeeper." << endl; - return 1; - } - } else { - rasChannel = new MyH323TransportUDP(*endPoint); - - if (!rasChannel) { - cout << "Error: No RAS Channel, this is bad" << endl; - return 1; - } - if (endPoint->SetGatekeeper(gkName, rasChannel)) { - cout << "== Using " << (endPoint->GetGatekeeper())->GetName() << " as our Gatekeeper." << endl; - } else { - cout << "Error registering with gatekeeper \"" << gkName << "\". " << endl; - /* XXX Maybe we should fire a new thread to attempt to re-register later and not kill asterisk here? */ - return 1; - } - } - return 0; -} - -/** Send a DTMF tone over the H323Connection with the - * specified token. - */ -void h323_send_tone(const char *call_token, char tone) -{ - if (!h323_end_point_exist()) { - cout << "ERROR: [h323_send_tone] No Endpoint, this is bad!" << endl; - return; - } - PString token = PString(call_token); - endPoint->SendUserTone(token, tone); -} - -/** Make a call to the remote endpoint. - */ -int h323_make_call(char *dest, call_details_t *cd, call_options_t *call_options) -{ - int res; - PString token; - PString host(dest); - - if (!h323_end_point_exist()) { - return 1; - } - - res = endPoint->MyMakeCall(host, token, &cd->call_reference, call_options); - memcpy((char *)(cd->call_token), (const unsigned char *)token, token.GetLength()); - return res; -}; - -int h323_clear_call(const char *call_token, int cause) -{ - H225_ReleaseCompleteReason dummy; - H323Connection::CallEndReason r = H323Connection::EndedByLocalUser; - MyH323Connection *connection; - const PString currentToken(call_token); - - if (!h323_end_point_exist()) { - return 1; - } - - if (cause) { - r = H323TranslateToCallEndReason((Q931::CauseValues)(cause), dummy); - } - - connection = (MyH323Connection *)endPoint->FindConnectionWithLock(currentToken); - if (connection) { - connection->SetCause(cause); - connection->SetCallEndReason(r); - connection->Unlock(); - } - endPoint->ClearCall(currentToken, r); - return 0; -}; - -/* Send Alerting PDU to H.323 caller */ -int h323_send_alerting(const char *token) -{ - const PString currentToken(token); - H323Connection * connection; - - if (h323debug) { - cout << "\tSending alerting" << endl; - } - connection = endPoint->FindConnectionWithLock(currentToken); - if (!connection) { - cout << "No connection found for " << token << endl; - return -1; - } - connection->AnsweringCall(H323Connection::AnswerCallPending); - connection->Unlock(); - return 0; -} - -/* Send Progress PDU to H.323 caller */ -int h323_send_progress(const char *token) -{ - const PString currentToken(token); - H323Connection * connection; - - connection = endPoint->FindConnectionWithLock(currentToken); - if (!connection) { - cout << "No connection found for " << token << endl; - return -1; - } -#if 1 - ((MyH323Connection *)connection)->MySendProgress(); -#else - connection->AnsweringCall(H323Connection::AnswerCallDeferredWithMedia); -#endif - connection->Unlock(); - return 0; -} - -/** This function tells the h.323 stack to either - answer or deny an incoming call */ -int h323_answering_call(const char *token, int busy) -{ - const PString currentToken(token); - H323Connection * connection; - - connection = endPoint->FindConnectionWithLock(currentToken); - - if (!connection) { - cout << "No connection found for " << token << endl; - return -1; - } - if (!busy) { - if (h323debug) { - cout << "\tAnswering call " << token << endl; - } - connection->AnsweringCall(H323Connection::AnswerCallNow); - } else { - if (h323debug) { - cout << "\tdenying call " << token << endl; - } - connection->AnsweringCall(H323Connection::AnswerCallDenied); - } - connection->Unlock(); - return 0; -} - -int h323_soft_hangup(const char *data) -{ - PString token(data); - BOOL result; - cout << "Soft hangup" << endl; - result = endPoint->ClearCall(token); - return result; -} - -/* alas, this doesn't work :( */ -void h323_native_bridge(const char *token, const char *them, char *capability) -{ - H323Channel *channel; - MyH323Connection *connection = (MyH323Connection *)endPoint->FindConnectionWithLock(token); - - if (!connection) { - cout << "ERROR: No connection found, this is bad" << endl; - return; - } - - cout << "Native Bridge: them [" << them << "]" << endl; - - channel = connection->FindChannel(connection->sessionId, TRUE); - connection->bridging = TRUE; - connection->CloseLogicalChannelNumber(channel->GetNumber()); - - connection->Unlock(); - return; - -} - -#undef cout -#undef endl -void h323_end_process(void) -{ - if (endPoint) { - endPoint->ClearAllCalls(); - endPoint->RemoveListener(NULL); - delete endPoint; - endPoint = NULL; - } - if (localProcess) { - delete localProcess; - localProcess = NULL; -#ifndef SKIP_PWLIB_PIPE_BUG_WORKAROUND - close(_timerChangePipe[0]); - close(_timerChangePipe[1]); -#endif - } - if (logstream) { - PTrace::SetLevel(0); - PTrace::SetStream(&cout); - delete logstream; - logstream = NULL; - } -} - -} /* extern "C" */ - diff --git a/1.4.23-rc4/channels/h323/ast_h323.h b/1.4.23-rc4/channels/h323/ast_h323.h deleted file mode 100644 index c4f24c529..000000000 --- a/1.4.23-rc4/channels/h323/ast_h323.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * ast_h323.h - * - * OpenH323 Channel Driver for ASTERISK PBX. - * By Jeremy McNamara - * For The NuFone Network - * - * This code has been derived from code created by - * Michael Manousos and Mark Spencer - * - * This file is part of the chan_h323 driver for Asterisk - * - * chan_h323 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. - * - * chan_h323 is distributed WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Version Info: $Id$ - */ - -#ifndef AST_H323_H -#define AST_H323_H - -#define VERSION(a,b,c) ((a)*10000+(b)*100+(c)) - -class MyH323EndPoint : public H323EndPoint -{ - PCLASSINFO(MyH323EndPoint, H323EndPoint); - -public: - MyH323EndPoint(); - int MyMakeCall(const PString &, PString &, void *_callReference, void *_opts); - BOOL ClearCall(const PString &, H323Connection::CallEndReason reason); - BOOL ClearCall(const PString &); - - void OnClosedLogicalChannel(H323Connection &, const H323Channel &); - void OnConnectionEstablished(H323Connection &, const PString &); - void OnConnectionCleared(H323Connection &, const PString &); - virtual H323Connection * CreateConnection(unsigned, void *, H323Transport *, H323SignalPDU *); - void SendUserTone(const PString &, char); - BOOL OnConnectionForwarded(H323Connection &, const PString &, const H323SignalPDU &); - BOOL ForwardConnection(H323Connection &, const PString &, const H323SignalPDU &); - void SetEndpointTypeInfo( H225_EndpointType & info ) const; - void SetGateway(void); - PStringArray SupportedPrefixes; -}; - -class MyH323Connection : public H323Connection -{ - PCLASSINFO(MyH323Connection, H323Connection); - -public: - MyH323Connection(MyH323EndPoint &, unsigned, unsigned); - ~MyH323Connection(); - H323Channel * CreateRealTimeLogicalChannel(const H323Capability &, - H323Channel::Directions, - unsigned, - const H245_H2250LogicalChannelParameters *, - RTP_QOS *); - H323Connection::AnswerCallResponse OnAnswerCall(const PString &, - const H323SignalPDU &, - H323SignalPDU &); - void OnReceivedReleaseComplete(const H323SignalPDU &); - BOOL OnAlerting(const H323SignalPDU &, const PString &); - BOOL OnSendReleaseComplete(H323SignalPDU &); - BOOL OnReceivedSignalSetup(const H323SignalPDU &); - BOOL OnReceivedFacility(const H323SignalPDU &); - BOOL OnSendSignalSetup(H323SignalPDU &); - BOOL OnStartLogicalChannel(H323Channel &); - BOOL OnClosingLogicalChannel(H323Channel &); - virtual void SendUserInputTone(char tone, unsigned duration = 0, unsigned logicalChannel = 0, unsigned rtpTimestamp = 0); - virtual void OnUserInputTone(char, unsigned, unsigned, unsigned); - virtual void OnUserInputString(const PString &value); - BOOL OnReceivedProgress(const H323SignalPDU &); - BOOL MySendProgress(); - void OnSendCapabilitySet(H245_TerminalCapabilitySet &); - void OnSetLocalCapabilities(); - void SetCapabilities(int, int, void *, int); - BOOL OnReceivedCapabilitySet(const H323Capabilities &, const H245_MultiplexCapability *, - H245_TerminalCapabilitySetReject &); - void SetCause(int _cause) { cause = _cause; }; - virtual BOOL StartControlChannel(const H225_TransportAddress & h245Address); - void SetCallOptions(void *opts, BOOL isIncoming); - void SetCallDetails(void *callDetails, const H323SignalPDU &setupPDU, BOOL isIncoming); - virtual H323Connection::CallEndReason SendSignalSetup(const PString&, const H323TransportAddress&); -#ifdef TUNNELLING - virtual BOOL HandleSignalPDU(H323SignalPDU &pdu); - BOOL EmbedTunneledInfo(H323SignalPDU &pdu); -#endif - - PString sourceAliases; - PString destAliases; - PString sourceE164; - PString destE164; - int cid_presentation; - int cid_ton; - PString rdnis; - int redirect_reason; - int transfer_capability; - - WORD sessionId; - BOOL bridging; -#ifdef TUNNELLING - int remoteTunnelOptions; - int tunnelOptions; -#endif - - unsigned progressSetup; - unsigned progressAlert; - int cause; - - RTP_DataFrame::PayloadTypes dtmfCodec; - int dtmfMode; -}; - -class MyH323_ExternalRTPChannel : public H323_ExternalRTPChannel -{ - PCLASSINFO(MyH323_ExternalRTPChannel, H323_ExternalRTPChannel); - -public: - MyH323_ExternalRTPChannel( - MyH323Connection & connection, - const H323Capability & capability, - Directions direction, - unsigned sessionID); - - ~MyH323_ExternalRTPChannel(); - - /* Overrides */ - BOOL Start(void); - BOOL OnReceivedAckPDU(const H245_H2250LogicalChannelAckParameters & param); - -protected: - BYTE payloadCode; - - PIPSocket::Address localIpAddr; - PIPSocket::Address remoteIpAddr; - WORD localPort; - WORD remotePort; -}; - -/** - * The MyProcess is a necessary descendant PProcess class so that the H323EndPoint - * objected to be created from within that class. (Solves the who owns main() problem). - */ -class MyProcess : public PProcess -{ - PCLASSINFO(MyProcess, PProcess); - -public: - MyProcess(); - ~MyProcess(); - void Main(); -}; - -#include "compat_h323.h" - -#endif /* !defined AST_H323_H */ diff --git a/1.4.23-rc4/channels/h323/caps_h323.cxx b/1.4.23-rc4/channels/h323/caps_h323.cxx deleted file mode 100644 index a420825a3..000000000 --- a/1.4.23-rc4/channels/h323/caps_h323.cxx +++ /dev/null @@ -1,239 +0,0 @@ -#include <ptlib.h> -#include <h323.h> -#include <h245.h> -#include "ast_h323.h" -#include "caps_h323.h" - -#define DEFINE_G711_CAPABILITY(cls, code, capName) \ -class cls : public AST_G711Capability { \ - public: \ - cls() : AST_G711Capability(240, code) { } \ -}; \ -H323_REGISTER_CAPABILITY(cls, capName) \ - -DEFINE_G711_CAPABILITY(AST_G711ALaw64Capability, H323_G711Capability::ALaw, OPAL_G711_ALAW_64K); -DEFINE_G711_CAPABILITY(AST_G711uLaw64Capability, H323_G711Capability::muLaw, OPAL_G711_ULAW_64K); -H323_REGISTER_CAPABILITY(AST_G7231Capability, OPAL_G7231); -H323_REGISTER_CAPABILITY(AST_G729Capability, OPAL_G729); -H323_REGISTER_CAPABILITY(AST_G729ACapability, OPAL_G729A); -H323_REGISTER_CAPABILITY(AST_GSM0610Capability, OPAL_GSM0610); - -/* - * Capability: G.711 - */ -AST_G711Capability::AST_G711Capability(int rx_frames, H323_G711Capability::Mode m, H323_G711Capability::Speed s) - : H323AudioCapability(rx_frames, 30) // 240ms max, 30ms desired -{ - mode = m; - speed = s; -} - - -PObject * AST_G711Capability::Clone() const -{ - return new AST_G711Capability(*this); -} - - -unsigned AST_G711Capability::GetSubType() const -{ - static const unsigned G711SubType[2][2] = { - { H245_AudioCapability::e_g711Alaw64k, H245_AudioCapability::e_g711Alaw56k }, - { H245_AudioCapability::e_g711Ulaw64k, H245_AudioCapability::e_g711Ulaw56k } - }; - return G711SubType[mode][speed]; -} - - -PString AST_G711Capability::GetFormatName() const -{ - static const char * const G711Name[2][2] = { - { OPAL_G711_ALAW_64K, OPAL_G711_ALAW_56K }, - { OPAL_G711_ULAW_64K, OPAL_G711_ULAW_56K }, - }; - return G711Name[mode][speed]; -} - - -H323Codec * AST_G711Capability::CreateCodec(H323Codec::Direction direction) const -{ - return NULL; -} - - -/* - * Capability: G.723.1 - */ -AST_G7231Capability::AST_G7231Capability(int rx_frames, BOOL annexA_) - : H323AudioCapability(rx_frames, 4) -{ - annexA = annexA_; -} - -PObject::Comparison AST_G7231Capability::Compare(const PObject & obj) const -{ - Comparison result = H323AudioCapability::Compare(obj); - if (result != EqualTo) { - return result; - } - PINDEX otherAnnexA = ((const AST_G7231Capability &)obj).annexA; - if (annexA < otherAnnexA) { - return LessThan; - } - if (annexA > otherAnnexA) { - return GreaterThan; - } - return EqualTo; -} - -PObject * AST_G7231Capability::Clone() const -{ - return new AST_G7231Capability(*this); -} - -PString AST_G7231Capability::GetFormatName() const -{ - return (annexA ? OPAL_G7231 "A" : OPAL_G7231); -} - -unsigned AST_G7231Capability::GetSubType() const -{ - return H245_AudioCapability::e_g7231; -} - -BOOL AST_G7231Capability::OnSendingPDU(H245_AudioCapability & cap, - unsigned packetSize) const -{ - cap.SetTag(H245_AudioCapability::e_g7231); - H245_AudioCapability_g7231 & g7231 = cap; - g7231.m_maxAl_sduAudioFrames = packetSize; - g7231.m_silenceSuppression = annexA; - return TRUE; -} - -BOOL AST_G7231Capability::OnReceivedPDU(const H245_AudioCapability & cap, - unsigned & packetSize) -{ - if (cap.GetTag() != H245_AudioCapability::e_g7231) { - return FALSE; - } - const H245_AudioCapability_g7231 & g7231 = cap; - packetSize = g7231.m_maxAl_sduAudioFrames; - annexA = g7231.m_silenceSuppression; - return TRUE; -} - -H323Codec * AST_G7231Capability::CreateCodec(H323Codec::Direction direction) const -{ - return NULL; -} - -/* - * Capability: G.729 - */ -AST_G729Capability::AST_G729Capability(int rx_frames) - : H323AudioCapability(rx_frames, 2) -{ -} - -PObject * AST_G729Capability::Clone() const -{ - return new AST_G729Capability(*this); -} - -unsigned AST_G729Capability::GetSubType() const -{ - return H245_AudioCapability::e_g729; -} - -PString AST_G729Capability::GetFormatName() const -{ - return OPAL_G729; -} - -H323Codec * AST_G729Capability::CreateCodec(H323Codec::Direction direction) const -{ - return NULL; -} - -/* - * Capability: G.729A - */ -AST_G729ACapability::AST_G729ACapability(int rx_frames) - : H323AudioCapability(rx_frames, 6) -{ -} - -PObject * AST_G729ACapability::Clone() const -{ - return new AST_G729ACapability(*this); -} - -unsigned AST_G729ACapability::GetSubType() const -{ - return H245_AudioCapability::e_g729AnnexA; -} - -PString AST_G729ACapability::GetFormatName() const -{ - return OPAL_G729A; -} - -H323Codec * AST_G729ACapability::CreateCodec(H323Codec::Direction direction) const -{ - return NULL; -} - -/* - * Capability: GSM full rate - */ -AST_GSM0610Capability::AST_GSM0610Capability(int rx_frames, int comfortNoise_, int scrambled_) - : H323AudioCapability(rx_frames, 2) -{ - comfortNoise = comfortNoise_; - scrambled = scrambled_; -} - -PObject * AST_GSM0610Capability::Clone() const -{ - return new AST_GSM0610Capability(*this); -} - -unsigned AST_GSM0610Capability::GetSubType() const -{ - return H245_AudioCapability::e_gsmFullRate; -} - -BOOL AST_GSM0610Capability::OnSendingPDU(H245_AudioCapability & cap, - unsigned packetSize) const -{ - cap.SetTag(H245_AudioCapability::e_gsmFullRate); - H245_GSMAudioCapability & gsm = cap; - gsm.m_audioUnitSize = packetSize * 33; - gsm.m_comfortNoise = comfortNoise; - gsm.m_scrambled = scrambled; - return TRUE; -} - -BOOL AST_GSM0610Capability::OnReceivedPDU(const H245_AudioCapability & cap, - unsigned & packetSize) -{ - if (cap.GetTag() != H245_AudioCapability::e_gsmFullRate) - return FALSE; - const H245_GSMAudioCapability & gsm = cap; - packetSize = (gsm.m_audioUnitSize + 32) / 33; - comfortNoise = gsm.m_comfortNoise; - scrambled = gsm.m_scrambled; - - return TRUE; -} - -PString AST_GSM0610Capability::GetFormatName() const -{ - return OPAL_GSM0610; -} - -H323Codec * AST_GSM0610Capability::CreateCodec(H323Codec::Direction direction) const -{ - return NULL; -} diff --git a/1.4.23-rc4/channels/h323/caps_h323.h b/1.4.23-rc4/channels/h323/caps_h323.h deleted file mode 100644 index be63e0230..000000000 --- a/1.4.23-rc4/channels/h323/caps_h323.h +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef __AST_H323CAPS_H -#define __AST_H323CAPS_H - -/**This class describes the G.711 codec capability. - */ -class AST_G711Capability : public H323AudioCapability -{ - PCLASSINFO(AST_G711Capability, H323AudioCapability); - -public: - AST_G711Capability(int rx_frames = 125, H323_G711Capability::Mode _mode = H323_G711Capability::muLaw, H323_G711Capability::Speed _speed = H323_G711Capability::At64k); - virtual PObject *Clone() const; - virtual H323Codec * CreateCodec(H323Codec::Direction direction) const; - virtual unsigned GetSubType() const; - virtual PString GetFormatName() const; - -protected: - H323_G711Capability::Mode mode; - H323_G711Capability::Speed speed; -}; - -/**This class describes the G.723.1 codec capability. - */ -class AST_G7231Capability : public H323AudioCapability -{ - PCLASSINFO(AST_G7231Capability, H323AudioCapability); - -public: - AST_G7231Capability(int rx_frames = 7, BOOL annexA = TRUE); - Comparison Compare(const PObject & obj) const; - virtual PObject * Clone() const; - virtual H323Codec * CreateCodec(H323Codec::Direction direction) const; - virtual unsigned GetSubType() const; - virtual PString GetFormatName() const; - virtual BOOL OnSendingPDU(H245_AudioCapability & pdu, unsigned packetSize) const; - virtual BOOL OnReceivedPDU(const H245_AudioCapability & pdu, unsigned & packetSize); - -protected: - BOOL annexA; -}; - -/**This class describes the (fake) G729 codec capability. - */ -class AST_G729Capability : public H323AudioCapability -{ - PCLASSINFO(AST_G729Capability, H323AudioCapability); - -public: - AST_G729Capability(int rx_frames = 24); - /* Create a copy of the object. */ - virtual PObject * Clone() const; - - /* Create the codec instance, allocating resources as required. */ - virtual H323Codec * CreateCodec(H323Codec::Direction direction) const; - - /* Get the sub-type of the capability. This is a code dependent on the - main type of the capability. - - This returns one of the four possible combinations of mode and speed - using the enum values of the protocol ASN H245_AudioCapability class. */ - virtual unsigned GetSubType() const; - - /* Get the name of the media data format this class represents. */ - virtual PString GetFormatName() const; -}; - -/* This class describes the VoiceAge G729A codec capability. */ -class AST_G729ACapability : public H323AudioCapability -{ - PCLASSINFO(AST_G729ACapability, H323AudioCapability); - -public: - /* Create a new G.729A capability. */ - AST_G729ACapability(int rx_frames = 24); - - /* Create a copy of the object. */ - virtual PObject * Clone() const; - /* Create the codec instance, allocating resources as required. */ - virtual H323Codec * CreateCodec(H323Codec::Direction direction) const; - - /* Get the sub-type of the capability. This is a code dependent on the - main type of the capability. - - This returns one of the four possible combinations of mode and speed - using the enum values of the protocol ASN H245_AudioCapability class. */ - virtual unsigned GetSubType() const; - - /* Get the name of the media data format this class represents. */ - virtual PString GetFormatName() const; -}; - -/* This class describes the GSM-06.10 codec capability. */ -class AST_GSM0610Capability : public H323AudioCapability -{ - PCLASSINFO(AST_GSM0610Capability, H323AudioCapability); - -public: - /* Create a new GSM capability. */ - AST_GSM0610Capability(int rx_frames = 24, int comfortNoise = 0, int scrambled = 0); - - /* Create a copy of the object. */ - virtual PObject * Clone() const; - - /* Create the codec instance, allocating resources as required. */ - virtual H323Codec * CreateCodec(H323Codec::Direction direction) const; - - /* Get the sub-type of the capability. This is a code dependent on the - main type of the capability. - - This returns one of the four possible combinations of mode and speed - using the enum values of the protocol ASN H245_AudioCapability class. */ - virtual unsigned GetSubType() const; - - /* Get the name of the media data format this class represents. */ - virtual PString GetFormatName() const; - - BOOL OnSendingPDU(H245_AudioCapability & pdu, unsigned packetSize) const; - BOOL OnReceivedPDU(const H245_AudioCapability & pdu, unsigned & packetSize); - -protected: - int comfortNoise; - int scrambled; -}; -#endif /* __AST_H323CAPS_H */ diff --git a/1.4.23-rc4/channels/h323/chan_h323.h b/1.4.23-rc4/channels/h323/chan_h323.h deleted file mode 100644 index 0fd94561f..000000000 --- a/1.4.23-rc4/channels/h323/chan_h323.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * chan_h323.h - * - * OpenH323 Channel Driver for ASTERISK PBX. - * By Jeremy McNamara - * For The NuFone Network - * - * This code has been derived from code created by - * Michael Manousos and Mark Spencer - * - * This file is part of the chan_h323 driver for Asterisk - * - * chan_h323 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. - * - * chan_h323 is distributed WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Version Info: $Id$ - */ - -#include <arpa/inet.h> - -/* - * Enable support for sending/reception of tunnelled Q.SIG messages and - * some sort of IEs (especially RedirectingNumber) which Cisco CallManager - * isn't like to pass in standard Q.931 message. - * - */ -#define TUNNELLING - -#define H323_TUNNEL_CISCO (1 << 0) -#define H323_TUNNEL_QSIG (1 << 1) - -/** call_option struct holds various bits - * of information for each call */ -typedef struct call_options { - char cid_num[80]; - char cid_name[80]; - char cid_rdnis[80]; - int redirect_reason; - int presentation; - int type_of_number; - int transfer_capability; - int fastStart; - int h245Tunneling; - int silenceSuppression; - int progress_setup; - int progress_alert; - int progress_audio; - int dtmfcodec; - int dtmfmode; - int capability; - int bridge; - int nat; - int tunnelOptions; - struct ast_codec_pref prefs; -} call_options_t; - -/* structure to hold the valid asterisk users */ -struct oh323_user { - ASTOBJ_COMPONENTS(struct oh323_user); -// char name[80]; - char context[80]; - char secret[80]; - char accountcode[AST_MAX_ACCOUNT_CODE]; - int amaflags; - int host; - struct sockaddr_in addr; - struct ast_ha *ha; - call_options_t options; -}; - -/* structure to hold the valid asterisk peers - All peers are registered to a GK if there is one */ -struct oh323_peer { - ASTOBJ_COMPONENTS(struct oh323_peer); - char mailbox[80]; - int delme; - struct sockaddr_in addr; - struct ast_ha *ha; - call_options_t options; -}; - -/* structure to hold the H.323 aliases which get registered to - the H.323 endpoint and gatekeeper */ -struct oh323_alias { - ASTOBJ_COMPONENTS(struct oh323_alias); - char e164[20]; /* tells a GK to route this E.164 to this alias */ - char prefix[500]; /* tells a GK this alias supports these prefixes */ - char secret[20]; /* the H.235 password to send to the GK for authentication */ - char context[80]; -}; - -/** call_details struct call detail records - to asterisk for processing and used for matching up - asterisk channels to acutal h.323 connections */ -typedef struct call_details { - unsigned int call_reference; - char *call_token; - char *call_source_aliases; - char *call_dest_alias; - char *call_source_name; - char *call_source_e164; - char *call_dest_e164; - char *redirect_number; - int redirect_reason; - int presentation; - int type_of_number; - int transfer_capability; - char *sourceIp; -} call_details_t; - -typedef struct rtp_info { - char addr[32]; - unsigned int port; -} rtp_info_t; - -/* This is a callback prototype function, called pass - DTMF down the RTP. */ -typedef int (*receive_digit_cb)(unsigned, char, const char *, int); -extern receive_digit_cb on_receive_digit; - -/* This is a callback prototype function, called to collect - the external RTP port from Asterisk. */ -typedef rtp_info_t *(*on_rtp_cb)(unsigned, const char *); -extern on_rtp_cb on_external_rtp_create; - -/* This is a callback prototype function, called to send - the remote IP and RTP port from H.323 to Asterisk */ -typedef void (*start_rtp_cb)(unsigned int, const char *, int, const char *, int); -extern start_rtp_cb on_start_rtp_channel; - -/* This is a callback that happens when call progress is - * made, and handles inband progress */ -typedef int (*progress_cb)(unsigned, const char *, int); -extern progress_cb on_progress; - -/* This is a callback prototype function, called upon - an incoming call happens. */ -typedef call_options_t *(*setup_incoming_cb)(call_details_t *); -extern setup_incoming_cb on_incoming_call; - -/* This is a callback prototype function, called upon - an outbound call. */ -typedef int (*setup_outbound_cb)(call_details_t *); -extern setup_outbound_cb on_outgoing_call; - -/* This is a callback prototype function, called when - OnAlerting is invoked */ -typedef void (*chan_ringing_cb)(unsigned, const char *); -extern chan_ringing_cb on_chan_ringing; - -/* This is a callback protoype function, called when - OnConnectionEstablished is inovked */ -typedef void (*con_established_cb)(unsigned, const char *); -extern con_established_cb on_connection_established; - -/* This is a callback prototype function, called when - OnConnectionCleared callback is invoked */ -typedef void (*clear_con_cb)(unsigned, const char *); -extern clear_con_cb on_connection_cleared; - -/* This is a callback prototype function, called when - an H.323 call is answered */ -typedef int (*answer_call_cb)(unsigned, const char *); -extern answer_call_cb on_answer_call; - -/* This is a callback prototype function, called when - we know which RTP payload type RFC2833 will be - transmitted */ -typedef void (*rfc2833_cb)(unsigned, const char *, int); -extern rfc2833_cb on_set_rfc2833_payload; - -typedef void (*hangup_cb)(unsigned, const char *, int); -extern hangup_cb on_hangup; - -typedef void (*setcapabilities_cb)(unsigned, const char *); -extern setcapabilities_cb on_setcapabilities; - -typedef void (*setpeercapabilities_cb)(unsigned, const char *, int, struct ast_codec_pref *); -extern setpeercapabilities_cb on_setpeercapabilities; - -/* debug flag */ -extern int h323debug; - -#define H323_DTMF_RFC2833 (1 << 0) -#define H323_DTMF_INBAND (1 << 1) - -#ifndef BOOL -#define BOOL int -#endif - -#ifdef __cplusplus -extern "C" { -#endif - - void h323_gk_urq(void); - void h323_end_point_create(void); - void h323_end_process(void); - int h323_end_point_exist(void); - - void h323_debug(int, unsigned); - - /* callback function handler*/ - void h323_callback_register(setup_incoming_cb, - setup_outbound_cb, - on_rtp_cb, - start_rtp_cb, - clear_con_cb, - chan_ringing_cb, - con_established_cb, - receive_digit_cb, - answer_call_cb, - progress_cb, - rfc2833_cb, - hangup_cb, - setcapabilities_cb, - setpeercapabilities_cb); - int h323_set_capabilities(const char *, int, int, struct ast_codec_pref *, int); - int h323_set_alias(struct oh323_alias *); - int h323_set_gk(int, char *, char *); - void h323_set_id(char *); - void h323_show_tokens(void); - - /* H323 listener related funcions */ - int h323_start_listener(int, struct sockaddr_in); - - void h323_native_bridge(const char *, const char *, char *); - - /* Send a DTMF tone to remote endpoint */ - void h323_send_tone(const char *call_token, char tone); - - /* H323 create and destroy sessions */ - int h323_make_call(char *dest, call_details_t *cd, call_options_t *); - int h323_clear_call(const char *, int cause); - - /* H.323 alerting and progress */ - int h323_send_alerting(const char *token); - int h323_send_progress(const char *token); - int h323_answering_call(const char *token, int); - int h323_soft_hangup(const char *data); - int h323_show_codec(int fd, int argc, char *argv[]); - -#ifdef __cplusplus -} -#endif diff --git a/1.4.23-rc4/channels/h323/cisco-h225.asn b/1.4.23-rc4/channels/h323/cisco-h225.asn deleted file mode 100644 index 1372e67d5..000000000 --- a/1.4.23-rc4/channels/h323/cisco-h225.asn +++ /dev/null @@ -1,74 +0,0 @@ -CISCO-H225-MESSAGES DEFINITIONS AUTOMATIC TAGS ::= -BEGIN - -H323_UU_NonStdInfo ::= SEQUENCE -{ - version INTEGER OPTIONAL, - protoParam ProtoParam OPTIONAL, - commonParam CommonParam OPTIONAL, - ..., - dummy1 OCTET STRING OPTIONAL, - progIndParam ProgIndParam OPTIONAL, - callMgrParam CallMgrParam OPTIONAL, - callSignallingParam CallSignallingParam OPTIONAL, - dummy2 OCTET STRING OPTIONAL, - callPreserveParam CallPreserveParam OPTIONAL -} - -CommonParam ::= SEQUENCE -{ - redirectIEinfo RedirectIEinfo, - ... -} - -RedirectIEinfo ::= SEQUENCE -{ - redirectIE OCTET STRING, - ... -} - -ProgIndParam ::= SEQUENCE -{ - progIndIEinfo ProgIndIEinfo, - ... -} - -ProgIndIEinfo ::= SEQUENCE -{ - progIndIE OCTET STRING, - ... -} - -ProtoParam ::= SEQUENCE -{ - qsigNonStdInfo QsigNonStdInfo, - ... -} - -QsigNonStdInfo ::= SEQUENCE -{ - iei INTEGER, - rawMesg OCTET STRING, - ... -} - -CallMgrParam ::= SEQUENCE -{ - interclusterVersion INTEGER, - enterpriseID OCTET STRING, - ... -} - -CallPreserveParam ::= SEQUENCE -{ - callPreserveIE BOOLEAN, - ... -} - -CallSignallingParam ::= SEQUENCE -{ - connectedNumber OCTET STRING (1..127) OPTIONAL, - ... -} - -END diff --git a/1.4.23-rc4/channels/h323/cisco-h225.cxx b/1.4.23-rc4/channels/h323/cisco-h225.cxx deleted file mode 100644 index 37adc4e87..000000000 --- a/1.4.23-rc4/channels/h323/cisco-h225.cxx +++ /dev/null @@ -1,853 +0,0 @@ -// -// cisco-h225.cxx -// -// Code automatically generated by asnparse. -// - -#ifdef P_USE_PRAGMA -#pragma implementation "cisco-h225.h" -#endif - -#include <ptlib.h> -#include "cisco-h225.h" - -#define new PNEW - - -#if ! H323_DISABLE_CISCO_H225 - -// -// RedirectIEinfo -// - -CISCO_H225_RedirectIEinfo::CISCO_H225_RedirectIEinfo(unsigned tag, PASN_Object::TagClass tagClass) - : PASN_Sequence(tag, tagClass, 0, TRUE, 0) -{ -} - - -#ifndef PASN_NOPRINTON -void CISCO_H225_RedirectIEinfo::PrintOn(ostream & strm) const -{ - int indent = strm.precision() + 2; - strm << "{\n"; - strm << setw(indent+13) << "redirectIE = " << setprecision(indent) << m_redirectIE << '\n'; - strm << setw(indent-1) << setprecision(indent-2) << "}"; -} -#endif - - -PObject::Comparison CISCO_H225_RedirectIEinfo::Compare(const PObject & obj) const -{ -#ifndef PASN_LEANANDMEAN - PAssert(PIsDescendant(&obj, CISCO_H225_RedirectIEinfo), PInvalidCast); -#endif - const CISCO_H225_RedirectIEinfo & other = (const CISCO_H225_RedirectIEinfo &)obj; - - Comparison result; - - if ((result = m_redirectIE.Compare(other.m_redirectIE)) != EqualTo) - return result; - - return PASN_Sequence::Compare(other); -} - - -PINDEX CISCO_H225_RedirectIEinfo::GetDataLength() const -{ - PINDEX length = 0; - length += m_redirectIE.GetObjectLength(); - return length; -} - - -BOOL CISCO_H225_RedirectIEinfo::Decode(PASN_Stream & strm) -{ - if (!PreambleDecode(strm)) - return FALSE; - - if (!m_redirectIE.Decode(strm)) - return FALSE; - - return UnknownExtensionsDecode(strm); -} - - -void CISCO_H225_RedirectIEinfo::Encode(PASN_Stream & strm) const -{ - PreambleEncode(strm); - - m_redirectIE.Encode(strm); - - UnknownExtensionsEncode(strm); -} - - -PObject * CISCO_H225_RedirectIEinfo::Clone() const -{ -#ifndef PASN_LEANANDMEAN - PAssert(IsClass(CISCO_H225_RedirectIEinfo::Class()), PInvalidCast); -#endif - return new CISCO_H225_RedirectIEinfo(*this); -} - - -// -// ProgIndIEinfo -// - -CISCO_H225_ProgIndIEinfo::CISCO_H225_ProgIndIEinfo(unsigned tag, PASN_Object::TagClass tagClass) - : PASN_Sequence(tag, tagClass, 0, TRUE, 0) -{ -} - - -#ifndef PASN_NOPRINTON -void CISCO_H225_ProgIndIEinfo::PrintOn(ostream & strm) const -{ - int indent = strm.precision() + 2; - strm << "{\n"; - strm << setw(indent+12) << "progIndIE = " << setprecision(indent) << m_progIndIE << '\n'; - strm << setw(indent-1) << setprecision(indent-2) << "}"; -} -#endif - - -PObject::Comparison CISCO_H225_ProgIndIEinfo::Compare(const PObject & obj) const -{ -#ifndef PASN_LEANANDMEAN - PAssert(PIsDescendant(&obj, CISCO_H225_ProgIndIEinfo), PInvalidCast); -#endif - const CISCO_H225_ProgIndIEinfo & other = (const CISCO_H225_ProgIndIEinfo &)obj; - - Comparison result; - - if ((result = m_progIndIE.Compare(other.m_progIndIE)) != EqualTo) - return result; - - return PASN_Sequence::Compare(other); -} - - -PINDEX CISCO_H225_ProgIndIEinfo::GetDataLength() const -{ - PINDEX length = 0; - length += m_progIndIE.GetObjectLength(); - return length; -} - - -BOOL CISCO_H225_ProgIndIEinfo::Decode(PASN_Stream & strm) -{ - if (!PreambleDecode(strm)) - return FALSE; - - if (!m_progIndIE.Decode(strm)) - return FALSE; - - return UnknownExtensionsDecode(strm); -} - - -void CISCO_H225_ProgIndIEinfo::Encode(PASN_Stream & strm) const -{ - PreambleEncode(strm); - - m_progIndIE.Encode(strm); - - UnknownExtensionsEncode(strm); -} - - -PObject * CISCO_H225_ProgIndIEinfo::Clone() const -{ -#ifndef PASN_LEANANDMEAN - PAssert(IsClass(CISCO_H225_ProgIndIEinfo::Class()), PInvalidCast); -#endif - return new CISCO_H225_ProgIndIEinfo(*this); -} - - -// -// QsigNonStdInfo -// - -CISCO_H225_QsigNonStdInfo::CISCO_H225_QsigNonStdInfo(unsigned tag, PASN_Object::TagClass tagClass) - : PASN_Sequence(tag, tagClass, 0, TRUE, 0) -{ -} - - -#ifndef PASN_NOPRINTON -void CISCO_H225_QsigNonStdInfo::PrintOn(ostream & strm) const -{ - int indent = strm.precision() + 2; - strm << "{\n"; - strm << setw(indent+6) << "iei = " << setprecision(indent) << m_iei << '\n'; - strm << setw(indent+10) << "rawMesg = " << setprecision(indent) << m_rawMesg << '\n'; - strm << setw(indent-1) << setprecision(indent-2) << "}"; -} -#endif - - -PObject::Comparison CISCO_H225_QsigNonStdInfo::Compare(const PObject & obj) const -{ -#ifndef PASN_LEANANDMEAN - PAssert(PIsDescendant(&obj, CISCO_H225_QsigNonStdInfo), PInvalidCast); -#endif - const CISCO_H225_QsigNonStdInfo & other = (const CISCO_H225_QsigNonStdInfo &)obj; - - Comparison result; - - if ((result = m_iei.Compare(other.m_iei)) != EqualTo) - return result; - if ((result = m_rawMesg.Compare(other.m_rawMesg)) != EqualTo) - return result; - - return PASN_Sequence::Compare(other); -} - - -PINDEX CISCO_H225_QsigNonStdInfo::GetDataLength() const -{ - PINDEX length = 0; - length += m_iei.GetObjectLength(); - length += m_rawMesg.GetObjectLength(); - return length; -} - - -BOOL CISCO_H225_QsigNonStdInfo::Decode(PASN_Stream & strm) -{ - if (!PreambleDecode(strm)) - return FALSE; - - if (!m_iei.Decode(strm)) - return FALSE; - if (!m_rawMesg.Decode(strm)) - return FALSE; - - return UnknownExtensionsDecode(strm); -} - - -void CISCO_H225_QsigNonStdInfo::Encode(PASN_Stream & strm) const -{ - PreambleEncode(strm); - - m_iei.Encode(strm); - m_rawMesg.Encode(strm); - - UnknownExtensionsEncode(strm); -} - - -PObject * CISCO_H225_QsigNonStdInfo::Clone() const -{ -#ifndef PASN_LEANANDMEAN - PAssert(IsClass(CISCO_H225_QsigNonStdInfo::Class()), PInvalidCast); -#endif - return new CISCO_H225_QsigNonStdInfo(*this); -} - - -// -// CallMgrParam -// - -CISCO_H225_CallMgrParam::CISCO_H225_CallMgrParam(unsigned tag, PASN_Object::TagClass tagClass) - : PASN_Sequence(tag, tagClass, 0, TRUE, 0) -{ -} - - -#ifndef PASN_NOPRINTON -void CISCO_H225_CallMgrParam::PrintOn(ostream & strm) const -{ - int indent = strm.precision() + 2; - strm << "{\n"; - strm << setw(indent+22) << "interclusterVersion = " << setprecision(indent) << m_interclusterVersion << '\n'; - strm << setw(indent+15) << "enterpriseID = " << setprecision(indent) << m_enterpriseID << '\n'; - strm << setw(indent-1) << setprecision(indent-2) << "}"; -} -#endif - - -PObject::Comparison CISCO_H225_CallMgrParam::Compare(const PObject & obj) const -{ -#ifndef PASN_LEANANDMEAN - PAssert(PIsDescendant(&obj, CISCO_H225_CallMgrParam), PInvalidCast); -#endif - const CISCO_H225_CallMgrParam & other = (const CISCO_H225_CallMgrParam &)obj; - - Comparison result; - - if ((result = m_interclusterVersion.Compare(other.m_interclusterVersion)) != EqualTo) - return result; - if ((result = m_enterpriseID.Compare(other.m_enterpriseID)) != EqualTo) - return result; - - return PASN_Sequence::Compare(other); -} - - -PINDEX CISCO_H225_CallMgrParam::GetDataLength() const -{ - PINDEX length = 0; - length += m_interclusterVersion.GetObjectLength(); - length += m_enterpriseID.GetObjectLength(); - return length; -} - - -BOOL CISCO_H225_CallMgrParam::Decode(PASN_Stream & strm) -{ - if (!PreambleDecode(strm)) - return FALSE; - - if (!m_interclusterVersion.Decode(strm)) - return FALSE; - if (!m_enterpriseID.Decode(strm)) - return FALSE; - - return UnknownExtensionsDecode(strm); -} - - -void CISCO_H225_CallMgrParam::Encode(PASN_Stream & strm) const -{ - PreambleEncode(strm); - - m_interclusterVersion.Encode(strm); - m_enterpriseID.Encode(strm); - - UnknownExtensionsEncode(strm); -} - - -PObject * CISCO_H225_CallMgrParam::Clone() const -{ -#ifndef PASN_LEANANDMEAN - PAssert(IsClass(CISCO_H225_CallMgrParam::Class()), PInvalidCast); -#endif - return new CISCO_H225_CallMgrParam(*this); -} - - -// -// CallPreserveParam -// - -CISCO_H225_CallPreserveParam::CISCO_H225_CallPreserveParam(unsigned tag, PASN_Object::TagClass tagClass) - : PASN_Sequence(tag, tagClass, 0, TRUE, 0) -{ -} - - -#ifndef PASN_NOPRINTON -void CISCO_H225_CallPreserveParam::PrintOn(ostream & strm) const -{ - int indent = strm.precision() + 2; - strm << "{\n"; - strm << setw(indent+17) << "callPreserveIE = " << setprecision(indent) << m_callPreserveIE << '\n'; - strm << setw(indent-1) << setprecision(indent-2) << "}"; -} -#endif - - -PObject::Comparison CISCO_H225_CallPreserveParam::Compare(const PObject & obj) const -{ -#ifndef PASN_LEANANDMEAN - PAssert(PIsDescendant(&obj, CISCO_H225_CallPreserveParam), PInvalidCast); -#endif - const CISCO_H225_CallPreserveParam & other = (const CISCO_H225_CallPreserveParam &)obj; - - Comparison result; - - if ((result = m_callPreserveIE.Compare(other.m_callPreserveIE)) != EqualTo) - return result; - - return PASN_Sequence::Compare(other); -} - - -PINDEX CISCO_H225_CallPreserveParam::GetDataLength() const -{ - PINDEX length = 0; - length += m_callPreserveIE.GetObjectLength(); - return length; -} - - -BOOL CISCO_H225_CallPreserveParam::Decode(PASN_Stream & strm) -{ - if (!PreambleDecode(strm)) - return FALSE; - - if (!m_callPreserveIE.Decode(strm)) - return FALSE; - - return UnknownExtensionsDecode(strm); -} - - -void CISCO_H225_CallPreserveParam::Encode(PASN_Stream & strm) const -{ - PreambleEncode(strm); - - m_callPreserveIE.Encode(strm); - - UnknownExtensionsEncode(strm); -} - - -PObject * CISCO_H225_CallPreserveParam::Clone() const -{ -#ifndef PASN_LEANANDMEAN - PAssert(IsClass(CISCO_H225_CallPreserveParam::Class()), PInvalidCast); -#endif - return new CISCO_H225_CallPreserveParam(*this); -} - - -// -// CallSignallingParam -// - -CISCO_H225_CallSignallingParam::CISCO_H225_CallSignallingParam(unsigned tag, PASN_Object::TagClass tagClass) - : PASN_Sequence(tag, tagClass, 1, TRUE, 0) -{ - m_connectedNumber.SetConstraints(PASN_Object::FixedConstraint, 1, 127); -} - - -#ifndef PASN_NOPRINTON -void CISCO_H225_CallSignallingParam::PrintOn(ostream & strm) const -{ - int indent = strm.precision() + 2; - strm << "{\n"; - if (HasOptionalField(e_connectedNumber)) - strm << setw(indent+18) << "connectedNumber = " << setprecision(indent) << m_connectedNumber << '\n'; - strm << setw(indent-1) << setprecision(indent-2) << "}"; -} -#endif - - -PObject::Comparison CISCO_H225_CallSignallingParam::Compare(const PObject & obj) const -{ -#ifndef PASN_LEANANDMEAN - PAssert(PIsDescendant(&obj, CISCO_H225_CallSignallingParam), PInvalidCast); -#endif - const CISCO_H225_CallSignallingParam & other = (const CISCO_H225_CallSignallingParam &)obj; - - Comparison result; - - if ((result = m_connectedNumber.Compare(other.m_connectedNumber)) != EqualTo) - return result; - - return PASN_Sequence::Compare(other); -} - - -PINDEX CISCO_H225_CallSignallingParam::GetDataLength() const -{ - PINDEX length = 0; - if (HasOptionalField(e_connectedNumber)) - length += m_connectedNumber.GetObjectLength(); - return length; -} - - -BOOL CISCO_H225_CallSignallingParam::Decode(PASN_Stream & strm) -{ - if (!PreambleDecode(strm)) - return FALSE; - - if (HasOptionalField(e_connectedNumber) && !m_connectedNumber.Decode(strm)) - return FALSE; - - return UnknownExtensionsDecode(strm); -} - - -void CISCO_H225_CallSignallingParam::Encode(PASN_Stream & strm) const -{ - PreambleEncode(strm); - - if (HasOptionalField(e_connectedNumber)) - m_connectedNumber.Encode(strm); - - UnknownExtensionsEncode(strm); -} - - -PObject * CISCO_H225_CallSignallingParam::Clone() const -{ -#ifndef PASN_LEANANDMEAN - PAssert(IsClass(CISCO_H225_CallSignallingParam::Class()), PInvalidCast); -#endif - return new CISCO_H225_CallSignallingParam(*this); -} - - -// -// CommonParam -// - -CISCO_H225_CommonParam::CISCO_H225_CommonParam(unsigned tag, PASN_Object::TagClass tagClass) - : PASN_Sequence(tag, tagClass, 0, TRUE, 0) -{ -} - - -#ifndef PASN_NOPRINTON -void CISCO_H225_CommonParam::PrintOn(ostream & strm) const -{ - int indent = strm.precision() + 2; - strm << "{\n"; - strm << setw(indent+17) << "redirectIEinfo = " << setprecision(indent) << m_redirectIEinfo << '\n'; - strm << setw(indent-1) << setprecision(indent-2) << "}"; -} -#endif - - -PObject::Comparison CISCO_H225_CommonParam::Compare(const PObject & obj) const -{ -#ifndef PASN_LEANANDMEAN - PAssert(PIsDescendant(&obj, CISCO_H225_CommonParam), PInvalidCast); -#endif - const CISCO_H225_CommonParam & other = (const CISCO_H225_CommonParam &)obj; - - Comparison result; - - if ((result = m_redirectIEinfo.Compare(other.m_redirectIEinfo)) != EqualTo) - return result; - - return PASN_Sequence::Compare(other); -} - - -PINDEX CISCO_H225_CommonParam::GetDataLength() const -{ - PINDEX length = 0; - length += m_redirectIEinfo.GetObjectLength(); - return length; -} - - -BOOL CISCO_H225_CommonParam::Decode(PASN_Stream & strm) -{ - if (!PreambleDecode(strm)) - return FALSE; - - if (!m_redirectIEinfo.Decode(strm)) - return FALSE; - - return UnknownExtensionsDecode(strm); -} - - -void CISCO_H225_CommonParam::Encode(PASN_Stream & strm) const -{ - PreambleEncode(strm); - - m_redirectIEinfo.Encode(strm); - - UnknownExtensionsEncode(strm); -} - - -PObject * CISCO_H225_CommonParam::Clone() const -{ -#ifndef PASN_LEANANDMEAN - PAssert(IsClass(CISCO_H225_CommonParam::Class()), PInvalidCast); -#endif - return new CISCO_H225_CommonParam(*this); -} - - -// -// ProgIndParam -// - -CISCO_H225_ProgIndParam::CISCO_H225_ProgIndParam(unsigned tag, PASN_Object::TagClass tagClass) - : PASN_Sequence(tag, tagClass, 0, TRUE, 0) -{ -} - - -#ifndef PASN_NOPRINTON -void CISCO_H225_ProgIndParam::PrintOn(ostream & strm) const -{ - int indent = strm.precision() + 2; - strm << "{\n"; - strm << setw(indent+16) << "progIndIEinfo = " << setprecision(indent) << m_progIndIEinfo << '\n'; - strm << setw(indent-1) << setprecision(indent-2) << "}"; -} -#endif - - -PObject::Comparison CISCO_H225_ProgIndParam::Compare(const PObject & obj) const -{ -#ifndef PASN_LEANANDMEAN - PAssert(PIsDescendant(&obj, CISCO_H225_ProgIndParam), PInvalidCast); -#endif - const CISCO_H225_ProgIndParam & other = (const CISCO_H225_ProgIndParam &)obj; - - Comparison result; - - if ((result = m_progIndIEinfo.Compare(other.m_progIndIEinfo)) != EqualTo) - return result; - - return PASN_Sequence::Compare(other); -} - - -PINDEX CISCO_H225_ProgIndParam::GetDataLength() const -{ - PINDEX length = 0; - length += m_progIndIEinfo.GetObjectLength(); - return length; -} - - -BOOL CISCO_H225_ProgIndParam::Decode(PASN_Stream & strm) -{ - if (!PreambleDecode(strm)) - return FALSE; - - if (!m_progIndIEinfo.Decode(strm)) - return FALSE; - - return UnknownExtensionsDecode(strm); -} - - -void CISCO_H225_ProgIndParam::Encode(PASN_Stream & strm) const -{ - PreambleEncode(strm); - - m_progIndIEinfo.Encode(strm); - - UnknownExtensionsEncode(strm); -} - - -PObject * CISCO_H225_ProgIndParam::Clone() const -{ -#ifndef PASN_LEANANDMEAN - PAssert(IsClass(CISCO_H225_ProgIndParam::Class()), PInvalidCast); -#endif - return new CISCO_H225_ProgIndParam(*this); -} - - -// -// ProtoParam -// - -CISCO_H225_ProtoParam::CISCO_H225_ProtoParam(unsigned tag, PASN_Object::TagClass tagClass) - : PASN_Sequence(tag, tagClass, 0, TRUE, 0) -{ -} - - -#ifndef PASN_NOPRINTON -void CISCO_H225_ProtoParam::PrintOn(ostream & strm) const -{ - int indent = strm.precision() + 2; - strm << "{\n"; - strm << setw(indent+17) << "qsigNonStdInfo = " << setprecision(indent) << m_qsigNonStdInfo << '\n'; - strm << setw(indent-1) << setprecision(indent-2) << "}"; -} -#endif - - -PObject::Comparison CISCO_H225_ProtoParam::Compare(const PObject & obj) const -{ -#ifndef PASN_LEANANDMEAN - PAssert(PIsDescendant(&obj, CISCO_H225_ProtoParam), PInvalidCast); -#endif - const CISCO_H225_ProtoParam & other = (const CISCO_H225_ProtoParam &)obj; - - Comparison result; - - if ((result = m_qsigNonStdInfo.Compare(other.m_qsigNonStdInfo)) != EqualTo) - return result; - - return PASN_Sequence::Compare(other); -} - - -PINDEX CISCO_H225_ProtoParam::GetDataLength() const -{ - PINDEX length = 0; - length += m_qsigNonStdInfo.GetObjectLength(); - return length; -} - - -BOOL CISCO_H225_ProtoParam::Decode(PASN_Stream & strm) -{ - if (!PreambleDecode(strm)) - return FALSE; - - if (!m_qsigNonStdInfo.Decode(strm)) - return FALSE; - - return UnknownExtensionsDecode(strm); -} - - -void CISCO_H225_ProtoParam::Encode(PASN_Stream & strm) const -{ - PreambleEncode(strm); - - m_qsigNonStdInfo.Encode(strm); - - UnknownExtensionsEncode(strm); -} - - -PObject * CISCO_H225_ProtoParam::Clone() const -{ -#ifndef PASN_LEANANDMEAN - PAssert(IsClass(CISCO_H225_ProtoParam::Class()), PInvalidCast); -#endif - return new CISCO_H225_ProtoParam(*this); -} - - -// -// H323_UU_NonStdInfo -// - -CISCO_H225_H323_UU_NonStdInfo::CISCO_H225_H323_UU_NonStdInfo(unsigned tag, PASN_Object::TagClass tagClass) - : PASN_Sequence(tag, tagClass, 3, TRUE, 6) -{ -} - - -#ifndef PASN_NOPRINTON -void CISCO_H225_H323_UU_NonStdInfo::PrintOn(ostream & strm) const -{ - int indent = strm.precision() + 2; - strm << "{\n"; - if (HasOptionalField(e_version)) - strm << setw(indent+10) << "version = " << setprecision(indent) << m_version << '\n'; - if (HasOptionalField(e_protoParam)) - strm << setw(indent+13) << "protoParam = " << setprecision(indent) << m_protoParam << '\n'; - if (HasOptionalField(e_commonParam)) - strm << setw(indent+14) << "commonParam = " << setprecision(indent) << m_commonParam << '\n'; - if (HasOptionalField(e_dummy1)) - strm << setw(indent+9) << "dummy1 = " << setprecision(indent) << m_dummy1 << '\n'; - if (HasOptionalField(e_progIndParam)) - strm << setw(indent+15) << "progIndParam = " << setprecision(indent) << m_progIndParam << '\n'; - if (HasOptionalField(e_callMgrParam)) - strm << setw(indent+15) << "callMgrParam = " << setprecision(indent) << m_callMgrParam << '\n'; - if (HasOptionalField(e_callSignallingParam)) - strm << setw(indent+22) << "callSignallingParam = " << setprecision(indent) << m_callSignallingParam << '\n'; - if (HasOptionalField(e_dummy2)) - strm << setw(indent+9) << "dummy2 = " << setprecision(indent) << m_dummy2 << '\n'; - if (HasOptionalField(e_callPreserveParam)) - strm << setw(indent+20) << "callPreserveParam = " << setprecision(indent) << m_callPreserveParam << '\n'; - strm << setw(indent-1) << setprecision(indent-2) << "}"; -} -#endif - - -PObject::Comparison CISCO_H225_H323_UU_NonStdInfo::Compare(const PObject & obj) const -{ -#ifndef PASN_LEANANDMEAN - PAssert(PIsDescendant(&obj, CISCO_H225_H323_UU_NonStdInfo), PInvalidCast); -#endif - const CISCO_H225_H323_UU_NonStdInfo & other = (const CISCO_H225_H323_UU_NonStdInfo &)obj; - - Comparison result; - - if ((result = m_version.Compare(other.m_version)) != EqualTo) - return result; - if ((result = m_protoParam.Compare(other.m_protoParam)) != EqualTo) - return result; - if ((result = m_commonParam.Compare(other.m_commonParam)) != EqualTo) - return result; - - return PASN_Sequence::Compare(other); -} - - -PINDEX CISCO_H225_H323_UU_NonStdInfo::GetDataLength() const -{ - PINDEX length = 0; - if (HasOptionalField(e_version)) - length += m_version.GetObjectLength(); - if (HasOptionalField(e_protoParam)) - length += m_protoParam.GetObjectLength(); - if (HasOptionalField(e_commonParam)) - length += m_commonParam.GetObjectLength(); - return length; -} - - -BOOL CISCO_H225_H323_UU_NonStdInfo::Decode(PASN_Stream & strm) -{ - if (!PreambleDecode(strm)) - return FALSE; - - if (HasOptionalField(e_version) && !m_version.Decode(strm)) - return FALSE; - if (HasOptionalField(e_protoParam) && !m_protoParam.Decode(strm)) - return FALSE; - if (HasOptionalField(e_commonParam) && !m_commonParam.Decode(strm)) - return FALSE; - if (!KnownExtensionDecode(strm, e_dummy1, m_dummy1)) - return FALSE; - if (!KnownExtensionDecode(strm, e_progIndParam, m_progIndParam)) - return FALSE; - if (!KnownExtensionDecode(strm, e_callMgrParam, m_callMgrParam)) - return FALSE; - if (!KnownExtensionDecode(strm, e_callSignallingParam, m_callSignallingParam)) - return FALSE; - if (!KnownExtensionDecode(strm, e_dummy2, m_dummy2)) - return FALSE; - if (!KnownExtensionDecode(strm, e_callPreserveParam, m_callPreserveParam)) - return FALSE; - - return UnknownExtensionsDecode(strm); -} - - -void CISCO_H225_H323_UU_NonStdInfo::Encode(PASN_Stream & strm) const -{ - PreambleEncode(strm); - - if (HasOptionalField(e_version)) - m_version.Encode(strm); - if (HasOptionalField(e_protoParam)) - m_protoParam.Encode(strm); - if (HasOptionalField(e_commonParam)) - m_commonParam.Encode(strm); - KnownExtensionEncode(strm, e_dummy1, m_dummy1); - KnownExtensionEncode(strm, e_progIndParam, m_progIndParam); - KnownExtensionEncode(strm, e_callMgrParam, m_callMgrParam); - KnownExtensionEncode(strm, e_callSignallingParam, m_callSignallingParam); - KnownExtensionEncode(strm, e_dummy2, m_dummy2); - KnownExtensionEncode(strm, e_callPreserveParam, m_callPreserveParam); - - UnknownExtensionsEncode(strm); -} - - -PObject * CISCO_H225_H323_UU_NonStdInfo::Clone() const -{ -#ifndef PASN_LEANANDMEAN - PAssert(IsClass(CISCO_H225_H323_UU_NonStdInfo::Class()), PInvalidCast); -#endif - return new CISCO_H225_H323_UU_NonStdInfo(*this); -} - - -#endif // if ! H323_DISABLE_CISCO_H225 - - -// End of cisco-h225.cxx diff --git a/1.4.23-rc4/channels/h323/cisco-h225.h b/1.4.23-rc4/channels/h323/cisco-h225.h deleted file mode 100644 index 7595b4b65..000000000 --- a/1.4.23-rc4/channels/h323/cisco-h225.h +++ /dev/null @@ -1,299 +0,0 @@ -// -// cisco-h225.h -// -// Code automatically generated by asnparse. -// - -#if ! H323_DISABLE_CISCO_H225 - -#ifndef __CISCO_H225_H -#define __CISCO_H225_H - -#ifdef P_USE_PRAGMA -#pragma interface -#endif - -#include <ptclib/asner.h> - -// -// RedirectIEinfo -// - -class CISCO_H225_RedirectIEinfo : public PASN_Sequence -{ -#ifndef PASN_LEANANDMEAN - PCLASSINFO(CISCO_H225_RedirectIEinfo, PASN_Sequence); -#endif - public: - CISCO_H225_RedirectIEinfo(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass); - - PASN_OctetString m_redirectIE; - - PINDEX GetDataLength() const; - BOOL Decode(PASN_Stream & strm); - void Encode(PASN_Stream & strm) const; -#ifndef PASN_NOPRINTON - void PrintOn(ostream & strm) const; -#endif - Comparison Compare(const PObject & obj) const; - PObject * Clone() const; -}; - - -// -// ProgIndIEinfo -// - -class CISCO_H225_ProgIndIEinfo : public PASN_Sequence -{ -#ifndef PASN_LEANANDMEAN - PCLASSINFO(CISCO_H225_ProgIndIEinfo, PASN_Sequence); -#endif - public: - CISCO_H225_ProgIndIEinfo(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass); - - PASN_OctetString m_progIndIE; - - PINDEX GetDataLength() const; - BOOL Decode(PASN_Stream & strm); - void Encode(PASN_Stream & strm) const; -#ifndef PASN_NOPRINTON - void PrintOn(ostream & strm) const; -#endif - Comparison Compare(const PObject & obj) const; - PObject * Clone() const; -}; - - -// -// QsigNonStdInfo -// - -class CISCO_H225_QsigNonStdInfo : public PASN_Sequence -{ -#ifndef PASN_LEANANDMEAN - PCLASSINFO(CISCO_H225_QsigNonStdInfo, PASN_Sequence); -#endif - public: - CISCO_H225_QsigNonStdInfo(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass); - - PASN_Integer m_iei; - PASN_OctetString m_rawMesg; - - PINDEX GetDataLength() const; - BOOL Decode(PASN_Stream & strm); - void Encode(PASN_Stream & strm) const; -#ifndef PASN_NOPRINTON - void PrintOn(ostream & strm) const; -#endif - Comparison Compare(const PObject & obj) const; - PObject * Clone() const; -}; - - -// -// CallMgrParam -// - -class CISCO_H225_CallMgrParam : public PASN_Sequence -{ -#ifndef PASN_LEANANDMEAN - PCLASSINFO(CISCO_H225_CallMgrParam, PASN_Sequence); -#endif - public: - CISCO_H225_CallMgrParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass); - - PASN_Integer m_interclusterVersion; - PASN_OctetString m_enterpriseID; - - PINDEX GetDataLength() const; - BOOL Decode(PASN_Stream & strm); - void Encode(PASN_Stream & strm) const; -#ifndef PASN_NOPRINTON - void PrintOn(ostream & strm) const; -#endif - Comparison Compare(const PObject & obj) const; - PObject * Clone() const; -}; - - -// -// CallPreserveParam -// - -class CISCO_H225_CallPreserveParam : public PASN_Sequence -{ -#ifndef PASN_LEANANDMEAN - PCLASSINFO(CISCO_H225_CallPreserveParam, PASN_Sequence); -#endif - public: - CISCO_H225_CallPreserveParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass); - - PASN_Boolean m_callPreserveIE; - - PINDEX GetDataLength() const; - BOOL Decode(PASN_Stream & strm); - void Encode(PASN_Stream & strm) const; -#ifndef PASN_NOPRINTON - void PrintOn(ostream & strm) const; -#endif - Comparison Compare(const PObject & obj) const; - PObject * Clone() const; -}; - - -// -// CallSignallingParam -// - -class CISCO_H225_CallSignallingParam : public PASN_Sequence -{ -#ifndef PASN_LEANANDMEAN - PCLASSINFO(CISCO_H225_CallSignallingParam, PASN_Sequence); -#endif - public: - CISCO_H225_CallSignallingParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass); - - enum OptionalFields { - e_connectedNumber - }; - - PASN_OctetString m_connectedNumber; - - PINDEX GetDataLength() const; - BOOL Decode(PASN_Stream & strm); - void Encode(PASN_Stream & strm) const; -#ifndef PASN_NOPRINTON - void PrintOn(ostream & strm) const; -#endif - Comparison Compare(const PObject & obj) const; - PObject * Clone() const; -}; - - -// -// CommonParam -// - -class CISCO_H225_CommonParam : public PASN_Sequence -{ -#ifndef PASN_LEANANDMEAN - PCLASSINFO(CISCO_H225_CommonParam, PASN_Sequence); -#endif - public: - CISCO_H225_CommonParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass); - - CISCO_H225_RedirectIEinfo m_redirectIEinfo; - - PINDEX GetDataLength() const; - BOOL Decode(PASN_Stream & strm); - void Encode(PASN_Stream & strm) const; -#ifndef PASN_NOPRINTON - void PrintOn(ostream & strm) const; -#endif - Comparison Compare(const PObject & obj) const; - PObject * Clone() const; -}; - - -// -// ProgIndParam -// - -class CISCO_H225_ProgIndParam : public PASN_Sequence -{ -#ifndef PASN_LEANANDMEAN - PCLASSINFO(CISCO_H225_ProgIndParam, PASN_Sequence); -#endif - public: - CISCO_H225_ProgIndParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass); - - CISCO_H225_ProgIndIEinfo m_progIndIEinfo; - - PINDEX GetDataLength() const; - BOOL Decode(PASN_Stream & strm); - void Encode(PASN_Stream & strm) const; -#ifndef PASN_NOPRINTON - void PrintOn(ostream & strm) const; -#endif - Comparison Compare(const PObject & obj) const; - PObject * Clone() const; -}; - - -// -// ProtoParam -// - -class CISCO_H225_ProtoParam : public PASN_Sequence -{ -#ifndef PASN_LEANANDMEAN - PCLASSINFO(CISCO_H225_ProtoParam, PASN_Sequence); -#endif - public: - CISCO_H225_ProtoParam(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass); - - CISCO_H225_QsigNonStdInfo m_qsigNonStdInfo; - - PINDEX GetDataLength() const; - BOOL Decode(PASN_Stream & strm); - void Encode(PASN_Stream & strm) const; -#ifndef PASN_NOPRINTON - void PrintOn(ostream & strm) const; -#endif - Comparison Compare(const PObject & obj) const; - PObject * Clone() const; -}; - - -// -// H323_UU_NonStdInfo -// - -class CISCO_H225_H323_UU_NonStdInfo : public PASN_Sequence -{ -#ifndef PASN_LEANANDMEAN - PCLASSINFO(CISCO_H225_H323_UU_NonStdInfo, PASN_Sequence); -#endif - public: - CISCO_H225_H323_UU_NonStdInfo(unsigned tag = UniversalSequence, TagClass tagClass = UniversalTagClass); - - enum OptionalFields { - e_version, - e_protoParam, - e_commonParam, - e_dummy1, - e_progIndParam, - e_callMgrParam, - e_callSignallingParam, - e_dummy2, - e_callPreserveParam - }; - - PASN_Integer m_version; - CISCO_H225_ProtoParam m_protoParam; - CISCO_H225_CommonParam m_commonParam; - PASN_OctetString m_dummy1; - CISCO_H225_ProgIndParam m_progIndParam; - CISCO_H225_CallMgrParam m_callMgrParam; - CISCO_H225_CallSignallingParam m_callSignallingParam; - PASN_OctetString m_dummy2; - CISCO_H225_CallPreserveParam m_callPreserveParam; - - PINDEX GetDataLength() const; - BOOL Decode(PASN_Stream & strm); - void Encode(PASN_Stream & strm) const; -#ifndef PASN_NOPRINTON - void PrintOn(ostream & strm) const; -#endif - Comparison Compare(const PObject & obj) const; - PObject * Clone() const; -}; - - -#endif // __CISCO_H225_H - -#endif // if ! H323_DISABLE_CISCO_H225 - - -// End of cisco-h225.h diff --git a/1.4.23-rc4/channels/h323/compat_h323.cxx b/1.4.23-rc4/channels/h323/compat_h323.cxx deleted file mode 100644 index eec7361b2..000000000 --- a/1.4.23-rc4/channels/h323/compat_h323.cxx +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -/* - * ast_h323.cpp - * - * OpenH323 Channel Driver for ASTERISK PBX. - * By Jeremy McNamara - * For The NuFone Network - * - * chan_h323 has been derived from code created by - * Michael Manousos and Mark Spencer - * - * This file is part of the chan_h323 driver for Asterisk - * - * chan_h323 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. - * - * chan_h323 is distributed WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Version Info: $Id$ - */ -#include <ptlib.h> -#include <h323.h> -#include <transports.h> - -#include "ast_h323.h" - -#if VERSION(OPENH323_MAJOR,OPENH323_MINOR,OPENH323_BUILD) < VERSION(1,17,3) -MyH323TransportTCP::MyH323TransportTCP( - H323EndPoint & endpoint, - PIPSocket::Address binding, - BOOL listen) - : H323TransportTCP(endpoint, binding, listen) -{ -} - -BOOL MyH323TransportTCP::Connect() -{ - if (IsListening()) - return TRUE; - - PTCPSocket * socket = new PTCPSocket(remotePort); - Open(socket); - - channelPointerMutex.StartRead(); - - socket->SetReadTimeout(10000/*endpoint.GetSignallingChannelConnectTimeout()*/); - - localPort = endpoint.GetNextTCPPort(); - WORD firstPort = localPort; - for (;;) { - PTRACE(4, "H323TCP\tConnecting to " - << remoteAddress << ':' << remotePort - << " (local port=" << localPort << ')'); - if (socket->Connect(localAddress, localPort, remoteAddress)) - break; - - int errnum = socket->GetErrorNumber(); - if (localPort == 0 || (errnum != EADDRINUSE && errnum != EADDRNOTAVAIL)) { - PTRACE(1, "H323TCP\tCould not connect to " - << remoteAddress << ':' << remotePort - << " (local port=" << localPort << ") - " - << socket->GetErrorText() << '(' << errnum << ')'); - channelPointerMutex.EndRead(); - return SetErrorValues(socket->GetErrorCode(), errnum); - } - - localPort = endpoint.GetNextTCPPort(); - if (localPort == firstPort) { - PTRACE(1, "H323TCP\tCould not bind to any port in range " << - endpoint.GetTCPPortBase() << " to " << endpoint.GetTCPPortMax()); - channelPointerMutex.EndRead(); - return SetErrorValues(socket->GetErrorCode(), errnum); - } - } - - socket->SetReadTimeout(PMaxTimeInterval); - - channelPointerMutex.EndRead(); - - return OnOpen(); -} -#endif - -BOOL MyH323TransportUDP::DiscoverGatekeeper(H323Gatekeeper &gk, H323RasPDU &pdu, const H323TransportAddress &address) -{ - PThread *thd = PThread::Current(); - - /* If we run in OpenH323's thread use it instead of creating new one */ - if (thd) - return H323TransportUDP::DiscoverGatekeeper(gk, pdu, address); - - /* Make copy of arguments to pass them into thread */ - discoverGatekeeper = &gk; - discoverPDU = &pdu; - discoverAddress = &address; - - /* Assume discovery thread isn't finished */ - discoverReady = FALSE; - - /* Create discovery thread */ - thd = PThread::Create(PCREATE_NOTIFIER(DiscoverMain), 0, - PThread::NoAutoDeleteThread, - PThread::NormalPriority, - "GkDiscovery:%x"); - - /* Wait until discovery thread signal us its finished */ - for(;;) { - discoverMutex.Wait(); - if (discoverReady) /* Thread has been finished */ - break; - discoverMutex.Signal(); - } - discoverMutex.Signal(); - - /* Cleanup/delete thread */ - thd->WaitForTermination(); - delete thd; - - return discoverResult; -} - -void MyH323TransportUDP::DiscoverMain(PThread &thread, INT arg) -{ - PWaitAndSignal m(discoverMutex); - - discoverResult = H323TransportUDP::DiscoverGatekeeper(*discoverGatekeeper, *discoverPDU, *discoverAddress); - discoverReady = TRUE; -} diff --git a/1.4.23-rc4/channels/h323/compat_h323.h b/1.4.23-rc4/channels/h323/compat_h323.h deleted file mode 100644 index 63da8ac8c..000000000 --- a/1.4.23-rc4/channels/h323/compat_h323.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef COMPAT_H323_H -#define COMPAT_H323_H - -#if VERSION(OPENH323_MAJOR,OPENH323_MINOR,OPENH323_BUILD) < VERSION(1,17,3) -/** - * Workaround for broken (less than 1.17.3) OpenH323 stack to be able to - * make TCP connections from specific address - */ -class MyH323TransportTCP : public H323TransportTCP -{ - PCLASSINFO(MyH323TransportTCP, H323TransportTCP); - -public: - MyH323TransportTCP( - H323EndPoint & endpoint, ///< H323 End Point object - PIPSocket::Address binding = PIPSocket::GetDefaultIpAny(), ///< Local interface to use - BOOL listen = FALSE ///< Flag for need to wait for remote to connect - ); - /**Connect to the remote party. - */ - virtual BOOL Connect(); -}; -#else -#define MyH323TransportTCP H323TransportTCP -#endif /* <VERSION(1,17,3) */ - -class MyH323TransportUDP: public H323TransportUDP -{ - PCLASSINFO(MyH323TransportUDP, H323TransportUDP); - -public: - MyH323TransportUDP(H323EndPoint &endpoint, - PIPSocket::Address binding = PIPSocket::GetDefaultIpAny(), - WORD localPort = 0, - WORD remotePort = 0): H323TransportUDP(endpoint, binding, localPort, remotePort) - { - } - virtual BOOL DiscoverGatekeeper(H323Gatekeeper &, - H323RasPDU &, - const H323TransportAddress &); -protected: - PDECLARE_NOTIFIER(PThread, MyH323TransportUDP, DiscoverMain); - H323Gatekeeper *discoverGatekeeper; - H323RasPDU *discoverPDU; - const H323TransportAddress *discoverAddress; - BOOL discoverResult; - BOOL discoverReady; - PMutex discoverMutex; -}; - -template <class _Abstract_T, typename _Key_T = PString> -class MyPFactory: public PFactory<_Abstract_T, _Key_T> -{ -public: - template <class _Concrete_T> class Worker: public PFactory<_Abstract_T, _Key_T>::WorkerBase - { - public: - Worker(const _Key_T &_key, bool singleton = false) - :PFactory<_Abstract_T, _Key_T>::WorkerBase(singleton), key(_key) - { - PFactory<_Abstract_T, _Key_T>::Register(key, this); - } - ~Worker() - { - PFactory<_Abstract_T, _Key_T>::Unregister(key); - } - protected: - virtual _Abstract_T *Create(const _Key_T &) const { return new _Concrete_T; } - - private: - PString key; - }; -}; - -#ifdef H323_REGISTER_CAPABILITY -#undef H323_REGISTER_CAPABILITY -#endif -#define H323_REGISTER_CAPABILITY(cls, capName) static MyPFactory<H323Capability>::Worker<cls> cls##Factory(capName, true) - -#endif /* !defined AST_H323_H */ diff --git a/1.4.23-rc4/channels/h323/noexport.map b/1.4.23-rc4/channels/h323/noexport.map deleted file mode 100644 index b51f84263..000000000 --- a/1.4.23-rc4/channels/h323/noexport.map +++ /dev/null @@ -1,5 +0,0 @@ -{ - global: - _Z11PAssertFuncPKc; - local: *; -};
\ No newline at end of file diff --git a/1.4.23-rc4/channels/iax2-parser.c b/1.4.23-rc4/channels/iax2-parser.c deleted file mode 100644 index 67bffa897..000000000 --- a/1.4.23-rc4/channels/iax2-parser.c +++ /dev/null @@ -1,1053 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief Implementation of Inter-Asterisk eXchange Protocol, v 2 - * - * \author Mark Spencer <markster@digium.com> - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <sys/types.h> -#include <sys/socket.h> -#include <string.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> - -#include "asterisk/frame.h" -#include "asterisk/utils.h" -#include "asterisk/unaligned.h" -#include "asterisk/lock.h" -#include "asterisk/threadstorage.h" - -#include "iax2.h" -#include "iax2-parser.h" -#include "iax2-provision.h" - -static int frames = 0; -static int iframes = 0; -static int oframes = 0; - -#if !defined(LOW_MEMORY) -static void frame_cache_cleanup(void *data); - -/*! \brief A per-thread cache of iax_frame structures */ -AST_THREADSTORAGE_CUSTOM(frame_cache, frame_cache_init, frame_cache_cleanup); - -/*! \brief This is just so iax_frames, a list head struct for holding a list of - * iax_frame structures, is defined. */ -AST_LIST_HEAD_NOLOCK(iax_frame_list, iax_frame); - -struct iax_frames { - struct iax_frame_list list; - size_t size; -}; - -#define FRAME_CACHE_MAX_SIZE 20 -#endif - -static void internaloutput(const char *str) -{ - fputs(str, stdout); -} - -static void internalerror(const char *str) -{ - fprintf(stderr, "WARNING: %s", str); -} - -static void (*outputf)(const char *str) = internaloutput; -static void (*errorf)(const char *str) = internalerror; - -static void dump_addr(char *output, int maxlen, void *value, int len) -{ - struct sockaddr_in sin; - if (len == (int)sizeof(sin)) { - memcpy(&sin, value, len); - snprintf(output, maxlen, "IPV4 %s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - } else { - snprintf(output, maxlen, "Invalid Address"); - } -} - -static void dump_string(char *output, int maxlen, void *value, int len) -{ - maxlen--; - if (maxlen > len) - maxlen = len; - strncpy(output, value, maxlen); - output[maxlen] = '\0'; -} - -static void dump_prefs(char *output, int maxlen, void *value, int len) -{ - struct ast_codec_pref pref; - int total_len = 0; - - maxlen--; - total_len = maxlen; - - if (maxlen > len) - maxlen = len; - - strncpy(output, value, maxlen); - output[maxlen] = '\0'; - - ast_codec_pref_convert(&pref, output, total_len, 0); - memset(output,0,total_len); - ast_codec_pref_string(&pref, output, total_len); -} - -static void dump_int(char *output, int maxlen, void *value, int len) -{ - if (len == (int)sizeof(unsigned int)) - snprintf(output, maxlen, "%lu", (unsigned long)ntohl(get_unaligned_uint32(value))); - else - ast_copy_string(output, "Invalid INT", maxlen); -} - -static void dump_short(char *output, int maxlen, void *value, int len) -{ - if (len == (int)sizeof(unsigned short)) - snprintf(output, maxlen, "%d", ntohs(get_unaligned_uint16(value))); - else - ast_copy_string(output, "Invalid SHORT", maxlen); -} - -static void dump_byte(char *output, int maxlen, void *value, int len) -{ - if (len == (int)sizeof(unsigned char)) - snprintf(output, maxlen, "%d", *((unsigned char *)value)); - else - ast_copy_string(output, "Invalid BYTE", maxlen); -} - -static void dump_datetime(char *output, int maxlen, void *value, int len) -{ - struct tm tm; - unsigned long val = (unsigned long) ntohl(get_unaligned_uint32(value)); - if (len == (int)sizeof(unsigned int)) { - tm.tm_sec = (val & 0x1f) << 1; - tm.tm_min = (val >> 5) & 0x3f; - tm.tm_hour = (val >> 11) & 0x1f; - tm.tm_mday = (val >> 16) & 0x1f; - tm.tm_mon = ((val >> 21) & 0x0f) - 1; - tm.tm_year = ((val >> 25) & 0x7f) + 100; - strftime(output, maxlen, "%Y-%m-%d %T", &tm); - } else - ast_copy_string(output, "Invalid DATETIME format!", maxlen); -} - -static void dump_ipaddr(char *output, int maxlen, void *value, int len) -{ - struct sockaddr_in sin; - if (len == (int)sizeof(unsigned int)) { - memcpy(&sin.sin_addr, value, len); - snprintf(output, maxlen, "%s", ast_inet_ntoa(sin.sin_addr)); - } else - ast_copy_string(output, "Invalid IPADDR", maxlen); -} - - -static void dump_prov_flags(char *output, int maxlen, void *value, int len) -{ - char buf[256] = ""; - if (len == (int)sizeof(unsigned int)) - snprintf(output, maxlen, "%lu (%s)", (unsigned long)ntohl(get_unaligned_uint32(value)), - iax_provflags2str(buf, sizeof(buf), ntohl(get_unaligned_uint32(value)))); - else - ast_copy_string(output, "Invalid INT", maxlen); -} - -static void dump_samprate(char *output, int maxlen, void *value, int len) -{ - char tmp[256]=""; - int sr; - if (len == (int)sizeof(unsigned short)) { - sr = ntohs(*((unsigned short *)value)); - if (sr & IAX_RATE_8KHZ) - strcat(tmp, ",8khz"); - if (sr & IAX_RATE_11KHZ) - strcat(tmp, ",11.025khz"); - if (sr & IAX_RATE_16KHZ) - strcat(tmp, ",16khz"); - if (sr & IAX_RATE_22KHZ) - strcat(tmp, ",22.05khz"); - if (sr & IAX_RATE_44KHZ) - strcat(tmp, ",44.1khz"); - if (sr & IAX_RATE_48KHZ) - strcat(tmp, ",48khz"); - if (strlen(tmp)) - ast_copy_string(output, &tmp[1], maxlen); - else - ast_copy_string(output, "None Specified!\n", maxlen); - } else - ast_copy_string(output, "Invalid SHORT", maxlen); - -} - -static void dump_prov_ies(char *output, int maxlen, unsigned char *iedata, int len); -static void dump_prov(char *output, int maxlen, void *value, int len) -{ - dump_prov_ies(output, maxlen, value, len); -} - -static struct iax2_ie { - int ie; - char *name; - void (*dump)(char *output, int maxlen, void *value, int len); -} ies[] = { - { IAX_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string }, - { IAX_IE_CALLING_NUMBER, "CALLING NUMBER", dump_string }, - { IAX_IE_CALLING_ANI, "ANI", dump_string }, - { IAX_IE_CALLING_NAME, "CALLING NAME", dump_string }, - { IAX_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string }, - { IAX_IE_USERNAME, "USERNAME", dump_string }, - { IAX_IE_PASSWORD, "PASSWORD", dump_string }, - { IAX_IE_CAPABILITY, "CAPABILITY", dump_int }, - { IAX_IE_FORMAT, "FORMAT", dump_int }, - { IAX_IE_LANGUAGE, "LANGUAGE", dump_string }, - { IAX_IE_VERSION, "VERSION", dump_short }, - { IAX_IE_ADSICPE, "ADSICPE", dump_short }, - { IAX_IE_DNID, "DNID", dump_string }, - { IAX_IE_AUTHMETHODS, "AUTHMETHODS", dump_short }, - { IAX_IE_CHALLENGE, "CHALLENGE", dump_string }, - { IAX_IE_MD5_RESULT, "MD5 RESULT", dump_string }, - { IAX_IE_RSA_RESULT, "RSA RESULT", dump_string }, - { IAX_IE_APPARENT_ADDR, "APPARENT ADDRESS", dump_addr }, - { IAX_IE_REFRESH, "REFRESH", dump_short }, - { IAX_IE_DPSTATUS, "DIALPLAN STATUS", dump_short }, - { IAX_IE_CALLNO, "CALL NUMBER", dump_short }, - { IAX_IE_CAUSE, "CAUSE", dump_string }, - { IAX_IE_IAX_UNKNOWN, "UNKNOWN IAX CMD", dump_byte }, - { IAX_IE_MSGCOUNT, "MESSAGE COUNT", dump_short }, - { IAX_IE_AUTOANSWER, "AUTO ANSWER REQ" }, - { IAX_IE_TRANSFERID, "TRANSFER ID", dump_int }, - { IAX_IE_RDNIS, "REFERRING DNIS", dump_string }, - { IAX_IE_PROVISIONING, "PROVISIONING", dump_prov }, - { IAX_IE_AESPROVISIONING, "AES PROVISIONG" }, - { IAX_IE_DATETIME, "DATE TIME", dump_datetime }, - { IAX_IE_DEVICETYPE, "DEVICE TYPE", dump_string }, - { IAX_IE_SERVICEIDENT, "SERVICE IDENT", dump_string }, - { IAX_IE_FIRMWAREVER, "FIRMWARE VER", dump_short }, - { IAX_IE_FWBLOCKDESC, "FW BLOCK DESC", dump_int }, - { IAX_IE_FWBLOCKDATA, "FW BLOCK DATA" }, - { IAX_IE_PROVVER, "PROVISIONG VER", dump_int }, - { IAX_IE_CALLINGPRES, "CALLING PRESNTN", dump_byte }, - { IAX_IE_CALLINGTON, "CALLING TYPEOFNUM", dump_byte }, - { IAX_IE_CALLINGTNS, "CALLING TRANSITNET", dump_short }, - { IAX_IE_SAMPLINGRATE, "SAMPLINGRATE", dump_samprate }, - { IAX_IE_CAUSECODE, "CAUSE CODE", dump_byte }, - { IAX_IE_ENCRYPTION, "ENCRYPTION", dump_short }, - { IAX_IE_ENCKEY, "ENCRYPTION KEY" }, - { IAX_IE_CODEC_PREFS, "CODEC_PREFS", dump_prefs }, - { IAX_IE_RR_JITTER, "RR_JITTER", dump_int }, - { IAX_IE_RR_LOSS, "RR_LOSS", dump_int }, - { IAX_IE_RR_PKTS, "RR_PKTS", dump_int }, - { IAX_IE_RR_DELAY, "RR_DELAY", dump_short }, - { IAX_IE_RR_DROPPED, "RR_DROPPED", dump_int }, - { IAX_IE_RR_OOO, "RR_OUTOFORDER", dump_int }, -}; - -static struct iax2_ie prov_ies[] = { - { PROV_IE_USEDHCP, "USEDHCP" }, - { PROV_IE_IPADDR, "IPADDR", dump_ipaddr }, - { PROV_IE_SUBNET, "SUBNET", dump_ipaddr }, - { PROV_IE_GATEWAY, "GATEWAY", dump_ipaddr }, - { PROV_IE_PORTNO, "BINDPORT", dump_short }, - { PROV_IE_USER, "USERNAME", dump_string }, - { PROV_IE_PASS, "PASSWORD", dump_string }, - { PROV_IE_LANG, "LANGUAGE", dump_string }, - { PROV_IE_TOS, "TYPEOFSERVICE", dump_byte }, - { PROV_IE_FLAGS, "FLAGS", dump_prov_flags }, - { PROV_IE_FORMAT, "FORMAT", dump_int }, - { PROV_IE_AESKEY, "AESKEY" }, - { PROV_IE_SERVERIP, "SERVERIP", dump_ipaddr }, - { PROV_IE_SERVERPORT, "SERVERPORT", dump_short }, - { PROV_IE_NEWAESKEY, "NEWAESKEY" }, - { PROV_IE_PROVVER, "PROV VERSION", dump_int }, - { PROV_IE_ALTSERVER, "ALTSERVERIP", dump_ipaddr }, -}; - -const char *iax_ie2str(int ie) -{ - int x; - for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) { - if (ies[x].ie == ie) - return ies[x].name; - } - return "Unknown IE"; -} - - -static void dump_prov_ies(char *output, int maxlen, unsigned char *iedata, int len) -{ - int ielen; - int ie; - int x; - int found; - char interp[80]; - char tmp[256]; - if (len < 2) - return; - strcpy(output, "\n"); - maxlen -= strlen(output); output += strlen(output); - while(len > 2) { - ie = iedata[0]; - ielen = iedata[1]; - if (ielen + 2> len) { - snprintf(tmp, (int)sizeof(tmp), "Total Prov IE length of %d bytes exceeds remaining prov frame length of %d bytes\n", ielen + 2, len); - ast_copy_string(output, tmp, maxlen); - maxlen -= strlen(output); - output += strlen(output); - return; - } - found = 0; - for (x=0;x<(int)sizeof(prov_ies) / (int)sizeof(prov_ies[0]); x++) { - if (prov_ies[x].ie == ie) { - if (prov_ies[x].dump) { - prov_ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen); - snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", prov_ies[x].name, interp); - ast_copy_string(output, tmp, maxlen); - maxlen -= strlen(output); output += strlen(output); - } else { - if (ielen) - snprintf(interp, (int)sizeof(interp), "%d bytes", ielen); - else - strcpy(interp, "Present"); - snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", prov_ies[x].name, interp); - ast_copy_string(output, tmp, maxlen); - maxlen -= strlen(output); output += strlen(output); - } - found++; - } - } - if (!found) { - snprintf(tmp, (int)sizeof(tmp), " Unknown Prov IE %03d : Present\n", ie); - ast_copy_string(output, tmp, maxlen); - maxlen -= strlen(output); output += strlen(output); - } - iedata += (2 + ielen); - len -= (2 + ielen); - } -} - -static void dump_ies(unsigned char *iedata, int len) -{ - int ielen; - int ie; - int x; - int found; - char interp[1024]; - char tmp[1024]; - if (len < 2) - return; - while(len > 2) { - ie = iedata[0]; - ielen = iedata[1]; - if (ielen + 2> len) { - snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len); - outputf(tmp); - return; - } - found = 0; - for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) { - if (ies[x].ie == ie) { - if (ies[x].dump) { - ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen); - snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", ies[x].name, interp); - outputf(tmp); - } else { - if (ielen) - snprintf(interp, (int)sizeof(interp), "%d bytes", ielen); - else - strcpy(interp, "Present"); - snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", ies[x].name, interp); - outputf(tmp); - } - found++; - } - } - if (!found) { - snprintf(tmp, (int)sizeof(tmp), " Unknown IE %03d : Present\n", ie); - outputf(tmp); - } - iedata += (2 + ielen); - len -= (2 + ielen); - } - outputf("\n"); -} - -void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen) -{ - const char *frames[] = { - "(0?)", - "DTMF_E ", - "VOICE ", - "VIDEO ", - "CONTROL", - "NULL ", - "IAX ", - "TEXT ", - "IMAGE ", - "HTML ", - "CNG ", - "MODEM ", - "DTMF_B ", - }; - const char *iaxs[] = { - "(0?)", - "NEW ", - "PING ", - "PONG ", - "ACK ", - "HANGUP ", - "REJECT ", - "ACCEPT ", - "AUTHREQ", - "AUTHREP", - "INVAL ", - "LAGRQ ", - "LAGRP ", - "REGREQ ", - "REGAUTH", - "REGACK ", - "REGREJ ", - "REGREL ", - "VNAK ", - "DPREQ ", - "DPREP ", - "DIAL ", - "TXREQ ", - "TXCNT ", - "TXACC ", - "TXREADY", - "TXREL ", - "TXREJ ", - "QUELCH ", - "UNQULCH", - "POKE ", - "PAGE ", - "MWI ", - "UNSPRTD", - "TRANSFR", - "PROVISN", - "FWDWNLD", - "FWDATA ", - "TXMEDIA" - }; - const char *cmds[] = { - "(0?)", - "HANGUP ", - "RING ", - "RINGING", - "ANSWER ", - "BUSY ", - "TKOFFHK", - "OFFHOOK", - "CONGSTN", - "FLASH ", - "WINK ", - "OPTION ", - "RDKEY ", - "RDUNKEY", - "PROGRES", - "PROCDNG", - "HOLD ", - "UNHOLD ", - "VIDUPDT", }; - struct ast_iax2_full_hdr *fh; - char retries[20]; - char class2[20]; - char subclass2[20]; - const char *class; - const char *subclass; - char *dir; - char tmp[512]; - - switch(rx) { - case 0: - dir = "Tx"; - break; - case 2: - dir = "TE"; - break; - case 3: - dir = "RD"; - break; - default: - dir = "Rx"; - break; - } - if (f) { - fh = f->data; - snprintf(retries, sizeof(retries), "%03d", f->retries); - } else { - fh = fhi; - if (ntohs(fh->dcallno) & IAX_FLAG_RETRANS) - strcpy(retries, "Yes"); - else - strcpy(retries, " No"); - } - if (!(ntohs(fh->scallno) & IAX_FLAG_FULL)) { - /* Don't mess with mini-frames */ - return; - } - if (fh->type >= (int)sizeof(frames)/(int)sizeof(frames[0])) { - snprintf(class2, sizeof(class2), "(%d?)", fh->type); - class = class2; - } else { - class = frames[(int)fh->type]; - } - if (fh->type == AST_FRAME_DTMF_BEGIN || fh->type == AST_FRAME_DTMF_END) { - sprintf(subclass2, "%c", fh->csub); - subclass = subclass2; - } else if (fh->type == AST_FRAME_IAX) { - if (fh->csub >= (int)sizeof(iaxs)/(int)sizeof(iaxs[0])) { - snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->csub); - subclass = subclass2; - } else { - subclass = iaxs[(int)fh->csub]; - } - } else if (fh->type == AST_FRAME_CONTROL) { - if (fh->csub >= (int)sizeof(cmds)/(int)sizeof(cmds[0])) { - snprintf(subclass2, sizeof(subclass2), "(%d?)", fh->csub); - subclass = subclass2; - } else { - subclass = cmds[(int)fh->csub]; - } - } else { - snprintf(subclass2, sizeof(subclass2), "%d", fh->csub); - subclass = subclass2; - } - snprintf(tmp, sizeof(tmp), - "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s Subclass: %s\n", - dir, - retries, fh->oseqno, fh->iseqno, class, subclass); - outputf(tmp); - snprintf(tmp, sizeof(tmp), - " Timestamp: %05lums SCall: %5.5d DCall: %5.5d [%s:%d]\n", - (unsigned long)ntohl(fh->ts), - ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS, - ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); - outputf(tmp); - if (fh->type == AST_FRAME_IAX) - dump_ies(fh->iedata, datalen); -} - -int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, const void *data, int datalen) -{ - char tmp[256]; - if (datalen > ((int)sizeof(ied->buf) - ied->pos)) { - snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", iax_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos); - errorf(tmp); - return -1; - } - ied->buf[ied->pos++] = ie; - ied->buf[ied->pos++] = datalen; - memcpy(ied->buf + ied->pos, data, datalen); - ied->pos += datalen; - return 0; -} - -int iax_ie_append_addr(struct iax_ie_data *ied, unsigned char ie, const struct sockaddr_in *sin) -{ - return iax_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in)); -} - -int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value) -{ - unsigned int newval; - newval = htonl(value); - return iax_ie_append_raw(ied, ie, &newval, (int)sizeof(newval)); -} - -int iax_ie_append_short(struct iax_ie_data *ied, unsigned char ie, unsigned short value) -{ - unsigned short newval; - newval = htons(value); - return iax_ie_append_raw(ied, ie, &newval, (int)sizeof(newval)); -} - -int iax_ie_append_str(struct iax_ie_data *ied, unsigned char ie, const char *str) -{ - return iax_ie_append_raw(ied, ie, str, strlen(str)); -} - -int iax_ie_append_byte(struct iax_ie_data *ied, unsigned char ie, unsigned char dat) -{ - return iax_ie_append_raw(ied, ie, &dat, 1); -} - -int iax_ie_append(struct iax_ie_data *ied, unsigned char ie) -{ - return iax_ie_append_raw(ied, ie, NULL, 0); -} - -void iax_set_output(void (*func)(const char *)) -{ - outputf = func; -} - -void iax_set_error(void (*func)(const char *)) -{ - errorf = func; -} - -int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen) -{ - /* Parse data into information elements */ - int len; - int ie; - char tmp[256]; - memset(ies, 0, (int)sizeof(struct iax_ies)); - ies->msgcount = -1; - ies->firmwarever = -1; - ies->calling_ton = -1; - ies->calling_tns = -1; - ies->calling_pres = -1; - ies->samprate = IAX_RATE_8KHZ; - while(datalen >= 2) { - ie = data[0]; - len = data[1]; - if (len > datalen - 2) { - errorf("Information element length exceeds message size\n"); - return -1; - } - switch(ie) { - case IAX_IE_CALLED_NUMBER: - ies->called_number = (char *)data + 2; - break; - case IAX_IE_CALLING_NUMBER: - ies->calling_number = (char *)data + 2; - break; - case IAX_IE_CALLING_ANI: - ies->calling_ani = (char *)data + 2; - break; - case IAX_IE_CALLING_NAME: - ies->calling_name = (char *)data + 2; - break; - case IAX_IE_CALLED_CONTEXT: - ies->called_context = (char *)data + 2; - break; - case IAX_IE_USERNAME: - ies->username = (char *)data + 2; - break; - case IAX_IE_PASSWORD: - ies->password = (char *)data + 2; - break; - case IAX_IE_CODEC_PREFS: - ies->codec_prefs = (char *)data + 2; - break; - case IAX_IE_CAPABILITY: - if (len != (int)sizeof(unsigned int)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting capability to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); - errorf(tmp); - } else - ies->capability = ntohl(get_unaligned_uint32(data + 2)); - break; - case IAX_IE_FORMAT: - if (len != (int)sizeof(unsigned int)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting format to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); - errorf(tmp); - } else - ies->format = ntohl(get_unaligned_uint32(data + 2)); - break; - case IAX_IE_LANGUAGE: - ies->language = (char *)data + 2; - break; - case IAX_IE_VERSION: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else - ies->version = ntohs(get_unaligned_uint16(data + 2)); - break; - case IAX_IE_ADSICPE: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting adsicpe to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else - ies->adsicpe = ntohs(get_unaligned_uint16(data + 2)); - break; - case IAX_IE_SAMPLINGRATE: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting samplingrate to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else - ies->samprate = ntohs(get_unaligned_uint16(data + 2)); - break; - case IAX_IE_DNID: - ies->dnid = (char *)data + 2; - break; - case IAX_IE_RDNIS: - ies->rdnis = (char *)data + 2; - break; - case IAX_IE_AUTHMETHODS: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting authmethods to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else - ies->authmethods = ntohs(get_unaligned_uint16(data + 2)); - break; - case IAX_IE_ENCRYPTION: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting encryption to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else - ies->encmethods = ntohs(get_unaligned_uint16(data + 2)); - break; - case IAX_IE_CHALLENGE: - ies->challenge = (char *)data + 2; - break; - case IAX_IE_MD5_RESULT: - ies->md5_result = (char *)data + 2; - break; - case IAX_IE_RSA_RESULT: - ies->rsa_result = (char *)data + 2; - break; - case IAX_IE_APPARENT_ADDR: - ies->apparent_addr = ((struct sockaddr_in *)(data + 2)); - break; - case IAX_IE_REFRESH: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting refresh to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else - ies->refresh = ntohs(get_unaligned_uint16(data + 2)); - break; - case IAX_IE_DPSTATUS: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting dpstatus to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else - ies->dpstatus = ntohs(get_unaligned_uint16(data + 2)); - break; - case IAX_IE_CALLNO: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting callno to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else - ies->callno = ntohs(get_unaligned_uint16(data + 2)); - break; - case IAX_IE_CAUSE: - ies->cause = (char *)data + 2; - break; - case IAX_IE_CAUSECODE: - if (len != 1) { - snprintf(tmp, (int)sizeof(tmp), "Expecting causecode to be single byte but was %d\n", len); - errorf(tmp); - } else { - ies->causecode = data[2]; - } - break; - case IAX_IE_IAX_UNKNOWN: - if (len == 1) - ies->iax_unknown = data[2]; - else { - snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len); - errorf(tmp); - } - break; - case IAX_IE_MSGCOUNT: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting msgcount to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else - ies->msgcount = ntohs(get_unaligned_uint16(data + 2)); - break; - case IAX_IE_AUTOANSWER: - ies->autoanswer = 1; - break; - case IAX_IE_MUSICONHOLD: - ies->musiconhold = 1; - break; - case IAX_IE_TRANSFERID: - if (len != (int)sizeof(unsigned int)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting transferid to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); - errorf(tmp); - } else - ies->transferid = ntohl(get_unaligned_uint32(data + 2)); - break; - case IAX_IE_DATETIME: - if (len != (int)sizeof(unsigned int)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting date/time to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); - errorf(tmp); - } else - ies->datetime = ntohl(get_unaligned_uint32(data + 2)); - break; - case IAX_IE_FIRMWAREVER: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting firmwarever to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else - ies->firmwarever = ntohs(get_unaligned_uint16(data + 2)); - break; - case IAX_IE_DEVICETYPE: - ies->devicetype = (char *)data + 2; - break; - case IAX_IE_SERVICEIDENT: - ies->serviceident = (char *)data + 2; - break; - case IAX_IE_FWBLOCKDESC: - if (len != (int)sizeof(unsigned int)) { - snprintf(tmp, (int)sizeof(tmp), "Expected block desc to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); - errorf(tmp); - } else - ies->fwdesc = ntohl(get_unaligned_uint32(data + 2)); - break; - case IAX_IE_FWBLOCKDATA: - ies->fwdata = data + 2; - ies->fwdatalen = len; - break; - case IAX_IE_ENCKEY: - ies->enckey = data + 2; - ies->enckeylen = len; - break; - case IAX_IE_PROVVER: - if (len != (int)sizeof(unsigned int)) { - snprintf(tmp, (int)sizeof(tmp), "Expected provisioning version to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); - errorf(tmp); - } else { - ies->provverpres = 1; - ies->provver = ntohl(get_unaligned_uint32(data + 2)); - } - break; - case IAX_IE_CALLINGPRES: - if (len == 1) - ies->calling_pres = data[2]; - else { - snprintf(tmp, (int)sizeof(tmp), "Expected single byte callingpres, but was %d long\n", len); - errorf(tmp); - } - break; - case IAX_IE_CALLINGTON: - if (len == 1) - ies->calling_ton = data[2]; - else { - snprintf(tmp, (int)sizeof(tmp), "Expected single byte callington, but was %d long\n", len); - errorf(tmp); - } - break; - case IAX_IE_CALLINGTNS: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expecting callingtns to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else - ies->calling_tns = ntohs(get_unaligned_uint16(data + 2)); - break; - case IAX_IE_RR_JITTER: - if (len != (int)sizeof(unsigned int)) { - snprintf(tmp, (int)sizeof(tmp), "Expected jitter rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); - errorf(tmp); - } else { - ies->rr_jitter = ntohl(get_unaligned_uint32(data + 2)); - } - break; - case IAX_IE_RR_LOSS: - if (len != (int)sizeof(unsigned int)) { - snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); - errorf(tmp); - } else { - ies->rr_loss = ntohl(get_unaligned_uint32(data + 2)); - } - break; - case IAX_IE_RR_PKTS: - if (len != (int)sizeof(unsigned int)) { - snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); - errorf(tmp); - } else { - ies->rr_pkts = ntohl(get_unaligned_uint32(data + 2)); - } - break; - case IAX_IE_RR_DELAY: - if (len != (int)sizeof(unsigned short)) { - snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); - errorf(tmp); - } else { - ies->rr_delay = ntohs(get_unaligned_uint16(data + 2)); - } - break; - case IAX_IE_RR_DROPPED: - if (len != (int)sizeof(unsigned int)) { - snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); - errorf(tmp); - } else { - ies->rr_dropped = ntohl(get_unaligned_uint32(data + 2)); - } - break; - case IAX_IE_RR_OOO: - if (len != (int)sizeof(unsigned int)) { - snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); - errorf(tmp); - } else { - ies->rr_ooo = ntohl(get_unaligned_uint32(data + 2)); - } - break; - default: - snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len); - outputf(tmp); - } - /* Overwrite information element with 0, to null terminate previous portion */ - data[0] = 0; - datalen -= (len + 2); - data += (len + 2); - } - /* Null-terminate last field */ - *data = '\0'; - if (datalen) { - errorf("Invalid information element contents, strange boundary\n"); - return -1; - } - return 0; -} - -void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f) -{ - fr->af.frametype = f->frametype; - fr->af.subclass = f->subclass; - fr->af.mallocd = 0; /* Our frame is static relative to the container */ - fr->af.datalen = f->datalen; - fr->af.samples = f->samples; - fr->af.offset = AST_FRIENDLY_OFFSET; - fr->af.src = f->src; - fr->af.delivery.tv_sec = 0; - fr->af.delivery.tv_usec = 0; - fr->af.data = fr->afdata; - fr->af.len = f->len; - if (fr->af.datalen) { - size_t copy_len = fr->af.datalen; - if (copy_len > fr->afdatalen) { - ast_log(LOG_ERROR, "Losing frame data because destination buffer size '%d' bytes not big enough for '%d' bytes in the frame\n", - (int) fr->afdatalen, (int) fr->af.datalen); - copy_len = fr->afdatalen; - } -#if __BYTE_ORDER == __LITTLE_ENDIAN - /* We need to byte-swap slinear samples from network byte order */ - if ((fr->af.frametype == AST_FRAME_VOICE) && (fr->af.subclass == AST_FORMAT_SLINEAR)) { - /* 2 bytes / sample for SLINEAR */ - ast_swapcopy_samples(fr->af.data, f->data, copy_len / 2); - } else -#endif - memcpy(fr->af.data, f->data, copy_len); - } -} - -struct iax_frame *iax_frame_new(int direction, int datalen, unsigned int cacheable) -{ - struct iax_frame *fr = NULL; - -#if !defined(LOW_MEMORY) - struct iax_frames *iax_frames; - - /* Attempt to get a frame from this thread's cache */ - if ((iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) { - AST_LIST_TRAVERSE_SAFE_BEGIN(&iax_frames->list, fr, list) { - if (fr->afdatalen >= datalen) { - size_t afdatalen = fr->afdatalen; - AST_LIST_REMOVE_CURRENT(&iax_frames->list, list); - iax_frames->size--; - memset(fr, 0, sizeof(*fr)); - fr->afdatalen = afdatalen; - break; - } - } - AST_LIST_TRAVERSE_SAFE_END - } - if (!fr) { - if (!(fr = ast_calloc_cache(1, sizeof(*fr) + datalen))) - return NULL; - fr->afdatalen = datalen; - } -#else - if (!(fr = ast_calloc(1, sizeof(*fr) + datalen))) - return NULL; - fr->afdatalen = datalen; -#endif - - - fr->direction = direction; - fr->retrans = -1; - fr->cacheable = cacheable; - - if (fr->direction == DIRECTION_INGRESS) - ast_atomic_fetchadd_int(&iframes, 1); - else - ast_atomic_fetchadd_int(&oframes, 1); - - ast_atomic_fetchadd_int(&frames, 1); - - return fr; -} - -void iax_frame_free(struct iax_frame *fr) -{ -#if !defined(LOW_MEMORY) - struct iax_frames *iax_frames; -#endif - - /* Note: does not remove from scheduler! */ - if (fr->direction == DIRECTION_INGRESS) - ast_atomic_fetchadd_int(&iframes, -1); - else if (fr->direction == DIRECTION_OUTGRESS) - ast_atomic_fetchadd_int(&oframes, -1); - else { - errorf("Attempt to double free frame detected\n"); - return; - } - ast_atomic_fetchadd_int(&frames, -1); - -#if !defined(LOW_MEMORY) - if (!fr->cacheable || !(iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) { - free(fr); - return; - } - - if (iax_frames->size < FRAME_CACHE_MAX_SIZE) { - fr->direction = 0; - AST_LIST_INSERT_HEAD(&iax_frames->list, fr, list); - iax_frames->size++; - return; - } -#endif - free(fr); -} - -#if !defined(LOW_MEMORY) -static void frame_cache_cleanup(void *data) -{ - struct iax_frames *frames = data; - struct iax_frame *cur; - - while ((cur = AST_LIST_REMOVE_HEAD(&frames->list, list))) - free(cur); - - free(frames); -} -#endif - -int iax_get_frames(void) { return frames; } -int iax_get_iframes(void) { return iframes; } -int iax_get_oframes(void) { return oframes; } diff --git a/1.4.23-rc4/channels/iax2-parser.h b/1.4.23-rc4/channels/iax2-parser.h deleted file mode 100644 index 0f3e18c00..000000000 --- a/1.4.23-rc4/channels/iax2-parser.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Asterisk -- A telephony toolkit for Linux. - * - * Implementation of Inter-Asterisk eXchange - * - * Copyright (C) 2003, Digium - * - * Mark Spencer <markster@digium.com> - * - * This program is free software, distributed under the terms of - * the GNU General Public License - */ - -/*!\file - * \brief Implementation of the IAX2 protocol - */ - -#ifndef _IAX2_PARSER_H -#define _IAX2_PARSER_H - -#include "asterisk/linkedlists.h" - -struct iax_ies { - char *called_number; - char *calling_number; - char *calling_ani; - char *calling_name; - int calling_ton; - int calling_tns; - int calling_pres; - char *called_context; - char *username; - char *password; - unsigned int capability; - unsigned int format; - char *codec_prefs; - char *language; - int version; - unsigned short adsicpe; - char *dnid; - char *rdnis; - unsigned int authmethods; - unsigned int encmethods; - char *challenge; - char *md5_result; - char *rsa_result; - struct sockaddr_in *apparent_addr; - unsigned short refresh; - unsigned short dpstatus; - unsigned short callno; - char *cause; - unsigned char causecode; - unsigned char iax_unknown; - int msgcount; - int autoanswer; - int musiconhold; - unsigned int transferid; - unsigned int datetime; - char *devicetype; - char *serviceident; - int firmwarever; - unsigned int fwdesc; - unsigned char *fwdata; - unsigned char fwdatalen; - unsigned char *enckey; - unsigned char enckeylen; - unsigned int provver; - unsigned short samprate; - int provverpres; - unsigned int rr_jitter; - unsigned int rr_loss; - unsigned int rr_pkts; - unsigned short rr_delay; - unsigned int rr_dropped; - unsigned int rr_ooo; -}; - -#define DIRECTION_INGRESS 1 -#define DIRECTION_OUTGRESS 2 - -struct iax_frame { -#ifdef LIBIAX - struct iax_session *session; - struct iax_event *event; -#else - int sockfd; -#endif - - /* /Our/ call number */ - unsigned short callno; - /* /Their/ call number */ - unsigned short dcallno; - /* Start of raw frame (outgoing only) */ - void *data; - /* Length of frame (outgoing only) */ - int datalen; - /* How many retries so far? */ - int retries; - /* Outgoing relative timestamp (ms) */ - unsigned int ts; - /* How long to wait before retrying */ - int retrytime; - /* Are we received out of order? */ - unsigned int outoforder:1; - /* Have we been sent at all yet? */ - unsigned int sentyet:1; - /* Non-zero if should be sent to transfer peer */ - unsigned int transfer:1; - /* Non-zero if this is the final message */ - unsigned int final:1; - /* Ingress or outgres */ - unsigned int direction:2; - /* Can this frame be cached? */ - unsigned int cacheable:1; - /* Outgoing Packet sequence number */ - int oseqno; - /* Next expected incoming packet sequence number */ - int iseqno; - /* Retransmission ID */ - int retrans; - /* Easy linking */ - AST_LIST_ENTRY(iax_frame) list; - /* Actual, isolated frame header */ - struct ast_frame af; - /*! Amount of space _allocated_ for data */ - size_t afdatalen; - unsigned char unused[AST_FRIENDLY_OFFSET]; - unsigned char afdata[0]; /* Data for frame */ -}; - -struct iax_ie_data { - unsigned char buf[1024]; - int pos; -}; - -/* Choose a different function for output */ -void iax_set_output(void (*output)(const char *data)); -/* Choose a different function for errors */ -void iax_set_error(void (*output)(const char *data)); -void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen); - -const char *iax_ie2str(int ie); - -int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, const void *data, int datalen); -int iax_ie_append_addr(struct iax_ie_data *ied, unsigned char ie, const struct sockaddr_in *sin); -int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value); -int iax_ie_append_short(struct iax_ie_data *ied, unsigned char ie, unsigned short value); -int iax_ie_append_str(struct iax_ie_data *ied, unsigned char ie, const char *str); -int iax_ie_append_byte(struct iax_ie_data *ied, unsigned char ie, unsigned char dat); -int iax_ie_append(struct iax_ie_data *ied, unsigned char ie); -int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen); - -int iax_get_frames(void); -int iax_get_iframes(void); -int iax_get_oframes(void); - -void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f); -struct iax_frame *iax_frame_new(int direction, int datalen, unsigned int cacheable); -void iax_frame_free(struct iax_frame *fr); -#endif diff --git a/1.4.23-rc4/channels/iax2-provision.c b/1.4.23-rc4/channels/iax2-provision.c deleted file mode 100644 index b6137a88b..000000000 --- a/1.4.23-rc4/channels/iax2-provision.c +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief IAX Provisioning Protocol - * - * \author Mark Spencer <markster@digium.com> - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <netdb.h> -#include <netinet/in.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <sys/socket.h> - -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/cli.h" -#include "asterisk/lock.h" -#include "asterisk/frame.h" -#include "asterisk/options.h" -#include "asterisk/md5.h" -#include "asterisk/astdb.h" -#include "asterisk/utils.h" -#include "asterisk/acl.h" -#include "iax2.h" -#include "iax2-provision.h" -#include "iax2-parser.h" - -#ifndef IPTOS_MINCOST -#define IPTOS_MINCOST 0x02 -#endif - -static int provinit = 0; - -struct iax_template { - int dead; - char name[80]; - char src[80]; - struct iax_template *next; - char user[20]; - char pass[20]; - char lang[10]; - unsigned short port; - unsigned int server; - unsigned short serverport; - unsigned int altserver; - unsigned int flags; - unsigned int format; - unsigned int tos; -} *templates; - -static struct iax_flag { - char *name; - int value; -} iax_flags[] = { - { "register", PROV_FLAG_REGISTER }, - { "secure", PROV_FLAG_SECURE }, - { "heartbeat", PROV_FLAG_HEARTBEAT }, - { "debug", PROV_FLAG_DEBUG }, - { "disablecid", PROV_FLAG_DIS_CALLERID }, - { "disablecw", PROV_FLAG_DIS_CALLWAIT }, - { "disablecidcw", PROV_FLAG_DIS_CIDCW }, - { "disable3way", PROV_FLAG_DIS_THREEWAY }, -}; - -char *iax_provflags2str(char *buf, int buflen, unsigned int flags) -{ - int x; - - if (!buf || buflen < 1) - return NULL; - - buf[0] = '\0'; - - for (x = 0; x < sizeof(iax_flags) / sizeof(iax_flags[0]); x++) { - if (flags & iax_flags[x].value){ - strncat(buf, iax_flags[x].name, buflen - strlen(buf) - 1); - strncat(buf, ",", buflen - strlen(buf) - 1); - } - } - - if (!ast_strlen_zero(buf)) - buf[strlen(buf) - 1] = '\0'; - else - strncpy(buf, "none", buflen - 1); - - return buf; -} - -static unsigned int iax_str2flags(const char *buf) -{ - int x; - int len; - int found; - unsigned int flags = 0; - char *e; - while(buf && *buf) { - e = strchr(buf, ','); - if (e) - len = e - buf; - else - len = 0; - found = 0; - for (x=0;x<sizeof(iax_flags) / sizeof(iax_flags[0]); x++) { - if ((len && !strncasecmp(iax_flags[x].name, buf, len)) || - (!len && !strcasecmp(iax_flags[x].name, buf))) { - flags |= iax_flags[x].value; - break; - } - } - if (e) { - buf = e + 1; - while(*buf && (*buf < 33)) - buf++; - } else - break; - } - return flags; -} -AST_MUTEX_DEFINE_STATIC(provlock); - -static struct iax_template *iax_template_find(const char *s, int allowdead) -{ - struct iax_template *cur; - cur = templates; - while(cur) { - if (!strcasecmp(s, cur->name)) { - if (!allowdead && cur->dead) - cur = NULL; - break; - } - cur = cur->next; - } - return cur; -} - -char *iax_prov_complete_template(const char *line, const char *word, int pos, int state) -{ - struct iax_template *c; - int which=0; - char *ret = NULL; - int wordlen = strlen(word); - - ast_mutex_lock(&provlock); - for (c = templates; c; c = c->next) { - if (!strncasecmp(word, c->name, wordlen) && ++which > state) { - ret = strdup(c->name); - break; - } - } - ast_mutex_unlock(&provlock); - - return ret; -} - -static unsigned int prov_ver_calc(struct iax_ie_data *provdata) -{ - struct MD5Context md5; - unsigned int tmp[4]; - MD5Init(&md5); - MD5Update(&md5, provdata->buf, provdata->pos); - MD5Final((unsigned char *)tmp, &md5); - return tmp[0] ^ tmp[1] ^ tmp[2] ^ tmp[3]; -} - -int iax_provision_build(struct iax_ie_data *provdata, unsigned int *signature, const char *template, int force) -{ - struct iax_template *cur; - unsigned int sig; - char tmp[40]; - memset(provdata, 0, sizeof(*provdata)); - ast_mutex_lock(&provlock); - cur = iax_template_find(template, 1); - /* If no match, try searching for '*' */ - if (!cur) - cur = iax_template_find("*", 1); - if (cur) { - /* found it -- add information elements as appropriate */ - if (force || strlen(cur->user)) - iax_ie_append_str(provdata, PROV_IE_USER, cur->user); - if (force || strlen(cur->pass)) - iax_ie_append_str(provdata, PROV_IE_PASS, cur->pass); - if (force || strlen(cur->lang)) - iax_ie_append_str(provdata, PROV_IE_LANG, cur->lang); - if (force || cur->port) - iax_ie_append_short(provdata, PROV_IE_PORTNO, cur->port); - if (force || cur->server) - iax_ie_append_int(provdata, PROV_IE_SERVERIP, cur->server); - if (force || cur->serverport) - iax_ie_append_short(provdata, PROV_IE_SERVERPORT, cur->serverport); - if (force || cur->altserver) - iax_ie_append_int(provdata, PROV_IE_ALTSERVER, cur->altserver); - if (force || cur->flags) - iax_ie_append_int(provdata, PROV_IE_FLAGS, cur->flags); - if (force || cur->format) - iax_ie_append_int(provdata, PROV_IE_FORMAT, cur->format); - if (force || cur->tos) - iax_ie_append_byte(provdata, PROV_IE_TOS, cur->tos); - - /* Calculate checksum of message so far */ - sig = prov_ver_calc(provdata); - if (signature) - *signature = sig; - /* Store signature */ - iax_ie_append_int(provdata, PROV_IE_PROVVER, sig); - /* Cache signature for later verification so we need not recalculate all this */ - snprintf(tmp, sizeof(tmp), "v0x%08x", sig); - ast_db_put("iax/provisioning/cache", template, tmp); - } else - ast_db_put("iax/provisioning/cache", template, "u"); - ast_mutex_unlock(&provlock); - return cur ? 0 : -1; -} - -int iax_provision_version(unsigned int *version, const char *template, int force) -{ - char tmp[80] = ""; - struct iax_ie_data ied; - int ret=0; - memset(&ied, 0, sizeof(ied)); - - ast_mutex_lock(&provlock); - ast_db_get("iax/provisioning/cache", template, tmp, sizeof(tmp)); - if (sscanf(tmp, "v%x", version) != 1) { - if (strcmp(tmp, "u")) { - ret = iax_provision_build(&ied, version, template, force); - if (ret) - ast_log(LOG_DEBUG, "Unable to create provisioning packet for '%s'\n", template); - } else - ret = -1; - } else if (option_debug) - ast_log(LOG_DEBUG, "Retrieved cached version '%s' = '%08x'\n", tmp, *version); - ast_mutex_unlock(&provlock); - return ret; -} - -static int iax_template_parse(struct iax_template *cur, struct ast_config *cfg, const char *s, const char *def) -{ - struct ast_variable *v; - int foundportno = 0; - int foundserverportno = 0; - int x; - struct in_addr ia; - struct hostent *hp; - struct ast_hostent h; - struct iax_template *src, tmp; - const char *t; - if (def) { - t = ast_variable_retrieve(cfg, s ,"template"); - src = NULL; - if (t && strlen(t)) { - src = iax_template_find(t, 0); - if (!src) - ast_log(LOG_WARNING, "Unable to find base template '%s' for creating '%s'. Trying '%s'\n", t, s, def); - else - def = t; - } - if (!src) { - src = iax_template_find(def, 0); - if (!src) - ast_log(LOG_WARNING, "Unable to locate default base template '%s' for creating '%s', omitting.\n", def, s); - } - if (!src) - return -1; - ast_mutex_lock(&provlock); - /* Backup old data */ - memcpy(&tmp, cur, sizeof(tmp)); - /* Restore from src */ - memcpy(cur, src, sizeof(tmp)); - /* Restore important headers */ - memcpy(cur->name, tmp.name, sizeof(cur->name)); - cur->dead = tmp.dead; - cur->next = tmp.next; - ast_mutex_unlock(&provlock); - } - if (def) - strncpy(cur->src, def, sizeof(cur->src) - 1); - else - cur->src[0] = '\0'; - v = ast_variable_browse(cfg, s); - while(v) { - if (!strcasecmp(v->name, "port") || !strcasecmp(v->name, "serverport")) { - if ((sscanf(v->value, "%d", &x) == 1) && (x > 0) && (x < 65535)) { - if (!strcasecmp(v->name, "port")) { - cur->port = x; - foundportno = 1; - } else { - cur->serverport = x; - foundserverportno = 1; - } - } else - ast_log(LOG_WARNING, "Ignoring invalid %s '%s' for '%s' at line %d\n", v->name, v->value, s, v->lineno); - } else if (!strcasecmp(v->name, "server") || !strcasecmp(v->name, "altserver")) { - hp = ast_gethostbyname(v->value, &h); - if (hp) { - memcpy(&ia, hp->h_addr, sizeof(ia)); - if (!strcasecmp(v->name, "server")) - cur->server = ntohl(ia.s_addr); - else - cur->altserver = ntohl(ia.s_addr); - } else - ast_log(LOG_WARNING, "Ignoring invalid %s '%s' for '%s' at line %d\n", v->name, v->value, s, v->lineno); - } else if (!strcasecmp(v->name, "codec")) { - if ((x = ast_getformatbyname(v->value)) > 0) { - cur->format = x; - } else - ast_log(LOG_WARNING, "Ignoring invalid codec '%s' for '%s' at line %d\n", v->value, s, v->lineno); - } else if (!strcasecmp(v->name, "tos")) { - if (ast_str2tos(v->value, &cur->tos)) - ast_log(LOG_WARNING, "Invalid tos value at line %d, see doc/ip-tos.txt for more information.\n", v->lineno); - } else if (!strcasecmp(v->name, "user")) { - strncpy(cur->user, v->value, sizeof(cur->user) - 1); - if (strcmp(cur->user, v->value)) - ast_log(LOG_WARNING, "Truncating username from '%s' to '%s' for '%s' at line %d\n", v->value, cur->user, s, v->lineno); - } else if (!strcasecmp(v->name, "pass")) { - strncpy(cur->pass, v->value, sizeof(cur->pass) - 1); - if (strcmp(cur->pass, v->value)) - ast_log(LOG_WARNING, "Truncating password from '%s' to '%s' for '%s' at line %d\n", v->value, cur->pass, s, v->lineno); - } else if (!strcasecmp(v->name, "language")) { - strncpy(cur->lang, v->value, sizeof(cur->lang) - 1); - if (strcmp(cur->lang, v->value)) - ast_log(LOG_WARNING, "Truncating language from '%s' to '%s' for '%s' at line %d\n", v->value, cur->lang, s, v->lineno); - } else if (!strcasecmp(v->name, "flags")) { - cur->flags = iax_str2flags(v->value); - } else if (!strncasecmp(v->name, "flags", 5) && strchr(v->name, '+')) { - cur->flags |= iax_str2flags(v->value); - } else if (!strncasecmp(v->name, "flags", 5) && strchr(v->name, '-')) { - cur->flags &= ~iax_str2flags(v->value); - } else if (strcasecmp(v->name, "template")) { - ast_log(LOG_WARNING, "Unknown keyword '%s' in definition of '%s' at line %d\n", v->name, s, v->lineno); - } - v = v->next; - } - if (!foundportno) - cur->port = IAX_DEFAULT_PORTNO; - if (!foundserverportno) - cur->serverport = IAX_DEFAULT_PORTNO; - return 0; -} - -static int iax_process_template(struct ast_config *cfg, char *s, char *def) -{ - /* Find an already existing one if there */ - struct iax_template *cur; - int mallocd = 0; - cur = templates; - while(cur) { - if (!strcasecmp(cur->name, s)) - break; - cur = cur->next; - } - if (!cur) { - mallocd = 1; - cur = malloc(sizeof(struct iax_template)); - if (!cur) { - ast_log(LOG_WARNING, "Out of memory!\n"); - return -1; - } - /* Initialize entry */ - memset(cur, 0, sizeof(*cur)); - strncpy(cur->name, s, sizeof(cur->name) - 1); - cur->dead = 1; - } - if (!iax_template_parse(cur, cfg, s, def)) - cur->dead = 0; - - /* Link if we're mallocd */ - if (mallocd) { - ast_mutex_lock(&provlock); - cur->next = templates; - templates = cur; - ast_mutex_unlock(&provlock); - } - return 0; -} - -static char show_provisioning_usage[] = -"Usage: iax list provisioning [template]\n" -" Lists all known IAX provisioning templates or a\n" -" specific one if specified.\n"; - -static const char *ifthere(const char *s) -{ - if (strlen(s)) - return s; - else - return "<unspecified>"; -} - -static const char *iax_server(unsigned int addr) -{ - struct in_addr ia; - - if (!addr) - return "<unspecified>"; - - ia.s_addr = htonl(addr); - - return ast_inet_ntoa(ia); -} - - -static int iax_show_provisioning(int fd, int argc, char *argv[]) -{ - struct iax_template *cur; - char server[INET_ADDRSTRLEN]; - char alternate[INET_ADDRSTRLEN]; - char flags[80]; /* Has to be big enough for 'flags' too */ - int found = 0; - if ((argc != 3) && (argc != 4)) - return RESULT_SHOWUSAGE; - ast_mutex_lock(&provlock); - for (cur = templates;cur;cur = cur->next) { - if ((argc == 3) || (!strcasecmp(argv[3], cur->name))) { - if (found) - ast_cli(fd, "\n"); - ast_copy_string(server, iax_server(cur->server), sizeof(server)); - ast_copy_string(alternate, iax_server(cur->altserver), sizeof(alternate)); - ast_cli(fd, "== %s ==\n", cur->name); - ast_cli(fd, "Base Templ: %s\n", strlen(cur->src) ? cur->src : "<none>"); - ast_cli(fd, "Username: %s\n", ifthere(cur->user)); - ast_cli(fd, "Secret: %s\n", ifthere(cur->pass)); - ast_cli(fd, "Language: %s\n", ifthere(cur->lang)); - ast_cli(fd, "Bind Port: %d\n", cur->port); - ast_cli(fd, "Server: %s\n", server); - ast_cli(fd, "Server Port: %d\n", cur->serverport); - ast_cli(fd, "Alternate: %s\n", alternate); - ast_cli(fd, "Flags: %s\n", iax_provflags2str(flags, sizeof(flags), cur->flags)); - ast_cli(fd, "Format: %s\n", ast_getformatname(cur->format)); - ast_cli(fd, "TOS: 0x%x\n", cur->tos); - found++; - } - } - ast_mutex_unlock(&provlock); - if (!found) { - if (argc == 3) - ast_cli(fd, "No provisioning templates found\n"); - else - ast_cli(fd, "No provisioning template matching '%s' found\n", argv[3]); - } - return RESULT_SUCCESS; -} - -static struct ast_cli_entry cli_iax2_provision[] = { - { { "iax2", "show", "provisioning", NULL }, - iax_show_provisioning, "Display iax provisioning", - show_provisioning_usage, iax_prov_complete_template, }, -}; - -static int iax_provision_init(void) -{ - ast_cli_register_multiple(cli_iax2_provision, sizeof(cli_iax2_provision) / sizeof(struct ast_cli_entry)); - provinit = 1; - return 0; -} - -int iax_provision_unload(void) -{ - provinit = 0; - ast_cli_unregister_multiple(cli_iax2_provision, sizeof(cli_iax2_provision) / sizeof(struct ast_cli_entry)); - return 0; -} - -int iax_provision_reload(void) -{ - struct ast_config *cfg; - struct iax_template *cur, *prev, *next; - char *cat; - int found = 0; - if (!provinit) - iax_provision_init(); - /* Mark all as dead. No need for locking */ - cur = templates; - while(cur) { - cur->dead = 1; - cur = cur->next; - } - cfg = ast_config_load("iaxprov.conf"); - if (cfg) { - /* Load as appropriate */ - cat = ast_category_browse(cfg, NULL); - while(cat) { - if (strcasecmp(cat, "general")) { - iax_process_template(cfg, cat, found ? "default" : NULL); - found++; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Loaded provisioning template '%s'\n", cat); - } - cat = ast_category_browse(cfg, cat); - } - ast_config_destroy(cfg); - } else - ast_log(LOG_NOTICE, "No IAX provisioning configuration found, IAX provisioning disabled.\n"); - ast_mutex_lock(&provlock); - /* Drop dead entries while locked */ - prev = NULL; - cur = templates; - while(cur) { - next = cur->next; - if (cur->dead) { - if (prev) - prev->next = next; - else - templates = next; - free(cur); - } else - prev = cur; - cur = next; - } - ast_mutex_unlock(&provlock); - /* Purge cached signature DB entries */ - ast_db_deltree("iax/provisioning/cache", NULL); - return 0; - -} diff --git a/1.4.23-rc4/channels/iax2-provision.h b/1.4.23-rc4/channels/iax2-provision.h deleted file mode 100644 index d95150253..000000000 --- a/1.4.23-rc4/channels/iax2-provision.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * IAX Provisioning Protocol - * - * Sub-information elements - * - * Copyright (C) 2003, Digium - * - * Mark Spencer <markster@digium.com> - * - */ - -/*! \file - * \brief IAX2 Provisioning protocol - */ - -#include "iax2-parser.h" - -#define PROV_IE_USEDHCP 1 /* Presense only */ -#define PROV_IE_IPADDR 2 /* 32-bit */ -#define PROV_IE_SUBNET 3 /* 32-bit */ -#define PROV_IE_GATEWAY 4 /* 32-bit */ -#define PROV_IE_PORTNO 5 /* 16-bit */ -#define PROV_IE_USER 6 /* < 20 bytes */ -#define PROV_IE_PASS 7 /* < 20 bytes */ -#define PROV_IE_SERVERUSER 8 /* < 20 bytes */ -#define PROV_IE_SERVERPASS 9 /* < 20 bytes */ -#define PROV_IE_LANG 10 /* < 10 bytes */ -#define PROV_IE_TOS 11 /* 8-bits */ -#define PROV_IE_FLAGS 12 /* 32-bits */ -#define PROV_IE_FORMAT 13 /* 32-bits */ -#define PROV_IE_AESKEY 14 /* 128-bits */ -#define PROV_IE_SERVERIP 15 /* 32-bits */ -#define PROV_IE_SERVERPORT 16 /* 16-bits */ -#define PROV_IE_NEWAESKEY 17 /* 128-bits */ -#define PROV_IE_PROVVER 18 /* 32-bits */ -#define PROV_IE_ALTSERVER 19 /* 32-bits */ - -#define PROV_FLAG_REGISTER (1 << 0) -#define PROV_FLAG_SECURE (1 << 1) -#define PROV_FLAG_HEARTBEAT (1 << 2) -#define PROV_FLAG_DEBUG (1 << 3) - -#define PROV_FLAG_DIS_CALLERID (1 << 4) /* Caller-ID Disabled */ -#define PROV_FLAG_DIS_CALLWAIT (1 << 5) /* Caller-ID / Call Waiting Disable */ -#define PROV_FLAG_DIS_CIDCW (1 << 6) /* CID/CW Disabled */ -#define PROV_FLAG_DIS_THREEWAY (1 << 7) /* Three-way calling, transfer disabled */ - -char *iax_provflags2str(char *buf, int buflen, unsigned int flags); -int iax_provision_reload(void); -int iax_provision_unload(void); -int iax_provision_build(struct iax_ie_data *provdata, unsigned int *signature, const char *template, int force); -int iax_provision_version(unsigned int *signature, const char *template, int force); -char *iax_prov_complete_template(const char *line, const char *word, int pos, int state); diff --git a/1.4.23-rc4/channels/iax2.h b/1.4.23-rc4/channels/iax2.h deleted file mode 100644 index 960dec8bb..000000000 --- a/1.4.23-rc4/channels/iax2.h +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Asterisk -- A telephony toolkit for Linux. - * - * Implementation of Inter-Asterisk eXchange - * - * Copyright (C) 2003, Digium - * - * Mark Spencer <markster@linux-support.net> - * - * This program is free software, distributed under the terms of - * the GNU General Public License - */ - -#ifndef _IAX2_H -#define _IAX2_H - -/* Max version of IAX protocol we support */ -#define IAX_PROTO_VERSION 2 - -/* NOTE: IT IS CRITICAL THAT IAX_MAX_CALLS BE A POWER OF 2. */ -#if defined(LOW_MEMORY) -#define IAX_MAX_CALLS 2048 -#else -#define IAX_MAX_CALLS 32768 -#endif - -#define IAX_FLAG_FULL 0x8000 - -#define IAX_FLAG_RETRANS 0x8000 - -#define IAX_FLAG_SC_LOG 0x80 - -#define IAX_MAX_SHIFT 0x1F - -#define IAX_WINDOW 64 - -/* Subclass for AST_FRAME_IAX */ -#define IAX_COMMAND_NEW 1 -#define IAX_COMMAND_PING 2 -#define IAX_COMMAND_PONG 3 -#define IAX_COMMAND_ACK 4 -#define IAX_COMMAND_HANGUP 5 -#define IAX_COMMAND_REJECT 6 -#define IAX_COMMAND_ACCEPT 7 -#define IAX_COMMAND_AUTHREQ 8 -#define IAX_COMMAND_AUTHREP 9 -#define IAX_COMMAND_INVAL 10 -#define IAX_COMMAND_LAGRQ 11 -#define IAX_COMMAND_LAGRP 12 -#define IAX_COMMAND_REGREQ 13 /* Registration request */ -#define IAX_COMMAND_REGAUTH 14 /* Registration authentication required */ -#define IAX_COMMAND_REGACK 15 /* Registration accepted */ -#define IAX_COMMAND_REGREJ 16 /* Registration rejected */ -#define IAX_COMMAND_REGREL 17 /* Force release of registration */ -#define IAX_COMMAND_VNAK 18 /* If we receive voice before valid first voice frame, send this */ -#define IAX_COMMAND_DPREQ 19 /* Request status of a dialplan entry */ -#define IAX_COMMAND_DPREP 20 /* Request status of a dialplan entry */ -#define IAX_COMMAND_DIAL 21 /* Request a dial on channel brought up TBD */ -#define IAX_COMMAND_TXREQ 22 /* Transfer Request */ -#define IAX_COMMAND_TXCNT 23 /* Transfer Connect */ -#define IAX_COMMAND_TXACC 24 /* Transfer Accepted */ -#define IAX_COMMAND_TXREADY 25 /* Transfer ready */ -#define IAX_COMMAND_TXREL 26 /* Transfer release */ -#define IAX_COMMAND_TXREJ 27 /* Transfer reject */ -#define IAX_COMMAND_QUELCH 28 /* Stop audio/video transmission */ -#define IAX_COMMAND_UNQUELCH 29 /* Resume audio/video transmission */ -#define IAX_COMMAND_POKE 30 /* Like ping, but does not require an open connection */ -#define IAX_COMMAND_PAGE 31 /* Paging description */ -#define IAX_COMMAND_MWI 32 /* Stand-alone message waiting indicator */ -#define IAX_COMMAND_UNSUPPORT 33 /* Unsupported message received */ -#define IAX_COMMAND_TRANSFER 34 /* Request remote transfer */ -#define IAX_COMMAND_PROVISION 35 /* Provision device */ -#define IAX_COMMAND_FWDOWNL 36 /* Download firmware */ -#define IAX_COMMAND_FWDATA 37 /* Firmware Data */ -#define IAX_COMMAND_TXMEDIA 38 /* Transfer media only */ - -#define IAX_DEFAULT_REG_EXPIRE 60 /* By default require re-registration once per minute */ - -#define IAX_LINGER_TIMEOUT 10 /* How long to wait before closing bridged call */ - -#define IAX_DEFAULT_PORTNO 4569 - -/* IAX Information elements */ -#define IAX_IE_CALLED_NUMBER 1 /* Number/extension being called - string */ -#define IAX_IE_CALLING_NUMBER 2 /* Calling number - string */ -#define IAX_IE_CALLING_ANI 3 /* Calling number ANI for billing - string */ -#define IAX_IE_CALLING_NAME 4 /* Name of caller - string */ -#define IAX_IE_CALLED_CONTEXT 5 /* Context for number - string */ -#define IAX_IE_USERNAME 6 /* Username (peer or user) for authentication - string */ -#define IAX_IE_PASSWORD 7 /* Password for authentication - string */ -#define IAX_IE_CAPABILITY 8 /* Actual codec capability - unsigned int */ -#define IAX_IE_FORMAT 9 /* Desired codec format - unsigned int */ -#define IAX_IE_LANGUAGE 10 /* Desired language - string */ -#define IAX_IE_VERSION 11 /* Protocol version - short */ -#define IAX_IE_ADSICPE 12 /* CPE ADSI capability - short */ -#define IAX_IE_DNID 13 /* Originally dialed DNID - string */ -#define IAX_IE_AUTHMETHODS 14 /* Authentication method(s) - short */ -#define IAX_IE_CHALLENGE 15 /* Challenge data for MD5/RSA - string */ -#define IAX_IE_MD5_RESULT 16 /* MD5 challenge result - string */ -#define IAX_IE_RSA_RESULT 17 /* RSA challenge result - string */ -#define IAX_IE_APPARENT_ADDR 18 /* Apparent address of peer - struct sockaddr_in */ -#define IAX_IE_REFRESH 19 /* When to refresh registration - short */ -#define IAX_IE_DPSTATUS 20 /* Dialplan status - short */ -#define IAX_IE_CALLNO 21 /* Call number of peer - short */ -#define IAX_IE_CAUSE 22 /* Cause - string */ -#define IAX_IE_IAX_UNKNOWN 23 /* Unknown IAX command - byte */ -#define IAX_IE_MSGCOUNT 24 /* How many messages waiting - short */ -#define IAX_IE_AUTOANSWER 25 /* Request auto-answering -- none */ -#define IAX_IE_MUSICONHOLD 26 /* Request musiconhold with QUELCH -- none or string */ -#define IAX_IE_TRANSFERID 27 /* Transfer Request Identifier -- int */ -#define IAX_IE_RDNIS 28 /* Referring DNIS -- string */ -#define IAX_IE_PROVISIONING 29 /* Provisioning info */ -#define IAX_IE_AESPROVISIONING 30 /* AES Provisioning info */ -#define IAX_IE_DATETIME 31 /* Date/Time */ -#define IAX_IE_DEVICETYPE 32 /* Device Type -- string */ -#define IAX_IE_SERVICEIDENT 33 /* Service Identifier -- string */ -#define IAX_IE_FIRMWAREVER 34 /* Firmware revision -- u16 */ -#define IAX_IE_FWBLOCKDESC 35 /* Firmware block description -- u32 */ -#define IAX_IE_FWBLOCKDATA 36 /* Firmware block of data -- raw */ -#define IAX_IE_PROVVER 37 /* Provisioning Version (u32) */ -#define IAX_IE_CALLINGPRES 38 /* Calling presentation (u8) */ -#define IAX_IE_CALLINGTON 39 /* Calling type of number (u8) */ -#define IAX_IE_CALLINGTNS 40 /* Calling transit network select (u16) */ -#define IAX_IE_SAMPLINGRATE 41 /* Supported sampling rates (u16) */ -#define IAX_IE_CAUSECODE 42 /* Hangup cause (u8) */ -#define IAX_IE_ENCRYPTION 43 /* Encryption format (u16) */ -#define IAX_IE_ENCKEY 44 /* Encryption key (raw) */ -#define IAX_IE_CODEC_PREFS 45 /* Codec Negotiation */ - -#define IAX_IE_RR_JITTER 46 /* Received jitter (as in RFC1889) u32 */ -#define IAX_IE_RR_LOSS 47 /* Received loss (high byte loss pct, low 24 bits loss count, as in rfc1889 */ -#define IAX_IE_RR_PKTS 48 /* Received frames (total frames received) u32 */ -#define IAX_IE_RR_DELAY 49 /* Max playout delay for received frames (in ms) u16 */ -#define IAX_IE_RR_DROPPED 50 /* Dropped frames (presumably by jitterbuf) u32 */ -#define IAX_IE_RR_OOO 51 /* Frames received Out of Order u32 */ - - -#define IAX_AUTH_PLAINTEXT (1 << 0) -#define IAX_AUTH_MD5 (1 << 1) -#define IAX_AUTH_RSA (1 << 2) - -#define IAX_ENCRYPT_AES128 (1 << 0) - -#define IAX_META_TRUNK 1 /* Trunk meta-message */ -#define IAX_META_VIDEO 2 /* Video frame */ - -#define IAX_META_TRUNK_SUPERMINI 0 /* This trunk frame contains classic supermini frames */ -#define IAX_META_TRUNK_MINI 1 /* This trunk frame contains trunked mini frames */ - -#define IAX_RATE_8KHZ (1 << 0) /* 8khz sampling (default if absent) */ -#define IAX_RATE_11KHZ (1 << 1) /* 11.025khz sampling */ -#define IAX_RATE_16KHZ (1 << 2) /* 16khz sampling */ -#define IAX_RATE_22KHZ (1 << 3) /* 22.05khz sampling */ -#define IAX_RATE_44KHZ (1 << 4) /* 44.1khz sampling */ -#define IAX_RATE_48KHZ (1 << 5) /* 48khz sampling */ - -#define IAX_DPSTATUS_EXISTS (1 << 0) -#define IAX_DPSTATUS_CANEXIST (1 << 1) -#define IAX_DPSTATUS_NONEXISTENT (1 << 2) -#define IAX_DPSTATUS_IGNOREPAT (1 << 14) -#define IAX_DPSTATUS_MATCHMORE (1 << 15) - -/* Full frames are always delivered reliably */ -struct ast_iax2_full_hdr { - unsigned short scallno; /* Source call number -- high bit must be 1 */ - unsigned short dcallno; /* Destination call number -- high bit is 1 if retransmission */ - unsigned int ts; /* 32-bit timestamp in milliseconds (from 1st transmission) */ - unsigned char oseqno; /* Packet number (outgoing) */ - unsigned char iseqno; /* Packet number (next incoming expected) */ - unsigned char type; /* Frame type */ - unsigned char csub; /* Compressed subclass */ - unsigned char iedata[0]; -} __attribute__ ((__packed__)); - -/* Full frames are always delivered reliably */ -struct ast_iax2_full_enc_hdr { - unsigned short scallno; /* Source call number -- high bit must be 1 */ - unsigned short dcallno; /* Destination call number -- high bit is 1 if retransmission */ - unsigned char encdata[0]; -} __attribute__ ((__packed__)); - -/* Mini header is used only for voice frames -- delivered unreliably */ -struct ast_iax2_mini_hdr { - unsigned short callno; /* Source call number -- high bit must be 0, rest must be non-zero */ - unsigned short ts; /* 16-bit Timestamp (high 16 bits from last ast_iax2_full_hdr) */ - /* Frametype implicitly VOICE_FRAME */ - /* subclass implicit from last ast_iax2_full_hdr */ - unsigned char data[0]; -} __attribute__ ((__packed__)); - -/* Mini header is used only for voice frames -- delivered unreliably */ -struct ast_iax2_mini_enc_hdr { - unsigned short callno; /* Source call number -- high bit must be 0, rest must be non-zero */ - unsigned char encdata[0]; -} __attribute__ ((__packed__)); - -struct ast_iax2_meta_hdr { - unsigned short zeros; /* Zeros field -- must be zero */ - unsigned char metacmd; /* Meta command */ - unsigned char cmddata; /* Command Data */ - unsigned char data[0]; -} __attribute__ ((__packed__)); - -struct ast_iax2_video_hdr { - unsigned short zeros; /* Zeros field -- must be zero */ - unsigned short callno; /* Video call number */ - unsigned short ts; /* Timestamp and mark if present */ - unsigned char data[0]; -} __attribute__ ((__packed__)); - -struct ast_iax2_meta_trunk_hdr { - unsigned int ts; /* 32-bit timestamp for all messages */ - unsigned char data[0]; -} __attribute__ ((__packed__)); - -struct ast_iax2_meta_trunk_entry { - unsigned short callno; /* Call number */ - unsigned short len; /* Length of data for this callno */ -} __attribute__ ((__packed__)); - -/* When trunktimestamps are used, we use this format instead */ -struct ast_iax2_meta_trunk_mini { - unsigned short len; - struct ast_iax2_mini_hdr mini; /* this is an actual miniframe */ -} __attribute__ ((__packed__)); - -#define IAX_FIRMWARE_MAGIC 0x69617879 - -struct ast_iax2_firmware_header { - unsigned int magic; /* Magic number */ - unsigned short version; /* Software version */ - unsigned char devname[16]; /* Device */ - unsigned int datalen; /* Data length of file beyond header */ - unsigned char chksum[16]; /* Checksum of all data */ - unsigned char data[0]; -} __attribute__ ((__packed__)); -#endif diff --git a/1.4.23-rc4/channels/misdn/Makefile b/1.4.23-rc4/channels/misdn/Makefile deleted file mode 100644 index e277636e6..000000000 --- a/1.4.23-rc4/channels/misdn/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# -# Makefile for chan_misdn support -# -ifneq ($(wildcard /usr/include/linux/mISDNdsp.h),) -CFLAGS+=-DMISDN_1_2 -endif - -all: - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -portinfo: portinfo.o - $(CC) -o $@ $^ -lisdnnet -lmISDN -lpthread - -clean: - rm -rf *.a *.o *.so portinfo *.i diff --git a/1.4.23-rc4/channels/misdn/chan_misdn_config.h b/1.4.23-rc4/channels/misdn/chan_misdn_config.h deleted file mode 100644 index f675704c0..000000000 --- a/1.4.23-rc4/channels/misdn/chan_misdn_config.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Chan_Misdn -- Channel Driver for Asterisk - * - * Interface to mISDN - * - * Copyright (C) 2004, Christian Richter - * - * Christian Richter <crich@beronet.com> - * - * This program is free software, distributed under the terms of - * the GNU General Public License - */ - - - -#ifndef CHAN_MISDN_CONFIG_H -#define CHAN_MISDN_CONFIG_H - -#define BUFFERSIZE 512 - -enum misdn_cfg_elements { - - /* port config items */ - MISDN_CFG_FIRST = 0, - MISDN_CFG_GROUPNAME, /* char[] */ - MISDN_CFG_ALLOWED_BEARERS, /* char[] */ - MISDN_CFG_FAR_ALERTING, /* int (bool) */ - MISDN_CFG_RXGAIN, /* int */ - MISDN_CFG_TXGAIN, /* int */ - MISDN_CFG_TE_CHOOSE_CHANNEL, /* int (bool) */ - MISDN_CFG_PMP_L1_CHECK, /* int (bool) */ - MISDN_CFG_REJECT_CAUSE, /* int */ - MISDN_CFG_ALARM_BLOCK, /* int (bool) */ - MISDN_CFG_HDLC, /* int (bool) */ - MISDN_CFG_CONTEXT, /* char[] */ - MISDN_CFG_LANGUAGE, /* char[] */ - MISDN_CFG_MUSICCLASS, /* char[] */ - MISDN_CFG_CALLERID, /* char[] */ - MISDN_CFG_METHOD, /* char[] */ - MISDN_CFG_DIALPLAN, /* int */ - MISDN_CFG_LOCALDIALPLAN, /* int */ - MISDN_CFG_CPNDIALPLAN, /* int */ - MISDN_CFG_NATPREFIX, /* char[] */ - MISDN_CFG_INTERNATPREFIX, /* char[] */ - MISDN_CFG_PRES, /* int */ - MISDN_CFG_SCREEN, /* int */ - MISDN_CFG_ALWAYS_IMMEDIATE, /* int (bool) */ - MISDN_CFG_NODIALTONE, /* int (bool) */ - MISDN_CFG_IMMEDIATE, /* int (bool) */ - MISDN_CFG_SENDDTMF, /* int (bool) */ - MISDN_CFG_ASTDTMF, /* int (bool) */ - MISDN_CFG_HOLD_ALLOWED, /* int (bool) */ - MISDN_CFG_EARLY_BCONNECT, /* int (bool) */ - MISDN_CFG_INCOMING_EARLY_AUDIO, /* int (bool) */ - MISDN_CFG_ECHOCANCEL, /* int */ -#ifdef MISDN_1_2 - MISDN_CFG_PIPELINE, /* char[] */ -#endif - -#ifdef WITH_BEROEC - MISDN_CFG_BNECHOCANCEL, - MISDN_CFG_BNEC_ANTIHOWL, - MISDN_CFG_BNEC_NLP, - MISDN_CFG_BNEC_ZEROCOEFF, - MISDN_CFG_BNEC_TD, - MISDN_CFG_BNEC_ADAPT, -#endif - MISDN_CFG_NEED_MORE_INFOS, /* bool */ - MISDN_CFG_NOAUTORESPOND_ON_SETUP, /* bool */ - MISDN_CFG_NTTIMEOUT, /* bool */ - MISDN_CFG_BRIDGING, /* bool */ - MISDN_CFG_JITTERBUFFER, /* int */ - MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD, /* int */ - MISDN_CFG_CALLGROUP, /* ast_group_t */ - MISDN_CFG_PICKUPGROUP, /* ast_group_t */ - MISDN_CFG_MAX_IN, /* int */ - MISDN_CFG_MAX_OUT, /* int */ - MISDN_CFG_L1_TIMEOUT, /* int */ - MISDN_CFG_OVERLAP_DIAL, /* int (bool)*/ - MISDN_CFG_MSNS, /* char[] */ - MISDN_CFG_FAXDETECT, /* char[] */ - MISDN_CFG_FAXDETECT_CONTEXT, /* char[] */ - MISDN_CFG_FAXDETECT_TIMEOUT, /* int */ - MISDN_CFG_PTP, /* int (bool) */ - MISDN_CFG_LAST, - - /* general config items */ - MISDN_GEN_FIRST, -#ifndef MISDN_1_2 - MISDN_GEN_MISDN_INIT, /* char[] */ -#endif - MISDN_GEN_DEBUG, /* int */ - MISDN_GEN_TRACEFILE, /* char[] */ - MISDN_GEN_BRIDGING, /* int (bool) */ - MISDN_GEN_STOP_TONE, /* int (bool) */ - MISDN_GEN_APPEND_DIGITS2EXTEN, /* int (bool) */ - MISDN_GEN_DYNAMIC_CRYPT, /* int (bool) */ - MISDN_GEN_CRYPT_PREFIX, /* char[] */ - MISDN_GEN_CRYPT_KEYS, /* char[] */ - MISDN_GEN_NTKEEPCALLS, /* int (bool) */ - MISDN_GEN_NTDEBUGFLAGS, /* int */ - MISDN_GEN_NTDEBUGFILE, /* char[] */ - MISDN_GEN_LAST -}; - -enum misdn_cfg_method { - METHOD_STANDARD = 0, - METHOD_ROUND_ROBIN, - METHOD_STANDARD_DEC -}; - -/* you must call misdn_cfg_init before any other function of this header file */ -int misdn_cfg_init(int max_ports); -void misdn_cfg_reload(void); -void misdn_cfg_destroy(void); - -void misdn_cfg_update_ptp( void ); - -/* if you requst a general config element, the port value is ignored. if the requested - * value is not available, or the buffer is too small, the buffer will be nulled (in - * case of a char* only its first byte will be nulled). */ -void misdn_cfg_get(int port, enum misdn_cfg_elements elem, void* buf, int bufsize); - -/* returns the enum element for the given name, returns MISDN_CFG_FIRST if none was found */ -enum misdn_cfg_elements misdn_cfg_get_elem (char *name); - -/* fills the buffer with the name of the given config element */ -void misdn_cfg_get_name (enum misdn_cfg_elements elem, void *buf, int bufsize); - -/* fills the buffer with the description of the given config element */ -void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default); - -/* fills the buffer with a ',' separated list of all active ports */ -void misdn_cfg_get_ports_string(char *ports); - -/* fills the buffer with a nice printable string representation of the config element */ -void misdn_cfg_get_config_string(int port, enum misdn_cfg_elements elem, char* buf, int bufsize); - -/* returns the next available port number. returns -1 if the last one was reached. */ -int misdn_cfg_get_next_port(int port); -int misdn_cfg_get_next_port_spin(int port); - -int misdn_cfg_is_msn_valid(int port, char* msn); -int misdn_cfg_is_port_valid(int port); -int misdn_cfg_is_group_method(char *group, enum misdn_cfg_method meth); - -#if 0 -char *misdn_cfg_get_next_group(char *group); -int misdn_cfg_get_next_port_in_group(int port, char *group); -#endif - -#endif diff --git a/1.4.23-rc4/channels/misdn/ie.c b/1.4.23-rc4/channels/misdn/ie.c deleted file mode 100644 index 2e7fae998..000000000 --- a/1.4.23-rc4/channels/misdn/ie.c +++ /dev/null @@ -1,1422 +0,0 @@ - -/* - * Chan_Misdn -- Channel Driver for Asterisk - * - * Interface to mISDN - * - * Copyright (C) 2005, Christian Richter - * - * Christian Richter <crich@beronet.com> - * - * heaviliy patched from jollys ie.cpp, jolly gave me ALL - * rights for this code, i can even have my own copyright on it. - * - * This program is free software, distributed under the terms of - * the GNU General Public License - */ - -/* - the pointer of enc_ie_* always points to the IE itself - if qi is not NULL (TE-mode), offset is set -*/ - - -#include <string.h> - -#include <mISDNuser/mISDNlib.h> -#include <mISDNuser/isdn_net.h> -#include <mISDNuser/l3dss1.h> -#include <mISDNuser/net_l3.h> - - - -#define MISDN_IE_DEBG 0 - -/* support stuff */ -static void strnncpy(char *dest, char *src, int len, int dst_len) -{ - if (len > dst_len-1) - len = dst_len-1; - strncpy((char *)dest, (char *)src, len); - dest[len] = '\0'; -} - - -/* IE_COMPLETE */ -static void enc_ie_complete(unsigned char **ntmode, msg_t *msg, int complete, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - - if (complete<0 || complete>1) - { - printf("%s: ERROR: complete(%d) is out of range.\n", __FUNCTION__, complete); - return; - } - - if (complete) - if (MISDN_IE_DEBG) printf(" complete=%d\n", complete); - - if (complete) - { - p = msg_put(msg, 1); - if (nt) - { - *ntmode = p; - } else - qi->QI_ELEMENT(sending_complete) = p - (unsigned char *)qi - sizeof(Q931_info_t); - - p[0] = IE_COMPLETE; - } -} - -static void dec_ie_complete(unsigned char *p, Q931_info_t *qi, int *complete, int nt, struct misdn_bchannel *bc) -{ - *complete = 0; - if (!nt) - { - if (qi->QI_ELEMENT(sending_complete)) - *complete = 1; - } else - if (p) - *complete = 1; - - if (*complete) - if (MISDN_IE_DEBG) printf(" complete=%d\n", *complete); -} - - -/* IE_BEARER */ -static void enc_ie_bearer(unsigned char **ntmode, msg_t *msg, int coding, int capability, int mode, int rate, int multi, int user, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - if (coding<0 || coding>3) - { - printf("%s: ERROR: coding(%d) is out of range.\n", __FUNCTION__, coding); - return; - } - if (capability<0 || capability>31) - { - printf("%s: ERROR: capability(%d) is out of range.\n", __FUNCTION__, capability); - return; - } - if (mode<0 || mode>3) - { - printf("%s: ERROR: mode(%d) is out of range.\n", __FUNCTION__, mode); - return; - } - if (rate<0 || rate>31) - { - printf("%s: ERROR: rate(%d) is out of range.\n", __FUNCTION__, rate); - return; - } - if (multi>127) - { - printf("%s: ERROR: multi(%d) is out of range.\n", __FUNCTION__, multi); - return; - } - if (user>31) - { - printf("%s: ERROR: user L1(%d) is out of range.\n", __FUNCTION__, rate); - return; - } - if (rate!=24 && multi>=0) - { - printf("%s: WARNING: multi(%d) is only possible if rate(%d) would be 24.\n", __FUNCTION__, multi, rate); - multi = -1; - } - - if (MISDN_IE_DEBG) printf(" coding=%d capability=%d mode=%d rate=%d multi=%d user=%d\n", coding, capability, mode, rate, multi, user); - - l = 2 + (multi>=0) + (user>=0); - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(bearer_capability) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_BEARER; - p[1] = l; - p[2] = 0x80 + (coding<<5) + capability; - p[3] = 0x80 + (mode<<5) + rate; - if (multi >= 0) - p[4] = 0x80 + multi; - if (user >= 0) - p[4+(multi>=0)] = 0xa0 + user; -} - -static void dec_ie_bearer(unsigned char *p, Q931_info_t *qi, int *coding, int *capability, int *mode, int *rate, int *multi, int *user, - int *async, int *urate, int *stopbits, int *dbits, int *parity, int nt, struct misdn_bchannel *bc) -{ - int octet; - *coding = -1; - *capability = -1; - *mode = -1; - *rate = -1; - *multi = -1; - *user = -1; - *async = -1; - *urate = -1; - *stopbits = -1; - *dbits = -1; - *parity = -1; - - if (!nt) - { - p = NULL; -#ifdef LLC_SUPPORT - if (qi->QI_ELEMENT(llc)) { - - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(llc) + 1; - } -#endif - if (qi->QI_ELEMENT(bearer_capability)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(bearer_capability) + 1; - } - if (!p) - return; - - if (p[0] < 2) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - *coding = (p[1]&0x60) >> 5; - *capability = p[1] & 0x1f; - octet = 2; - if (!(p[1] & 0x80)) - octet++; - - if (p[0] < octet) - goto done; - - *mode = (p[octet]&0x60) >> 5; - *rate = p[octet] & 0x1f; - - octet++; - - if (p[0] < octet) - goto done; - - if (*rate == 0x18) { - /* Rate multiplier only present if 64Kb/s base rate */ - *multi = p[octet++] & 0x7f; - } - - if (p[0] < octet) - goto done; - - /* Start L1 info */ - if ((p[octet] & 0x60) == 0x20) { - *user = p[octet] & 0x1f; - - if (p[0] <= octet) - goto done; - - if (p[octet++] & 0x80) - goto l2; - - *async = !!(p[octet] & 0x40); - /* 0x20 is inband negotiation */ - *urate = p[octet] & 0x1f; - - if (p[0] <= octet) - goto done; - - if (p[octet++] & 0x80) - goto l2; - - /* Ignore next byte for now: Intermediate rate, NIC, flow control */ - - if (p[0] <= octet) - goto done; - - if (p[octet++] & 0x80) - goto l2; - - /* And the next one. Header, multiframe, mode, assignor/ee, negotiation */ - - if (p[0] <= octet) - goto done; - - if (!p[octet++] & 0x80) - goto l2; - - /* Wheee. V.110 speed information */ - - *stopbits = (p[octet] & 0x60) >> 5; - *dbits = (p[octet] & 0x18) >> 3; - *parity = p[octet] & 7; - - octet++; - } - l2: /* Nobody seems to want the rest so we don't bother (yet) */ - done: - if (MISDN_IE_DEBG) printf(" coding=%d capability=%d mode=%d rate=%d multi=%d user=%d async=%d urate=%d stopbits=%d dbits=%d parity=%d\n", *coding, *capability, *mode, *rate, *multi, *user, *async, *urate, *stopbits, *dbits, *parity); -} - - -/* IE_CALL_ID */ -#if 0 -static void enc_ie_call_id(unsigned char **ntmode, msg_t *msg, char *callid, int callid_len, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - char debug[25]; - int i; - - if (!callid || callid_len<=0) - { - return; - } - if (callid_len>8) - { - printf("%s: ERROR: callid_len(%d) is out of range.\n", __FUNCTION__, callid_len); - return; - } - - i = 0; - while(i < callid_len) - { - if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", callid[i]); - i++; - } - - if (MISDN_IE_DEBG) printf(" callid%s\n", debug); - - l = callid_len; - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(call_id) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_CALL_ID; - p[1] = l; - memcpy(p+2, callid, callid_len); -} -#endif - -#if 0 -static void dec_ie_call_id(unsigned char *p, Q931_info_t *qi, char *callid, int *callid_len, int nt, struct misdn_bchannel *bc) -{ - char debug[25]; - int i; - - *callid_len = -1; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(call_id)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(call_id) + 1; - } - if (!p) - return; - if (p[0] > 8) - { - printf("%s: ERROR: IE too long (%d).\n", __FUNCTION__, p[0]); - return; - } - - *callid_len = p[0]; - memcpy(callid, p+1, *callid_len); - - i = 0; - while(i < *callid_len) - { - if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", callid[i]); - i++; - } - - if (MISDN_IE_DEBG) printf(" callid%s\n", debug); -} -#endif - -/* IE_CALLED_PN */ -static void enc_ie_called_pn(unsigned char **ntmode, msg_t *msg, int type, int plan, char *number, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - if (type<0 || type>7) - { - printf("%s: ERROR: type(%d) is out of range.\n", __FUNCTION__, type); - return; - } - if (plan<0 || plan>15) - { - printf("%s: ERROR: plan(%d) is out of range.\n", __FUNCTION__, plan); - return; - } - if (!number[0]) - { - printf("%s: ERROR: number is not given.\n", __FUNCTION__); - return; - } - - if (MISDN_IE_DEBG) printf(" type=%d plan=%d number='%s'\n", type, plan, number); - - l = 1+strlen((char *)number); - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(called_nr) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_CALLED_PN; - p[1] = l; - p[2] = 0x80 + (type<<4) + plan; - strncpy((char *)p+3, (char *)number, strlen((char *)number)); -} - -static void dec_ie_called_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, char *number, int number_len, int nt, struct misdn_bchannel *bc) -{ - *type = -1; - *plan = -1; - *number = '\0'; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(called_nr)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(called_nr) + 1; - } - if (!p) - return; - if (p[0] < 2) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - *type = (p[1]&0x70) >> 4; - *plan = p[1] & 0xf; - strnncpy(number, (char *)p+2, p[0]-1, number_len); - - if (MISDN_IE_DEBG) printf(" type=%d plan=%d number='%s'\n", *type, *plan, number); -} - - -/* IE_CALLING_PN */ -static void enc_ie_calling_pn(unsigned char **ntmode, msg_t *msg, int type, int plan, int present, int screen, char *number, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - if (type<0 || type>7) - { - printf("%s: ERROR: type(%d) is out of range.\n", __FUNCTION__, type); - return; - } - if (plan<0 || plan>15) - { - printf("%s: ERROR: plan(%d) is out of range.\n", __FUNCTION__, plan); - return; - } - if (present>3) - { - printf("%s: ERROR: present(%d) is out of range.\n", __FUNCTION__, present); - return; - } - if (present >= 0) if (screen<0 || screen>3) - { - printf("%s: ERROR: screen(%d) is out of range.\n", __FUNCTION__, screen); - return; - } - - if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d number='%s'\n", type, plan, present, screen, number); - - l = 1; - if (number) if (number[0]) - l += strlen((char *)number); - if (present >= 0) - l += 1; - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(calling_nr) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_CALLING_PN; - p[1] = l; - if (present >= 0) - { - p[2] = 0x00 + (type<<4) + plan; - p[3] = 0x80 + (present<<5) + screen; - if (number) if (number[0]) - strncpy((char *)p+4, (char *)number, strlen((char *)number)); - } else - { - p[2] = 0x80 + (type<<4) + plan; - if (number) if (number[0]) - strncpy((char *)p+3, (char *)number, strlen((char *)number)); - } -} - -static void dec_ie_calling_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, int number_len, int nt, struct misdn_bchannel *bc) -{ - *type = -1; - *plan = -1; - *present = -1; - *screen = -1; - *number = '\0'; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(calling_nr)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(calling_nr) + 1; - } - if (!p) - return; - if (p[0] < 1) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - *type = (p[1]&0x70) >> 4; - *plan = p[1] & 0xf; - if (!(p[1] & 0x80)) - { - if (p[0] < 2) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - *present = (p[2]&0x60) >> 5; - *screen = p[2] & 0x3; - strnncpy(number, (char *)p+3, p[0]-2, number_len); - } else - { - strnncpy(number, (char *)p+2, p[0]-1, number_len); - /* SPECIAL workarround for IBT software bug */ - /* if (number[0]==0x80) */ - /* strcpy((char *)number, (char *)number+1); */ - } - - if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d number='%s'\n", *type, *plan, *present, *screen, number); -} - - -/* IE_CONNECTED_PN */ -static void enc_ie_connected_pn(unsigned char **ntmode, msg_t *msg, int type, int plan, int present, int screen, char *number, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - if (type<0 || type>7) - { - printf("%s: ERROR: type(%d) is out of range.\n", __FUNCTION__, type); - return; - } - if (plan<0 || plan>15) - { - printf("%s: ERROR: plan(%d) is out of range.\n", __FUNCTION__, plan); - return; - } - if (present>3) - { - printf("%s: ERROR: present(%d) is out of range.\n", __FUNCTION__, present); - return; - } - if (present >= 0) if (screen<0 || screen>3) - { - printf("%s: ERROR: screen(%d) is out of range.\n", __FUNCTION__, screen); - return; - } - - if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d number='%s'\n", type, plan, present, screen, number); - - l = 1; - if (number) if (number[0]) - l += strlen((char *)number); - if (present >= 0) - l += 1; - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(connected_nr) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_CONNECT_PN; - p[1] = l; - if (present >= 0) - { - p[2] = 0x00 + (type<<4) + plan; - p[3] = 0x80 + (present<<5) + screen; - if (number) if (number[0]) - strncpy((char *)p+4, (char *)number, strlen((char *)number)); - } else - { - p[2] = 0x80 + (type<<4) + plan; - if (number) if (number[0]) - strncpy((char *)p+3, (char *)number, strlen((char *)number)); - } -} - -static void dec_ie_connected_pn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, char *number, int number_len, int nt, struct misdn_bchannel *bc) -{ - *type = -1; - *plan = -1; - *present = -1; - *screen = -1; - *number = '\0'; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(connected_nr)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(connected_nr) + 1; - } - if (!p) - return; - if (p[0] < 1) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - *type = (p[1]&0x70) >> 4; - *plan = p[1] & 0xf; - if (!(p[1] & 0x80)) - { - if (p[0] < 2) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - *present = (p[2]&0x60) >> 5; - *screen = p[2] & 0x3; - strnncpy(number, (char *)p+3, p[0]-2, number_len); - } else - { - strnncpy(number, (char *)p+2, p[0]-1, number_len); - } - - if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d number='%s'\n", *type, *plan, *present, *screen, number); -} - - -/* IE_CAUSE */ -static void enc_ie_cause(unsigned char **ntmode, msg_t *msg, int location, int cause, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - if (location<0 || location>7) - { - printf("%s: ERROR: location(%d) is out of range.\n", __FUNCTION__, location); - return; - } - if (cause<0 || cause>127) - { - printf("%s: ERROR: cause(%d) is out of range.\n", __FUNCTION__, cause); - return; - } - - if (MISDN_IE_DEBG) printf(" location=%d cause=%d\n", location, cause); - - l = 2; - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(cause) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_CAUSE; - p[1] = l; - p[2] = 0x80 + location; - p[3] = 0x80 + cause; -} - -#if 0 -static void enc_ie_cause_standalone(unsigned char **ntmode, msg_t *msg, int location, int cause, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p = msg_put(msg, 4); - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - if (ntmode) - *ntmode = p+1; - else - qi->QI_ELEMENT(cause) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_CAUSE; - p[1] = 2; - p[2] = 0x80 + location; - p[3] = 0x80 + cause; -} -#endif - -static void dec_ie_cause(unsigned char *p, Q931_info_t *qi, int *location, int *cause, int nt, struct misdn_bchannel *bc) -{ - *location = -1; - *cause = -1; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(cause)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(cause) + 1; - } - if (!p) - return; - if (p[0] < 2) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - *location = p[1] & 0x0f; - *cause = p[2] & 0x7f; - - if (MISDN_IE_DEBG) printf(" location=%d cause=%d\n", *location, *cause); -} - - -/* IE_CHANNEL_ID */ -static void enc_ie_channel_id(unsigned char **ntmode, msg_t *msg, int exclusive, int channel, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - struct misdn_stack *stack=get_stack_by_bc(bc); - int pri = stack->pri; - - if (exclusive<0 || exclusive>1) - { - printf("%s: ERROR: exclusive(%d) is out of range.\n", __FUNCTION__, exclusive); - return; - } - if ((channel<0 || channel>0xff) - || (!pri && (channel>2 && channel<0xff)) - || (pri && (channel>31 && channel<0xff)) - || (pri && channel==16)) - { - printf("%s: ERROR: channel(%d) is out of range.\n", __FUNCTION__, channel); - return; - } - - /* if (MISDN_IE_DEBG) printf(" exclusive=%d channel=%d\n", exclusive, channel); */ - - - if (!pri) - { - /* BRI */ - l = 1; - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(channel_id) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_CHANNEL_ID; - p[1] = l; - if (channel == 0xff) - channel = 3; - p[2] = 0x80 + (exclusive<<3) + channel; - /* printf(" exclusive=%d channel=%d\n", exclusive, channel); */ - } else - { - /* PRI */ - if (channel == 0) /* no channel */ - return; /* IE not present */ -/* if (MISDN_IE_DEBG) printf("channel = %d\n", channel); */ - if (channel == 0xff) /* any channel */ - { - l = 1; - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(channel_id) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_CHANNEL_ID; - p[1] = l; - p[2] = 0x80 + 0x20 + 0x03; -/* if (MISDN_IE_DEBG) printf("%02x\n", p[2]); */ - return; /* end */ - } - l = 3; - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(channel_id) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_CHANNEL_ID; - p[1] = l; - p[2] = 0x80 + 0x20 + (exclusive<<3) + 0x01; - p[3] = 0x80 + 3; /* CCITT, Number, B-type */ - p[4] = 0x80 + channel; -/* if (MISDN_IE_DEBG) printf("%02x %02x %02x\n", p[2], p[3], p[4]); */ - } -} - -static void dec_ie_channel_id(unsigned char *p, Q931_info_t *qi, int *exclusive, int *channel, int nt, struct misdn_bchannel *bc) -{ - struct misdn_stack *stack=get_stack_by_bc(bc); - int pri =stack->pri; - - *exclusive = -1; - *channel = -1; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(channel_id)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(channel_id) + 1; - } - if (!p) - return; - if (p[0] < 1) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - if (p[1] & 0x40) - { - printf("%s: ERROR: refering to channels of other interfaces is not supported.\n", __FUNCTION__); - return; - } - if (p[1] & 0x04) - { - printf("%s: ERROR: using d-channel is not supported.\n", __FUNCTION__); - return; - } - - *exclusive = (p[1]&0x08) >> 3; - if (!pri) - { - /* BRI */ - if (p[1] & 0x20) - { - printf("%s: ERROR: extended channel ID with non PRI interface.\n", __FUNCTION__); - return; - } - *channel = p[1] & 0x03; - if (*channel == 3) - *channel = 0xff; - } else - { - /* PRI */ - if (p[0] < 1) - { - printf("%s: ERROR: IE too short for PRI (%d).\n", __FUNCTION__, p[0]); - return; - } - if (!(p[1] & 0x20)) - { - printf("%s: ERROR: basic channel ID with PRI interface.\n", __FUNCTION__); - return; - } - if ((p[1]&0x03) == 0x00) - { - /* no channel */ - *channel = 0; - return; - } - if ((p[1]&0x03) == 0x03) - { - /* any channel */ - *channel = 0xff; - return; - } - if (p[0] < 3) - { - printf("%s: ERROR: IE too short for PRI with channel(%d).\n", __FUNCTION__, p[0]); - return; - } - if (p[2] & 0x10) - { - printf("%s: ERROR: channel map not supported.\n", __FUNCTION__); - return; - } - *channel = p[3] & 0x7f; - if ( (*channel<1) | (*channel==16) | (*channel>31)) - { - printf("%s: ERROR: PRI interface channel out of range (%d).\n", __FUNCTION__, *channel); - return; - } -/* if (MISDN_IE_DEBG) printf("%02x %02x %02x\n", p[1], p[2], p[3]); */ - } - - if (MISDN_IE_DEBG) printf(" exclusive=%d channel=%d\n", *exclusive, *channel); -} - - -/* IE_DATE */ -static void enc_ie_date(unsigned char **ntmode, msg_t *msg, time_t ti, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - struct tm *tm; - - tm = localtime(&ti); - if (!tm) - { - printf("%s: ERROR: gettimeofday() returned NULL.\n", __FUNCTION__); - return; - } - - if (MISDN_IE_DEBG) printf(" year=%d month=%d day=%d hour=%d minute=%d\n", tm->tm_year%100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - l = 5; - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(date) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_DATE; - p[1] = l; - p[2] = tm->tm_year % 100; - p[3] = tm->tm_mon + 1; - p[4] = tm->tm_mday; - p[5] = tm->tm_hour; - p[6] = tm->tm_min; -} - - -/* IE_DISPLAY */ -static void enc_ie_display(unsigned char **ntmode, msg_t *msg, char *display, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - if (!display[0]) - { - printf("%s: ERROR: display text not given.\n", __FUNCTION__); - return; - } - - if (strlen((char *)display) > 80) - { - printf("%s: WARNING: display text too long (max 80 chars), cutting.\n", __FUNCTION__); - display[80] = '\0'; - } - - /* if (MISDN_IE_DEBG) printf(" display='%s' (len=%d)\n", display, strlen((char *)display)); */ - - l = strlen((char *)display); - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(display) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_DISPLAY; - p[1] = l; - strncpy((char *)p+2, (char *)display, strlen((char *)display)); -} - -#if 0 -static void dec_ie_display(unsigned char *p, Q931_info_t *qi, char *display, int display_len, int nt, struct misdn_bchannel *bc) -{ - *display = '\0'; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(display)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(display) + 1; - } - if (!p) - return; - if (p[0] < 1) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - strnncpy(display, (char *)p+1, p[0], display_len); - - if (MISDN_IE_DEBG) printf(" display='%s'\n", display); -} -#endif - -/* IE_KEYPAD */ -#if 1 -static void enc_ie_keypad(unsigned char **ntmode, msg_t *msg, char *keypad, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - if (!keypad[0]) - { - printf("%s: ERROR: keypad info not given.\n", __FUNCTION__); - return; - } - - if (MISDN_IE_DEBG) printf(" keypad='%s'\n", keypad); - - l = strlen(keypad); - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(keypad) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_KEYPAD; - p[1] = l; - strncpy((char *)p+2, keypad, strlen(keypad)); -} -#endif - -static void dec_ie_keypad(unsigned char *p, Q931_info_t *qi, char *keypad, int keypad_len, int nt, struct misdn_bchannel *bc) -{ - *keypad = '\0'; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(keypad)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(keypad) + 1; - } - if (!p) - return; - if (p[0] < 1) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - strnncpy(keypad, (char *)p+1, p[0], keypad_len); - - if (MISDN_IE_DEBG) printf(" keypad='%s'\n", keypad); -} - - -/* IE_NOTIFY */ -#if 0 -static void enc_ie_notify(unsigned char **ntmode, msg_t *msg, int notify, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - if (notify<0 || notify>0x7f) - { - printf("%s: ERROR: notify(%d) is out of range.\n", __FUNCTION__, notify); - return; - } - - if (MISDN_IE_DEBG) printf(" notify=%d\n", notify); - - l = 1; - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(notify) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_NOTIFY; - p[1] = l; - p[2] = 0x80 + notify; -} -#endif - -#if 0 -static void dec_ie_notify(unsigned char *p, Q931_info_t *qi, int *notify, int nt, struct misdn_bchannel *bc) -{ - *notify = -1; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(notify)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(notify) + 1; - } - if (!p) - return; - if (p[0] < 1) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - *notify = p[1] & 0x7f; - - if (MISDN_IE_DEBG) printf(" notify=%d\n", *notify); -} -#endif - - -/* IE_PROGRESS */ -static void enc_ie_progress(unsigned char **ntmode, msg_t *msg, int coding, int location, int progress, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - if (coding<0 || coding>0x03) - { - printf("%s: ERROR: coding(%d) is out of range.\n", __FUNCTION__, coding); - return; - } - if (location<0 || location>0x0f) - { - printf("%s: ERROR: location(%d) is out of range.\n", __FUNCTION__, location); - return; - } - if (progress<0 || progress>0x7f) - { - printf("%s: ERROR: progress(%d) is out of range.\n", __FUNCTION__, progress); - return; - } - - if (MISDN_IE_DEBG) printf(" coding=%d location=%d progress=%d\n", coding, location, progress); - - l = 2; - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(progress) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_PROGRESS; - p[1] = l; - p[2] = 0x80 + (coding<<5) + location; - p[3] = 0x80 + progress; -} - -static void dec_ie_progress(unsigned char *p, Q931_info_t *qi, int *coding, int *location, int *progress, int nt, struct misdn_bchannel *bc) -{ - *coding = -1; - *location = -1; - //*progress = -1; - *progress = 0; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(progress)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(progress) + 1; - } - if (!p) - return; - if (p[0] < 1) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - *coding = (p[1]&0x60) >> 5; - *location = p[1] & 0x0f; - *progress = p[2] & 0x7f; - - if (MISDN_IE_DEBG) printf(" coding=%d location=%d progress=%d\n", *coding, *location, *progress); -} - - -/* IE_REDIR_NR (redirecting = during MT_SETUP) */ -static void enc_ie_redir_nr(unsigned char **ntmode, msg_t *msg, int type, int plan, int present, int screen, int reason, char *number, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - if (type<0 || type>7) - { - printf("%s: ERROR: type(%d) is out of range.\n", __FUNCTION__, type); - return; - } - if (plan<0 || plan>15) - { - printf("%s: ERROR: plan(%d) is out of range.\n", __FUNCTION__, plan); - return; - } - if (present > 3) - { - printf("%s: ERROR: present(%d) is out of range.\n", __FUNCTION__, present); - return; - } - if (present >= 0) if (screen<0 || screen>3) - { - printf("%s: ERROR: screen(%d) is out of range.\n", __FUNCTION__, screen); - return; - } - if (reason > 0x0f) - { - printf("%s: ERROR: reason(%d) is out of range.\n", __FUNCTION__, reason); - return; - } - - if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d readon=%d number='%s'\n", type, plan, present, screen, reason, number); - - l = 1; - if (number) - l += strlen((char *)number); - if (present >= 0) - { - l += 1; - if (reason >= 0) - l += 1; - } - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(redirect_nr) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_REDIR_NR; - p[1] = l; - if (present >= 0) - { - if (reason >= 0) - { - p[2] = 0x00 + (type<<4) + plan; - p[3] = 0x00 + (present<<5) + screen; - p[4] = 0x80 + reason; - if (number) - strncpy((char *)p+5, (char *)number, strlen((char *)number)); - } else - { - p[2] = 0x00 + (type<<4) + plan; - p[3] = 0x80 + (present<<5) + screen; - if (number) - strncpy((char *)p+4, (char *)number, strlen((char *)number)); - } - } else - { - p[2] = 0x80 + (type<<4) + plan; - if (number) if (number[0]) - strncpy((char *)p+3, (char *)number, strlen((char *)number)); - } -} - -static void dec_ie_redir_nr(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, int *screen, int *reason, char *number, int number_len, int nt, struct misdn_bchannel *bc) -{ - *type = -1; - *plan = -1; - *present = -1; - *screen = -1; - *reason = -1; - *number = '\0'; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(redirect_nr)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(redirect_nr) + 1; - } - if (!p) - return; - if (p[0] < 1) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - *type = (p[1]&0x70) >> 4; - *plan = p[1] & 0xf; - if (!(p[1] & 0x80)) - { - *present = (p[2]&0x60) >> 5; - *screen = p[2] & 0x3; - if (!(p[2] & 0x80)) - { - *reason = p[3] & 0x0f; - strnncpy(number, (char *)p+4, p[0]-3, number_len); - } else - { - strnncpy(number, (char *)p+3, p[0]-2, number_len); - } - } else - { - strnncpy(number, (char *)p+2, p[0]-1, number_len); - } - - if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d screen=%d reason=%d number='%s'\n", *type, *plan, *present, *screen, *reason, number); -} - - -/* IE_REDIR_DN (redirection = during MT_NOTIFY) */ -#if 0 -static void enc_ie_redir_dn(unsigned char **ntmode, msg_t *msg, int type, int plan, int present, char *number, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; -/* Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); */ - int l; - - if (type<0 || type>7) - { - printf("%s: ERROR: type(%d) is out of range.\n", __FUNCTION__, type); - return; - } - if (plan<0 || plan>15) - { - printf("%s: ERROR: plan(%d) is out of range.\n", __FUNCTION__, plan); - return; - } - if (present > 3) - { - printf("%s: ERROR: present(%d) is out of range.\n", __FUNCTION__, present); - return; - } - - if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d number='%s'\n", type, plan, present, number); - - l = 1; - if (number) - l += strlen((char *)number); - if (present >= 0) - l += 1; - p = msg_put(msg, l+2); - if (nt) - *ntmode = p+1; - else -/* #warning REINSERT redir_dn, when included in te-mode */ - /*qi->QI_ELEMENT(redir_dn) = p - (unsigned char *)qi - sizeof(Q931_info_t)*/; - p[0] = IE_REDIR_DN; - p[1] = l; - if (present >= 0) - { - p[2] = 0x00 + (type<<4) + plan; - p[3] = 0x80 + (present<<5); - if (number) - strncpy((char *)p+4, (char *)number, strlen((char *)number)); - } else - { - p[2] = 0x80 + (type<<4) + plan; - if (number) - strncpy((char *)p+3, (char *)number, strlen((char *)number)); - } -} -#endif - -#if 0 -static void dec_ie_redir_dn(unsigned char *p, Q931_info_t *qi, int *type, int *plan, int *present, char *number, int number_len, int nt, struct misdn_bchannel *bc) -{ - *type = -1; - *plan = -1; - *present = -1; - *number = '\0'; - - if (!nt) - { - p = NULL; -/* #warning REINSERT redir_dn, when included in te-mode */ -/* if (qi->QI_ELEMENT(redir_dn)) */ -/* p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(redir_dn) + 1; */ - } - if (!p) - return; - if (p[0] < 1) - { - printf("%s: ERROR: IE too short (%d).\n", __FUNCTION__, p[0]); - return; - } - - *type = (p[1]&0x70) >> 4; - *plan = p[1] & 0xf; - if (!(p[1] & 0x80)) - { - *present = (p[2]&0x60) >> 5; - strnncpy(number, (char *)p+3, p[0]-2, number_len); - } else - { - strnncpy(number, (char *)p+2, p[0]-1, number_len); - } - - if (MISDN_IE_DEBG) printf(" type=%d plan=%d present=%d number='%s'\n", *type, *plan, *present, number); -} -#endif - - -/* IE_USERUSER */ -#if 1 -static void enc_ie_useruser(unsigned char **ntmode, msg_t *msg, int protocol, char *user, int user_len, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - int l; - - char debug[768]; - int i; - - if (protocol<0 || protocol>127) - { - printf("%s: ERROR: protocol(%d) is out of range.\n", __FUNCTION__, protocol); - return; - } - if (!user || user_len<=0) - { - return; - } - - i = 0; - while(i < user_len) - { - if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", user[i]); - i++; - } - - if (MISDN_IE_DEBG) printf(" protocol=%d user-user%s\n", protocol, debug); - - l = user_len+1; - p = msg_put(msg, l+3); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(useruser) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_USER_USER; - p[1] = l; - p[2] = protocol; - memcpy(p+3, user, user_len); -} -#endif - -#if 1 -static void dec_ie_useruser(unsigned char *p, Q931_info_t *qi, int *protocol, char *user, int *user_len, int nt, struct misdn_bchannel *bc) -{ - char debug[768]; - int i; - - *user_len = 0; - *protocol = -1; - - if (!nt) - { - p = NULL; - if (qi->QI_ELEMENT(useruser)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(useruser) + 1; - } - if (!p) - return; - - *user_len = p[0]-1; - if (p[0] < 1) - return; - *protocol = p[1]; - memcpy(user, p+2, (*user_len<=128)?*(user_len):128); /* clip to 128 maximum */ - - i = 0; - while(i < *user_len) - { - if (MISDN_IE_DEBG) printf(debug+(i*3), " %02x", user[i]); - i++; - } - debug[i*3] = '\0'; - - if (MISDN_IE_DEBG) printf(" protocol=%d user-user%s\n", *protocol, debug); -} -#endif - -/* IE_DISPLAY */ -static void enc_ie_restart_ind(unsigned char **ntmode, msg_t *msg, unsigned char rind, int nt, struct misdn_bchannel *bc) -{ - unsigned char *p; - Q931_info_t *qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - /* if (MISDN_IE_DEBG) printf(" display='%s' (len=%d)\n", display, strlen((char *)display)); */ - - p = msg_put(msg, 3); - if (nt) - *ntmode = p+1; - else - qi->QI_ELEMENT(restart_ind) = p - (unsigned char *)qi - sizeof(Q931_info_t); - p[0] = IE_RESTART_IND; - p[1] = 1; - p[2] = rind; - -} - diff --git a/1.4.23-rc4/channels/misdn/isdn_lib.c b/1.4.23-rc4/channels/misdn/isdn_lib.c deleted file mode 100644 index b21c794e7..000000000 --- a/1.4.23-rc4/channels/misdn/isdn_lib.c +++ /dev/null @@ -1,4661 +0,0 @@ -/* - * Chan_Misdn -- Channel Driver for Asterisk - * - * Interface to mISDN - * - * Copyright (C) 2004, Christian Richter - * - * Christian Richter <crich@beronet.com> - * - * This program is free software, distributed under the terms of - * the GNU General Public License - */ - -/*! \file - * \brief Interface to mISDN - * \author Christian Richter <crich@beronet.com> - */ - - - -#include <syslog.h> -#include <sys/time.h> -#include <mISDNuser/isdn_debug.h> - -#include "isdn_lib_intern.h" -#include "isdn_lib.h" - -/* - * Define ARRAY_LEN() because I cannot - * #include "asterisk/utils.h" - */ -#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) - -#include "asterisk/causes.h" - -void misdn_join_conf(struct misdn_bchannel *bc, int conf_id); -void misdn_split_conf(struct misdn_bchannel *bc, int conf_id); - -int queue_cleanup_bc(struct misdn_bchannel *bc) ; - -int misdn_lib_get_l2_up(struct misdn_stack *stack); - -struct misdn_stack* get_misdn_stack( void ); - -static int set_chan_in_stack(struct misdn_stack *stack, int channel); - -int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh); - -int misdn_lib_port_is_pri(int port) -{ - struct misdn_stack *stack=get_misdn_stack(); - for ( ; stack; stack=stack->next) { - if (stack->port == port) { - return stack->pri; - } - } - - return -1; -} - -static void misdn_make_dummy(struct misdn_bchannel *dummybc, int port, int l3id, int nt, int channel) -{ - memset (dummybc,0,sizeof(struct misdn_bchannel)); - dummybc->port=port; - dummybc->l3_id=l3id; - dummybc->nt=nt; - dummybc->dummy=1; - dummybc->channel=channel; -} - -int misdn_lib_port_block(int port) -{ - struct misdn_stack *stack=get_misdn_stack(); - for ( ; stack; stack=stack->next) { - if (stack->port == port) { - stack->blocked=1; - return 0; - } - } - return -1; - -} - -int misdn_lib_port_unblock(int port) -{ - struct misdn_stack *stack=get_misdn_stack(); - for ( ; stack; stack=stack->next) { - if (stack->port == port) { - stack->blocked=0; - return 0; - } - } - return -1; - -} - -int misdn_lib_is_port_blocked(int port) -{ - struct misdn_stack *stack=get_misdn_stack(); - for ( ; stack; stack=stack->next) { - if (stack->port == port) { - return stack->blocked; - } - } - return -1; -} - -int misdn_lib_is_ptp(int port) -{ - struct misdn_stack *stack=get_misdn_stack(); - for ( ; stack; stack=stack->next) { - if (stack->port == port) return stack->ptp; - } - return -1; -} - -int misdn_lib_get_maxchans(int port) -{ - struct misdn_stack *stack=get_misdn_stack(); - for ( ; stack; stack=stack->next) { - if (stack->port == port) { - if (stack->pri) - return 30; - else - return 2; - } - } - return -1; -} - - -struct misdn_stack *get_stack_by_bc(struct misdn_bchannel *bc) -{ - struct misdn_stack *stack = get_misdn_stack(); - - if (!bc) - return NULL; - - for ( ; stack; stack = stack->next) { - if (bc->port == stack->port) - return stack; - } - - return NULL; -} - - -void get_show_stack_details(int port, char *buf) -{ - struct misdn_stack *stack=get_misdn_stack(); - - for ( ; stack; stack=stack->next) { - if (stack->port == port) break; - } - - if (stack) { - sprintf(buf, "* Port %d Type %s Prot. %s L2Link %s L1Link:%s Blocked:%d", - stack->port, stack->nt ? "NT" : "TE", stack->ptp ? "PTP" : "PMP", - stack->l2link ? "UP" : "DOWN", stack->l1link ? "UP" : "DOWN", - stack->blocked); - } else { - buf[0]=0; - } -} - - -static int nt_err_cnt =0 ; - -enum global_states { - MISDN_INITIALIZING, - MISDN_INITIALIZED -} ; - -static enum global_states global_state=MISDN_INITIALIZING; - - -#include <mISDNuser/net_l2.h> -#include <mISDNuser/tone.h> -#include <unistd.h> -#include <semaphore.h> -#include <pthread.h> -#include <signal.h> - -#include "isdn_lib.h" - - -struct misdn_lib { - /*! \brief mISDN device handle returned by mISDN_open() */ - int midev; - int midev_nt; /* Not used */ - - pthread_t event_thread; - pthread_t event_handler_thread; - - void *user_data; - - msg_queue_t upqueue; - msg_queue_t activatequeue; - - sem_t new_msg; - - struct misdn_stack *stack_list; -} ; - -#ifndef ECHOCAN_ON -#define ECHOCAN_ON 123 -#define ECHOCAN_OFF 124 -#endif - -#define MISDN_DEBUG 0 - -void misdn_tx_jitter(struct misdn_bchannel *bc, int len); - -struct misdn_bchannel *find_bc_by_l3id(struct misdn_stack *stack, unsigned long l3id); - -struct misdn_bchannel *find_bc_by_confid(unsigned long confid); - -struct misdn_bchannel *stack_holder_find_bychan(struct misdn_stack *stack, int chan); - -int setup_bc(struct misdn_bchannel *bc); - -int manager_isdn_handler(iframe_t *frm ,msg_t *msg); - -int misdn_lib_port_restart(int port); -int misdn_lib_pid_restart(int pid); - -extern struct isdn_msg msgs_g[]; - -#define ISDN_PID_L3_B_USER 0x430000ff -#define ISDN_PID_L4_B_USER 0x440000ff - -/* #define MISDN_IBUF_SIZE 1024 */ -#define MISDN_IBUF_SIZE 512 - -/* Fine Tuning of Inband Signalling time */ -#define TONE_ALERT_CNT 41 /* 1 Sec */ -#define TONE_ALERT_SILENCE_CNT 200 /* 4 Sec */ - -#define TONE_BUSY_CNT 20 /* ? */ -#define TONE_BUSY_SILENCE_CNT 48 /* ? */ - -static int entity; - -static struct misdn_lib *glob_mgr; - -char tone_425_flip[TONE_425_SIZE]; -char tone_silence_flip[TONE_SILENCE_SIZE]; - -static void misdn_lib_isdn_event_catcher(void *arg); -static int handle_event_nt(void *dat, void *arg); - - -void stack_holder_add(struct misdn_stack *stack, struct misdn_bchannel *holder); -void stack_holder_remove(struct misdn_stack *stack, struct misdn_bchannel *holder); -struct misdn_bchannel *stack_holder_find(struct misdn_stack *stack, unsigned long l3id); - -/* from isdn_lib.h */ - /* user iface */ -int te_lib_init( void ) ; /* returns midev */ -void te_lib_destroy(int midev) ; -struct misdn_bchannel *manager_find_bc_by_pid(int pid); -struct misdn_bchannel *manager_find_bc_holded(struct misdn_bchannel* bc); -void manager_ph_control_block(struct misdn_bchannel *bc, int c1, void *c2, int c2_len); -void manager_clean_bc(struct misdn_bchannel *bc ); -void manager_bchannel_setup (struct misdn_bchannel *bc); -void manager_bchannel_cleanup (struct misdn_bchannel *bc); - -void ec_chunk( struct misdn_bchannel *bc, unsigned char *rxchunk, unsigned char *txchunk, int chunk_size); - /* end */ -int bchdev_echocancel_activate(struct misdn_bchannel* dev); -void bchdev_echocancel_deactivate(struct misdn_bchannel* dev); -/* end */ - - -static char *bearer2str(int cap) { - static char *bearers[]={ - "Speech", - "Audio 3.1k", - "Unres Digital", - "Res Digital", - "Unknown Bearer" - }; - - switch (cap) { - case INFO_CAPABILITY_SPEECH: - return bearers[0]; - break; - case INFO_CAPABILITY_AUDIO_3_1K: - return bearers[1]; - break; - case INFO_CAPABILITY_DIGITAL_UNRESTRICTED: - return bearers[2]; - break; - case INFO_CAPABILITY_DIGITAL_RESTRICTED: - return bearers[3]; - break; - default: - return bearers[4]; - break; - } -} - - -static char flip_table[256]; - -static void init_flip_bits(void) -{ - int i,k; - - for (i = 0 ; i < 256 ; i++) { - unsigned char sample = 0 ; - for (k = 0; k<8; k++) { - if ( i & 1 << k ) sample |= 0x80 >> k; - } - flip_table[i] = sample; - } -} - -static char * flip_buf_bits ( char * buf , int len) -{ - int i; - char * start = buf; - - for (i = 0 ; i < len; i++) { - buf[i] = flip_table[(unsigned char)buf[i]]; - } - - return start; -} - - - - -static msg_t *create_l2msg(int prim, int dinfo, int size) /* NT only */ -{ - int i = 0; - msg_t *dmsg; - - while(i < 10) - { - dmsg = prep_l3data_msg(prim, dinfo, size, 256, NULL); - if (dmsg) - return(dmsg); - - if (!i) - printf("cannot allocate memory, trying again...\n"); - i++; - usleep(300000); - } - printf("cannot allocate memory, system overloaded.\n"); - exit(-1); -} - - - -msg_t *create_l3msg(int prim, int mt, int dinfo, int size, int ntmode) -{ - int i = 0; - msg_t *dmsg; - Q931_info_t *qi; - iframe_t *frm; - - if (!ntmode) - size = sizeof(Q931_info_t)+2; - - while(i < 10) { - if (ntmode) { - dmsg = prep_l3data_msg(prim, dinfo, size, 256, NULL); - if (dmsg) { - return(dmsg); - } - } else { - dmsg = alloc_msg(size+256+mISDN_HEADER_LEN+DEFAULT_HEADROOM); - if (dmsg) - { - memset(msg_put(dmsg,size+mISDN_HEADER_LEN), 0, size+mISDN_HEADER_LEN); - frm = (iframe_t *)dmsg->data; - frm->prim = prim; - frm->dinfo = dinfo; - qi = (Q931_info_t *)(dmsg->data + mISDN_HEADER_LEN); - qi->type = mt; - return(dmsg); - } - } - - if (!i) printf("cannot allocate memory, trying again...\n"); - i++; - usleep(300000); - } - printf("cannot allocate memory, system overloaded.\n"); - exit(-1); -} - - -static int send_msg (int midev, struct misdn_bchannel *bc, msg_t *dmsg) -{ - iframe_t *frm = (iframe_t *)dmsg->data; - struct misdn_stack *stack=get_stack_by_bc(bc); - - if (!stack) { - cb_log(0,bc->port,"send_msg: IEK!! no stack\n "); - return -1; - } - - frm->addr = (stack->upper_id | FLG_MSG_DOWN); - frm->dinfo = bc->l3_id; - frm->len = (dmsg->len) - mISDN_HEADER_LEN; - - cb_log(4,stack->port,"Sending msg, prim:%x addr:%x dinfo:%x\n",frm->prim,frm->addr,frm->dinfo); - - mISDN_write(midev, dmsg->data, dmsg->len, TIMEOUT_1SEC); - free_msg(dmsg); - - return 0; -} - - -static int mypid=1; - - -int misdn_cap_is_speech(int cap) -/** Poor mans version **/ -{ - if ( (cap != INFO_CAPABILITY_DIGITAL_UNRESTRICTED) && - (cap != INFO_CAPABILITY_DIGITAL_RESTRICTED) ) return 1; - return 0; -} - -int misdn_inband_avail(struct misdn_bchannel *bc) -{ - - if (!bc->early_bconnect) { - /* We have opted to never receive any available inband recorded messages */ - return 0; - } - - switch (bc->progress_indicator) { - case INFO_PI_INBAND_AVAILABLE: - case INFO_PI_CALL_NOT_E2E_ISDN: - case INFO_PI_CALLED_NOT_ISDN: - return 1; - default: - return 0; - } - return 0; -} - - -static void dump_chan_list(struct misdn_stack *stack) -{ - int i; - - for (i=0; i <= stack->b_num; i++) { - cb_log(6, stack->port, "Idx:%d stack->cchan:%d in_use:%d Chan:%d\n",i,stack->channels[i], stack->bc[i].in_use, i+1); - } -} - - -void misdn_dump_chanlist() -{ - struct misdn_stack *stack=get_misdn_stack(); - for ( ; stack; stack=stack->next) { - dump_chan_list(stack); - } - -} - -int set_chan_in_stack(struct misdn_stack *stack, int channel) -{ - - cb_log(4,stack->port,"set_chan_in_stack: %d\n",channel); - dump_chan_list(stack); - if (channel >=1 && channel <= MAX_BCHANS) { - if (!stack->channels[channel-1]) - stack->channels[channel-1] = 1; - else { - cb_log(4,stack->port,"channel already in use:%d\n", channel ); - return -1; - } - } else { - cb_log(0,stack->port,"couldn't set channel %d in\n", channel ); - return -1; - } - - return 0; -} - - - -static int find_free_chan_in_stack(struct misdn_stack *stack, struct misdn_bchannel *bc, int channel, int dec) -{ - int i; - int chan=0; - int bnums = stack->pri ? stack->b_num : stack->b_num - 1; - - if (bc->channel_found) - return 0; - - bc->channel_found=1; - - cb_log(5,stack->port,"find_free_chan: req_chan:%d\n",channel); - - if (channel < 0 || channel > MAX_BCHANS) { - cb_log(0, stack->port, " !! out of bound call to find_free_chan_in_stack! (ch:%d)\n", channel); - return 0; - } - - channel--; - - if (dec) { - for (i = bnums; i >=0; i--) { - if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 D channel ;) and work with chan preselection */ - if (!stack->channels[i]) { - cb_log (3, stack->port, " --> found chan%s: %d\n", channel>=0?" (preselected)":"", i+1); - chan=i+1; - break; - } - } - } - } else { - for (i = 0; i <= bnums; i++) { - if (i != 15 && (channel < 0 || i == channel)) { /* skip E1 D channel ;) and work with chan preselection */ - if (!stack->channels[i]) { - cb_log (3, stack->port, " --> found chan%s: %d\n", channel>=0?" (preselected)":"", i+1); - chan=i+1; - break; - } - } - } - } - - if (!chan) { - cb_log (1, stack->port, " !! NO FREE CHAN IN STACK\n"); - dump_chan_list(stack); - bc->out_cause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION; - return -1; - } - - if (set_chan_in_stack(stack, chan)<0) { - cb_log (0, stack->port, "Channel Already in use:%d\n", chan); - bc->out_cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL; - return -1; - } - - bc->channel=chan; - return 0; -} - -static int empty_chan_in_stack(struct misdn_stack *stack, int channel) -{ - if (channel<=0 || channel>MAX_BCHANS) { - cb_log(0,stack?stack->port:0, "empty_chan_in_stack: cannot empty channel %d\n",channel); - return -1; - } - - cb_log (4, stack?stack->port:0, "empty_chan_in_stack: %d\n",channel); - stack->channels[channel-1] = 0; - dump_chan_list(stack); - return 0; -} - -char *bc_state2str(enum bchannel_state state) { - int i; - - struct bchan_state_s { - char *n; - enum bchannel_state s; - } states[] = { - {"BCHAN_CLEANED", BCHAN_CLEANED }, - {"BCHAN_EMPTY", BCHAN_EMPTY}, - {"BCHAN_SETUP", BCHAN_SETUP}, - {"BCHAN_SETUPED", BCHAN_SETUPED}, - {"BCHAN_ACTIVE", BCHAN_ACTIVE}, - {"BCHAN_ACTIVATED", BCHAN_ACTIVATED}, - {"BCHAN_BRIDGE", BCHAN_BRIDGE}, - {"BCHAN_BRIDGED", BCHAN_BRIDGED}, - {"BCHAN_RELEASE", BCHAN_RELEASE}, - {"BCHAN_RELEASED", BCHAN_RELEASED}, - {"BCHAN_CLEAN", BCHAN_CLEAN}, - {"BCHAN_CLEAN_REQUEST", BCHAN_CLEAN_REQUEST}, - {"BCHAN_ERROR", BCHAN_ERROR} - }; - - for (i=0; i< sizeof(states)/sizeof(struct bchan_state_s); i++) - if ( states[i].s == state) - return states[i].n; - - return "UNKNOWN"; -} - -void bc_state_change(struct misdn_bchannel *bc, enum bchannel_state state) -{ - cb_log(5,bc->port,"BC_STATE_CHANGE: l3id:%x from:%s to:%s\n", - bc->l3_id, - bc_state2str(bc->bc_state), - bc_state2str(state) ); - - switch (state) { - case BCHAN_ACTIVATED: - if (bc->next_bc_state == BCHAN_BRIDGED) { - misdn_join_conf(bc, bc->conf_id); - bc->next_bc_state = BCHAN_EMPTY; - return; - } - default: - bc->bc_state=state; - break; - } -} - -static void bc_next_state_change(struct misdn_bchannel *bc, enum bchannel_state state) -{ - cb_log(5,bc->port,"BC_NEXT_STATE_CHANGE: from:%s to:%s\n", - bc_state2str(bc->next_bc_state), - bc_state2str(state) ); - - bc->next_bc_state=state; -} - - -static void empty_bc(struct misdn_bchannel *bc) -{ - bc->dummy=0; - - bc->bframe_len=0; - - bc->cw= 0; - - bc->dec=0; - bc->channel = 0; - - bc->sending_complete = 0; - - bc->restart_channel=0; - - bc->conf_id = 0; - - bc->need_more_infos = 0; - - bc->send_dtmf=0; - bc->nodsp=0; - bc->nojitter=0; - - bc->time_usec=0; - - bc->rxgain=0; - bc->txgain=0; - - bc->crypt=0; - bc->curptx=0; bc->curprx=0; - - bc->crypt_key[0] = 0; - - bc->generate_tone=0; - bc->tone_cnt=0; - - bc->dnumplan=NUMPLAN_UNKNOWN; - bc->onumplan=NUMPLAN_UNKNOWN; - bc->rnumplan=NUMPLAN_UNKNOWN; - bc->cpnnumplan=NUMPLAN_UNKNOWN; - - - bc->active = 0; - - bc->early_bconnect = 1; - -#ifdef MISDN_1_2 - *bc->pipeline = 0; -#else - bc->ec_enable = 0; - bc->ec_deftaps = 128; -#endif - - bc->orig=0; - - bc->cause = AST_CAUSE_NORMAL_CLEARING; - bc->out_cause = AST_CAUSE_NORMAL_CLEARING; - bc->pres = 0; /* allowed */ - - bc->evq=EVENT_NOTHING; - - bc->progress_coding=0; - bc->progress_location=0; - bc->progress_indicator=0; - -/** Set Default Bearer Caps **/ - bc->capability=INFO_CAPABILITY_SPEECH; - bc->law=INFO_CODEC_ALAW; - bc->mode=0; - bc->rate=0x10; - bc->user1=0; - bc->urate=0; - - bc->hdlc=0; - - - bc->info_dad[0] = 0; - bc->display[0] = 0; - bc->infos_pending[0] = 0; - bc->cad[0] = 0; - bc->oad[0] = 0; - bc->dad[0] = 0; - bc->rad[0] = 0; - bc->orig_dad[0] = 0; - bc->uu[0]=0; - bc->uulen=0; - - bc->fac_in.Function = Fac_None; - bc->fac_out.Function = Fac_None; - - bc->te_choose_channel = 0; - bc->channel_found= 0; - - gettimeofday(&bc->last_used, NULL); -} - - -static int clean_up_bc(struct misdn_bchannel *bc) -{ - int ret=0; - unsigned char buff[32]; - struct misdn_stack * stack; - - cb_log(3, bc?bc->port:0, "$$$ CLEANUP CALLED pid:%d\n", bc?bc->pid:-1); - - if (!bc ) return -1; - stack=get_stack_by_bc(bc); - - if (!stack) return -1; - - switch (bc->bc_state ) { - case BCHAN_CLEANED: - cb_log(5, stack->port, "$$$ Already cleaned up bc with stid :%x\n", bc->b_stid); - return -1; - - default: - break; - } - - cb_log(2, stack->port, "$$$ Cleaning up bc with stid :%x pid:%d\n", bc->b_stid, bc->pid); - - manager_ec_disable(bc); - - manager_bchannel_deactivate(bc); - - mISDN_write_frame(stack->midev, buff, bc->layer_id|FLG_MSG_TARGET|FLG_MSG_DOWN, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - - bc->b_stid = 0; - bc_state_change(bc, BCHAN_CLEANED); - - return ret; -} - - - -static void clear_l3(struct misdn_stack *stack) -{ - int i; - - for (i=0; i<=stack->b_num; i++) { - if (global_state == MISDN_INITIALIZED) { - cb_event(EVENT_CLEANUP, &stack->bc[i], NULL); - empty_chan_in_stack(stack,i+1); - empty_bc(&stack->bc[i]); - clean_up_bc(&stack->bc[i]); - stack->bc[i].in_use = 0; - } - - } -} - -static int new_te_id = 0; - -#define MAXPROCS 0x100 - -static int misdn_lib_get_l1_down(struct misdn_stack *stack) -{ - /* Pull Up L1 */ - iframe_t act; - act.prim = PH_DEACTIVATE | REQUEST; - act.addr = stack->lower_id|FLG_MSG_DOWN; - act.dinfo = 0; - act.len = 0; - - cb_log(1, stack->port, "SENDING PH_DEACTIVATE | REQ\n"); - return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); -} - - -static int misdn_lib_get_l2_down(struct misdn_stack *stack) -{ - - if (stack->ptp && (stack->nt) ) { - msg_t *dmsg; - /* L2 */ - dmsg = create_l2msg(DL_RELEASE| REQUEST, 0, 0); - - if (stack->nst.manager_l3(&stack->nst, dmsg)) - free_msg(dmsg); - - } else { - iframe_t act; - - act.prim = DL_RELEASE| REQUEST; - act.addr = (stack->upper_id |FLG_MSG_DOWN) ; - - act.dinfo = 0; - act.len = 0; - return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); - } - - return 0; -} - - -static int misdn_lib_get_l1_up(struct misdn_stack *stack) -{ - /* Pull Up L1 */ - iframe_t act; - act.prim = PH_ACTIVATE | REQUEST; - act.addr = (stack->upper_id | FLG_MSG_DOWN) ; - - - act.dinfo = 0; - act.len = 0; - - return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); - -} - -int misdn_lib_get_l2_up(struct misdn_stack *stack) -{ - - if (stack->ptp && (stack->nt) ) { - msg_t *dmsg; - /* L2 */ - dmsg = create_l2msg(DL_ESTABLISH | REQUEST, 0, 0); - - if (stack->nst.manager_l3(&stack->nst, dmsg)) - free_msg(dmsg); - - } else { - iframe_t act; - - act.prim = DL_ESTABLISH | REQUEST; - act.addr = (stack->upper_id |FLG_MSG_DOWN) ; - - act.dinfo = 0; - act.len = 0; - return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); - } - - return 0; -} - -#if 0 -static int misdn_lib_get_l2_te_ptp_up(struct misdn_stack *stack) -{ - iframe_t act; - - act.prim = DL_ESTABLISH | REQUEST; - act.addr = (stack->upper_id & ~LAYER_ID_MASK) | 3 | FLG_MSG_DOWN; - - act.dinfo = 0; - act.len = 0; - return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); - return 0; -} -#endif - -static int misdn_lib_get_short_status(struct misdn_stack *stack) -{ - iframe_t act; - - - act.prim = MGR_SHORTSTATUS | REQUEST; - - act.addr = (stack->upper_id | MSG_BROADCAST) ; - - act.dinfo = SSTATUS_BROADCAST_BIT | SSTATUS_ALL; - - act.len = 0; - return mISDN_write(stack->midev, &act, mISDN_HEADER_LEN+act.len, TIMEOUT_1SEC); -} - - - -static int create_process(int midev, struct misdn_bchannel *bc) -{ - iframe_t ncr; - int l3_id; - int proc_id; - struct misdn_stack *stack; - - stack = get_stack_by_bc(bc); - if (stack->nt) { - if (find_free_chan_in_stack(stack, bc, bc->channel_preselected ? bc->channel : 0, 0) < 0) { - return -1; - } - cb_log(4, stack->port, " --> found channel: %d\n", bc->channel); - - for (proc_id = 0; proc_id < MAXPROCS; ++proc_id) { - if (stack->procids[proc_id] == 0) { - break; - } - } /* end for */ - if (proc_id == MAXPROCS) { - cb_log(0, stack->port, "Couldn't Create New ProcId.\n"); - return -1; - } - - stack->procids[proc_id] = 1; - - l3_id = 0xff00 | proc_id; - bc->l3_id = l3_id; - cb_log(3, stack->port, " --> new_l3id %x\n", l3_id); - } else { - if (stack->ptp || bc->te_choose_channel) { - /* we know exactly which channels are in use */ - if (find_free_chan_in_stack(stack, bc, bc->channel_preselected ? bc->channel : 0, bc->dec) < 0) { - return -1; - } - cb_log(2, stack->port, " --> found channel: %d\n", bc->channel); - } else { - /* other phones could have made a call also on this port (ptmp) */ - bc->channel = 0xff; - } - - /* if we are in te-mode, we need to create a process first */ - if (++new_te_id > 0xffff) { - new_te_id = 0x0001; - } - - l3_id = (entity << 16) | new_te_id; - bc->l3_id = l3_id; - cb_log(3, stack->port, "--> new_l3id %x\n", l3_id); - - /* send message */ - ncr.prim = CC_NEW_CR | REQUEST; - ncr.addr = (stack->upper_id | FLG_MSG_DOWN); - ncr.dinfo = l3_id; - ncr.len = 0; - mISDN_write(midev, &ncr, mISDN_HEADER_LEN + ncr.len, TIMEOUT_1SEC); - } - - return l3_id; -} - - -void misdn_lib_setup_bc(struct misdn_bchannel *bc) -{ - clean_up_bc(bc); - setup_bc(bc); -} - - -int setup_bc(struct misdn_bchannel *bc) -{ - unsigned char buff[1025]; - int midev; - int channel; - int b_stid; - int i; - mISDN_pid_t pid; - int ret; - - struct misdn_stack *stack=get_stack_by_bc(bc); - - if (!stack) { - cb_log(0, bc->port, "setup_bc: NO STACK FOUND!!\n"); - return -1; - } - - midev = stack->midev; - channel = bc->channel - 1 - (bc->channel > 16); - b_stid = stack->b_stids[channel >= 0 ? channel : 0]; - - switch (bc->bc_state) { - case BCHAN_CLEANED: - break; - default: - cb_log(4, stack->port, "$$$ bc already setup stid :%x (state:%s)\n", b_stid, bc_state2str(bc->bc_state) ); - return -1; - } - - cb_log(5, stack->port, "$$$ Setting up bc with stid :%x\n", b_stid); - - /*check if the b_stid is already initialized*/ - for (i=0; i <= stack->b_num; i++) { - if (stack->bc[i].b_stid == b_stid) { - cb_log(0, bc->port, "setup_bc: b_stid:%x already in use !!!\n", b_stid); - return -1; - } - } - - if (b_stid <= 0) { - cb_log(0, stack->port," -- Stid <=0 at the moment in channel:%d\n",channel); - - bc_state_change(bc,BCHAN_ERROR); - return 1; - } - - bc->b_stid = b_stid; - - { - layer_info_t li; - memset(&li, 0, sizeof(li)); - - li.object_id = -1; - li.extentions = 0; - - li.st = bc->b_stid; /* given idx */ - - -#define MISDN_DSP -#ifndef MISDN_DSP - bc->nodsp=1; -#endif - if ( bc->hdlc || bc->nodsp) { - cb_log(4, stack->port,"setup_bc: without dsp\n"); - { - int l = sizeof(li.name); - strncpy(li.name, "B L3", l); - li.name[l-1] = 0; - } - li.pid.layermask = ISDN_LAYER((3)); - li.pid.protocol[3] = ISDN_PID_L3_B_USER; - - bc->layer=3; - } else { - cb_log(4, stack->port,"setup_bc: with dsp\n"); - { - int l = sizeof(li.name); - strncpy(li.name, "B L4", l); - li.name[l-1] = 0; - } - li.pid.layermask = ISDN_LAYER((4)); - li.pid.protocol[4] = ISDN_PID_L4_B_USER; - - bc->layer=4; - } - - ret = mISDN_new_layer(midev, &li); - if (ret ) { - cb_log(0, stack->port,"New Layer Err: %d %s\n",ret,strerror(errno)); - - bc_state_change(bc,BCHAN_ERROR); - return(-EINVAL); - } - - bc->layer_id = li.id; - } - - memset(&pid, 0, sizeof(pid)); - - - - cb_log(4, stack->port," --> Channel is %d\n", bc->channel); - - if (bc->nodsp) { - cb_log(2, stack->port," --> TRANSPARENT Mode (no DSP, no HDLC)\n"); - pid.protocol[1] = ISDN_PID_L1_B_64TRANS; - pid.protocol[2] = ISDN_PID_L2_B_TRANS; - pid.protocol[3] = ISDN_PID_L3_B_USER; - pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)); - - } else if ( bc->hdlc ) { - cb_log(2, stack->port," --> HDLC Mode\n"); - pid.protocol[1] = ISDN_PID_L1_B_64HDLC ; - pid.protocol[2] = ISDN_PID_L2_B_TRANS ; - pid.protocol[3] = ISDN_PID_L3_B_USER; - pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)) ; - } else { - cb_log(2, stack->port," --> TRANSPARENT Mode\n"); - pid.protocol[1] = ISDN_PID_L1_B_64TRANS; - pid.protocol[2] = ISDN_PID_L2_B_TRANS; - pid.protocol[3] = ISDN_PID_L3_B_DSP; - pid.protocol[4] = ISDN_PID_L4_B_USER; - pid.layermask = ISDN_LAYER((1)) | ISDN_LAYER((2)) | ISDN_LAYER((3)) | ISDN_LAYER((4)); - - } - - ret = mISDN_set_stack(midev, bc->b_stid, &pid); - - if (ret){ - cb_log(0, stack->port,"$$$ Set Stack Err: %d %s\n",ret,strerror(errno)); - - mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - - bc_state_change(bc,BCHAN_ERROR); - cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data); - return(-EINVAL); - } - - ret = mISDN_get_setstack_ind(midev, bc->layer_id); - - if (ret) { - cb_log(0, stack->port,"$$$ Set StackIND Err: %d %s\n",ret,strerror(errno)); - mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - - bc_state_change(bc,BCHAN_ERROR); - cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data); - return(-EINVAL); - } - - ret = mISDN_get_layerid(midev, bc->b_stid, bc->layer) ; - - bc->addr = ret>0? ret : 0; - - if (!bc->addr) { - cb_log(0, stack->port,"$$$ Get Layerid Err: %d %s\n",ret,strerror(errno)); - mISDN_write_frame(midev, buff, bc->layer_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - - bc_state_change(bc,BCHAN_ERROR); - cb_event(EVENT_BCHAN_ERROR, bc, glob_mgr->user_data); - return (-EINVAL); - } - - manager_bchannel_activate(bc); - - bc_state_change(bc,BCHAN_ACTIVATED); - - return 0; -} - - - -/** IFACE **/ -static int init_bc(struct misdn_stack *stack, struct misdn_bchannel *bc, int midev, int port, int bidx, char *msn, int firsttime) -{ - unsigned char buff[1025] = ""; - iframe_t *frm = (iframe_t *)buff; - int ret; - - if (!bc) return -1; - - cb_log(8, port, "Init.BC %d.\n",bidx); - - memset(bc, 0,sizeof(struct misdn_bchannel)); - - bc->send_lock=malloc(sizeof(struct send_lock)); - if (!bc->send_lock) { - return -1; - } - pthread_mutex_init(&bc->send_lock->lock, NULL); - - if (msn) { - int l = sizeof(bc->msn); - strncpy(bc->msn,msn, l); - bc->msn[l-1] = 0; - } - - - empty_bc(bc); - bc_state_change(bc, BCHAN_CLEANED); - - bc->port=stack->port; - bc->nt=stack->nt?1:0; - bc->pri=stack->pri; - - { - ibuffer_t* ibuf= init_ibuffer(MISDN_IBUF_SIZE); - - if (!ibuf) return -1; - - clear_ibuffer( ibuf); - - ibuf->rsem=malloc(sizeof(sem_t)); - if (!ibuf->rsem) { - return -1; - } - - bc->astbuf=ibuf; - - if (sem_init(ibuf->rsem,1,0)<0) - sem_init(ibuf->rsem,0,0); - - } - - { - stack_info_t *stinf; - ret = mISDN_get_stack_info(midev, stack->port, buff, sizeof(buff)); - if (ret < 0) { - cb_log(0, port, "%s: Cannot get stack info for this port. (ret=%d)\n", __FUNCTION__, ret); - return -1; - } - - stinf = (stack_info_t *)&frm->data.p; - - cb_log(8, port, " --> Child %x\n",stinf->child[bidx]); - } - - return 0; -} - - - -static struct misdn_stack *stack_init(int midev, int port, int ptp) -{ - int ret; - unsigned char buff[1025]; - iframe_t *frm = (iframe_t *)buff; - stack_info_t *stinf; - int i; - layer_info_t li; - - struct misdn_stack *stack = malloc(sizeof(struct misdn_stack)); - if (!stack ) return NULL; - - - cb_log(8, port, "Init. Stack.\n"); - - memset(stack,0,sizeof(struct misdn_stack)); - - for (i=0; i<MAX_BCHANS + 1; i++ ) stack->channels[i]=0; - - stack->port=port; - stack->midev=midev; - stack->ptp=ptp; - - stack->holding=NULL; - stack->pri=0; - - msg_queue_init(&stack->downqueue); - msg_queue_init(&stack->upqueue); - - /* query port's requirements */ - ret = mISDN_get_stack_info(midev, port, buff, sizeof(buff)); - if (ret < 0) { - cb_log(0, port, "%s: Cannot get stack info for this port. (ret=%d)\n", __FUNCTION__, ret); - return(NULL); - } - - stinf = (stack_info_t *)&frm->data.p; - - stack->d_stid = stinf->id; - stack->b_num = stinf->childcnt; - - for (i=0; i<=stinf->childcnt; i++) - stack->b_stids[i] = stinf->child[i]; - - switch(stinf->pid.protocol[0] & ~ISDN_PID_FEATURE_MASK) { - case ISDN_PID_L0_TE_S0: - stack->nt=0; - break; - case ISDN_PID_L0_NT_S0: - cb_log(8, port, "NT Stack\n"); - - stack->nt=1; - break; - case ISDN_PID_L0_TE_E1: - cb_log(8, port, "TE S2M Stack\n"); - stack->nt=0; - stack->pri=1; - break; - case ISDN_PID_L0_NT_E1: - cb_log(8, port, "TE S2M Stack\n"); - stack->nt=1; - stack->pri=1; - - break; - default: - cb_log(0, port, "this is a unknown port type 0x%08x\n", stinf->pid.protocol[0]); - - } - - if (!stack->nt) { - if (stinf->pid.protocol[2] & ISDN_PID_L2_DF_PTP ) { - stack->ptp = 1; - } else { - stack->ptp = 0; - } - } - - { - int ret; - int nt=stack->nt; - - cb_log(8, port, "Init. Stack.\n"); - - memset(&li, 0, sizeof(li)); - { - int l = sizeof(li.name); - strncpy(li.name,nt?"net l2":"user l4", l); - li.name[l-1] = 0; - } - li.object_id = -1; - li.extentions = 0; - li.pid.protocol[nt?2:4] = nt?ISDN_PID_L2_LAPD_NET:ISDN_PID_L4_CAPI20; - li.pid.layermask = ISDN_LAYER((nt?2:4)); - li.st = stack->d_stid; - - - ret = mISDN_new_layer(midev, &li); - if (ret) { - cb_log(0, port, "%s: Cannot add layer %d to this port.\n", __FUNCTION__, nt?2:4); - return(NULL); - } - - - stack->upper_id = li.id; - ret = mISDN_register_layer(midev, stack->d_stid, stack->upper_id); - if (ret) - { - cb_log(0,port,"Cannot register layer %d of this port.\n", nt?2:4); - return(NULL); - } - - stack->lower_id = mISDN_get_layerid(midev, stack->d_stid, nt?1:3); - if (stack->lower_id < 0) { - cb_log(0, port, "%s: Cannot get layer(%d) id of this port.\n", __FUNCTION__, nt?1:3); - return(NULL); - } - - stack->upper_id = mISDN_get_layerid(midev, stack->d_stid, nt?2:4); - if (stack->upper_id < 0) { - cb_log(0, port, "%s: Cannot get layer(%d) id of this port.\n", __FUNCTION__, 2); - return(NULL); - } - - cb_log(8, port, "NT Stacks upper_id %x\n",stack->upper_id); - - - /* create nst (nt-mode only) */ - if (nt) { - - memset(&stack->nst, 0, sizeof(net_stack_t)); - memset(&stack->mgr, 0, sizeof(manager_t)); - - stack->mgr.nst = &stack->nst; - stack->nst.manager = &stack->mgr; - - stack->nst.l3_manager = handle_event_nt; - stack->nst.device = midev; - stack->nst.cardnr = port; - stack->nst.d_stid = stack->d_stid; - - stack->nst.feature = FEATURE_NET_HOLD; - if (stack->ptp) - stack->nst.feature |= FEATURE_NET_PTP; - if (stack->pri) - stack->nst.feature |= FEATURE_NET_CRLEN2 | FEATURE_NET_EXTCID; - - stack->nst.l1_id = stack->lower_id; - stack->nst.l2_id = stack->upper_id; - - msg_queue_init(&stack->nst.down_queue); - - Isdnl2Init(&stack->nst); - Isdnl3Init(&stack->nst); - - } - - if (!stack->nt) { - /*assume L1 is up, we'll get DEACTIVATES soon, for non - * up L1s*/ - stack->l1link=0; - } - stack->l1link=0; - stack->l2link=0; -#if 0 - if (!stack->nt) { - misdn_lib_get_short_status(stack); - } else { - misdn_lib_get_l1_up(stack); - if (!stack->ptp) misdn_lib_get_l1_up(stack); - misdn_lib_get_l2_up(stack); - } -#endif - - misdn_lib_get_short_status(stack); - misdn_lib_get_l1_up(stack); - misdn_lib_get_l2_up(stack); - - } - - cb_log(8,0,"stack_init: port:%d lowerId:%x upperId:%x\n",stack->port,stack->lower_id, stack->upper_id); - - return stack; -} - - -static void stack_destroy(struct misdn_stack *stack) -{ - char buf[1024]; - if (!stack) return; - - if (stack->nt) { - cleanup_Isdnl2(&stack->nst); - cleanup_Isdnl3(&stack->nst); - } - - if (stack->lower_id) - mISDN_write_frame(stack->midev, buf, stack->lower_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - - if (stack->upper_id) - mISDN_write_frame(stack->midev, buf, stack->upper_id, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); -} - - -static struct misdn_stack * find_stack_by_addr(int addr) -{ - struct misdn_stack *stack; - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) { - if ( (stack->upper_id&STACK_ID_MASK) == (addr&STACK_ID_MASK)) return stack; - - } - - return NULL; -} - - -static struct misdn_stack * find_stack_by_port(int port) -{ - struct misdn_stack *stack; - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) - if (stack->port == port) return stack; - - return NULL; -} - -static struct misdn_stack * find_stack_by_mgr(manager_t* mgr_nt) -{ - struct misdn_stack *stack; - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) - if ( &stack->mgr == mgr_nt) return stack; - - return NULL; -} - -static struct misdn_bchannel *find_bc_by_masked_l3id(struct misdn_stack *stack, unsigned long l3id, unsigned long mask) -{ - int i; - for (i=0; i<=stack->b_num; i++) { - if ( (stack->bc[i].l3_id & mask) == (l3id & mask)) return &stack->bc[i] ; - } - return stack_holder_find(stack,l3id); -} - - -struct misdn_bchannel *find_bc_by_l3id(struct misdn_stack *stack, unsigned long l3id) -{ - int i; - for (i=0; i<=stack->b_num; i++) { - if (stack->bc[i].l3_id == l3id) return &stack->bc[i] ; - } - return stack_holder_find(stack,l3id); -} - -static struct misdn_bchannel *find_bc_holded(struct misdn_stack *stack) -{ - int i; - for (i=0; i<=stack->b_num; i++) { - if (stack->bc[i].holded ) return &stack->bc[i] ; - } - return NULL; -} - - -static struct misdn_bchannel *find_bc_by_addr(unsigned long addr) -{ - struct misdn_stack* stack; - int i; - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) { - for (i=0; i<=stack->b_num; i++) { - if ( (stack->bc[i].addr&STACK_ID_MASK)==(addr&STACK_ID_MASK) || stack->bc[i].layer_id== addr ) { - return &stack->bc[i]; - } - } - } - - return NULL; -} - -struct misdn_bchannel *find_bc_by_confid(unsigned long confid) -{ - struct misdn_stack* stack; - int i; - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) { - for (i=0; i<=stack->b_num; i++) { - if ( stack->bc[i].conf_id==confid ) { - return &stack->bc[i]; - } - } - } - return NULL; -} - - -static struct misdn_bchannel *find_bc_by_channel(int port, int channel) -{ - struct misdn_stack* stack=find_stack_by_port(port); - int i; - - if (!stack) return NULL; - - for (i=0; i<=stack->b_num; i++) { - if ( stack->bc[i].channel== channel ) { - return &stack->bc[i]; - } - } - - return NULL; -} - - - - - -static int handle_event ( struct misdn_bchannel *bc, enum event_e event, iframe_t *frm) -{ - struct misdn_stack *stack=get_stack_by_bc(bc); - - if (!stack->nt) { - - switch (event) { - - case EVENT_CONNECT_ACKNOWLEDGE: - setup_bc(bc); - - if ( *bc->crypt_key ) { - cb_log(4, stack->port, "ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s\n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); - manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) ); - } - - if (misdn_cap_is_speech(bc->capability)) { - if ( !bc->nodsp) manager_ph_control(bc, DTMF_TONE_START, 0); - manager_ec_enable(bc); - - if ( bc->txgain != 0 ) { - cb_log(4, stack->port, "--> Changing txgain to %d\n", bc->txgain); - manager_ph_control(bc, VOL_CHANGE_TX, bc->txgain); - } - if ( bc->rxgain != 0 ) { - cb_log(4, stack->port, "--> Changing rxgain to %d\n", bc->rxgain); - manager_ph_control(bc, VOL_CHANGE_RX, bc->rxgain); - } - } - - break; - case EVENT_CONNECT: - - if ( *bc->crypt_key ) { - cb_log(4, stack->port, "ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s\n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); - manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) ); - } - case EVENT_ALERTING: - case EVENT_PROGRESS: - case EVENT_PROCEEDING: - case EVENT_SETUP_ACKNOWLEDGE: - case EVENT_SETUP: - { - if (bc->channel == 0xff || bc->channel<=0) - bc->channel=0; - - if (find_free_chan_in_stack(stack, bc, bc->channel, 0)<0){ - if (!stack->pri && !stack->ptp) { - bc->cw=1; - break; - } - - if (!bc->channel) - cb_log(0, stack->port, "Any Channel Requested, but we have no more!!\n"); - else - cb_log(0, stack->port, "Requested Channel Already in Use releasing this call with cause 34!!!!\n"); - - /* when the channel is already in use, we can't - * simply clear it, we need to make sure that - * it will still be marked as in_use in the - * available channels list.*/ - bc->channel=0; - - misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); - return -1; - } - } - - setup_bc(bc); - break; - - case EVENT_RELEASE_COMPLETE: - case EVENT_RELEASE: - break; - default: - break; - } - } else { /** NT MODE **/ - - } - return 0; -} - -static int handle_cr ( struct misdn_stack *stack, iframe_t *frm) -{ - struct misdn_bchannel *bc; - - if (!stack) return -1; - - switch (frm->prim) { - case CC_NEW_CR|INDICATION: - cb_log(7, stack->port, " --> lib: NEW_CR Ind with l3id:%x on this port.\n",frm->dinfo); - - bc = misdn_lib_get_free_bc(stack->port, 0, 1, 0); - if (!bc) { - cb_log(0, stack->port, " --> !! lib: No free channel!\n"); - return -1; - } - - cb_log(7, stack->port, " --> new_process: New L3Id: %x\n",frm->dinfo); - bc->l3_id=frm->dinfo; - return 1; - case CC_NEW_CR|CONFIRM: - return 1; - case CC_NEW_CR|REQUEST: - return 1; - case CC_RELEASE_CR|REQUEST: - return 1; - case CC_RELEASE_CR|CONFIRM: - break; - case CC_RELEASE_CR|INDICATION: - cb_log(4, stack->port, " --> lib: RELEASE_CR Ind with l3id:%x\n",frm->dinfo); - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, frm->dinfo); - struct misdn_bchannel dummybc; - - if (!bc) { - cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", frm->dinfo); - misdn_make_dummy(&dummybc, stack->port, frm->dinfo, stack->nt, 0); - - bc=&dummybc; - } - - if (bc) { - int channel = bc->channel; - cb_log(4, stack->port, " --> lib: CLEANING UP l3id: %x\n",frm->dinfo); - - /*bc->pid = 0;*/ - bc->need_disconnect=0; - bc->need_release=0; - bc->need_release_complete=0; - - cb_event(EVENT_CLEANUP, bc, glob_mgr->user_data); - - empty_bc(bc); - clean_up_bc(bc); - - if (channel>0) - empty_chan_in_stack(stack,channel); - bc->in_use=0; - - dump_chan_list(stack); - - if (bc->stack_holder) { - cb_log(4,stack->port, "REMOVING Holder\n"); - stack_holder_remove( stack, bc); - free(bc); - } - } - else { - if (stack->nt) - cb_log(4, stack->port, "BC with dinfo: %x not found.. (prim was %x and addr %x)\n",frm->dinfo, frm->prim, frm->addr); - } - - return 1; - } - break; - } - - return 0; -} - - -/* Empties bc if it's reserved (no SETUP out yet) */ -void misdn_lib_release(struct misdn_bchannel *bc) -{ - struct misdn_stack *stack=get_stack_by_bc(bc); - - if (!stack) { - cb_log(1,0,"misdn_release: No Stack found\n"); - return; - } - - if (bc->channel>0) - empty_chan_in_stack(stack,bc->channel); - - empty_bc(bc); - clean_up_bc(bc); - bc->in_use=0; -} - - - - -int misdn_lib_get_port_up (int port) -{ /* Pull Up L1 */ - struct misdn_stack *stack; - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) { - - if (stack->port == port) { - - if (!stack->l1link) - misdn_lib_get_l1_up(stack); - if (!stack->l2link) - misdn_lib_get_l2_up(stack); - - return 0; - } - } - return 0; -} - - -int misdn_lib_get_port_down (int port) -{ /* Pull Down L1 */ - struct misdn_stack *stack; - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) { - if (stack->port == port) { - if (stack->l2link) - misdn_lib_get_l2_down(stack); - misdn_lib_get_l1_down(stack); - return 0; - } - } - return 0; -} - -int misdn_lib_port_up(int port, int check) -{ - struct misdn_stack *stack; - - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) { - - if (stack->port == port) { - - if (stack->blocked) { - cb_log(0,port, "Port Blocked:%d L2:%d L1:%d\n", stack->blocked, stack->l2link, stack->l1link); - return -1; - } - - if (stack->ptp ) { - - if (stack->l1link && stack->l2link) { - return 1; - } else { - cb_log(1,port, "Port Down L2:%d L1:%d\n", - stack->l2link, stack->l1link); - return 0; - } - } else { - if ( !check || stack->l1link ) - return 1; - else { - cb_log(1,port, "Port down PMP\n"); - return 0; - } - } - } - } - - return -1; -} - - -int release_cr(struct misdn_stack *stack, mISDNuser_head_t *hh) -{ - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - struct misdn_bchannel dummybc; - iframe_t frm; /* fake te frm to remove callref from global callreflist */ - frm.dinfo = hh->dinfo; - - frm.addr=stack->upper_id | FLG_MSG_DOWN; - - frm.prim = CC_RELEASE_CR|INDICATION; - cb_log(4, stack->port, " --> CC_RELEASE_CR: Faking Release_cr for %x l3id:%x\n",frm.addr, frm.dinfo); - /** removing procid **/ - if (!bc) { - cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", hh->dinfo); - misdn_make_dummy(&dummybc, stack->port, hh->dinfo, stack->nt, 0); - bc=&dummybc; - } - - if (bc) { - if ( (bc->l3_id & 0xff00) == 0xff00) { - cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", bc->l3_id&0xff); - stack->procids[bc->l3_id&0xff] = 0 ; - } - } - else cb_log(0, stack->port, "Couldn't find BC so I couldn't remove the Process!!!! this is a bad port.\n"); - - if (handle_cr(stack, &frm)<0) { - } - - return 0 ; -} - -int -handle_event_nt(void *dat, void *arg) -{ - manager_t *mgr = (manager_t *)dat; - msg_t *msg = (msg_t *)arg; - mISDNuser_head_t *hh; - int reject=0; - - struct misdn_stack *stack=find_stack_by_mgr(mgr); - int port; - - if (!msg || !mgr) - return(-EINVAL); - - hh=(mISDNuser_head_t*)msg->data; - port=stack->port; - - cb_log(5, stack->port, " --> lib: prim %x dinfo %x\n",hh->prim, hh->dinfo); - { - switch(hh->prim){ - case CC_RETRIEVE|INDICATION: - { - struct misdn_bchannel *bc; - struct misdn_bchannel *hold_bc; - - iframe_t frm; /* fake te frm to add callref to global callreflist */ - frm.dinfo = hh->dinfo; - - frm.addr=stack->upper_id | FLG_MSG_DOWN; - - frm.prim = CC_NEW_CR|INDICATION; - - if (handle_cr( stack, &frm)< 0) { - msg_t *dmsg; - cb_log(4, stack->port, "Patch from MEIDANIS:Sending RELEASE_COMPLETE %x (No free Chan for you..)\n", hh->dinfo); - dmsg = create_l3msg(CC_RELEASE_COMPLETE | REQUEST,MT_RELEASE_COMPLETE, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1); - stack->nst.manager_l3(&stack->nst, dmsg); - free_msg(msg); - return 0; - } - - bc = find_bc_by_l3id(stack, hh->dinfo); - hold_bc = stack_holder_find(stack, bc->l3_id); - cb_log(4, stack->port, "bc_l3id:%x holded_bc_l3id:%x\n",bc->l3_id, hold_bc->l3_id); - - if (hold_bc) { - cb_log(4, stack->port, "REMOVING Holder\n"); - - /*swap the backup to our new channel back*/ - stack_holder_remove(stack, hold_bc); - memcpy(bc, hold_bc, sizeof(struct misdn_bchannel)); - free(hold_bc); - - bc->holded=0; - bc->b_stid=0; - } - - } - - break; - - case CC_SETUP|CONFIRM: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - int l3id = *((int *)(((u_char *)msg->data)+ mISDNUSER_HEAD_SIZE)); - cb_log(4, stack->port, " --> lib: Event_ind:SETUP CONFIRM [NT] : new L3ID is %x\n",l3id ); - - if (!bc) { cb_log(4, stack->port, "Bc Not found (after SETUP CONFIRM)\n"); return 0; } - cb_log (2,bc->port,"I IND :CC_SETUP|CONFIRM: old l3id:%x new l3id:%x\n", bc->l3_id, l3id); - bc->l3_id=l3id; - cb_event(EVENT_NEW_L3ID, bc, glob_mgr->user_data); - } - free_msg(msg); - return 0; - - case CC_SETUP|INDICATION: - { - struct misdn_bchannel* bc=misdn_lib_get_free_bc(stack->port, 0, 1, 0); - if (!bc) - ERR_NO_CHANNEL: - { - msg_t *dmsg; - cb_log(4, stack->port, "Patch from MEIDANIS:Sending RELEASE_COMPLETE %x (No free Chan for you..)\n", hh->dinfo); - dmsg = create_l3msg(CC_RELEASE_COMPLETE | REQUEST,MT_RELEASE_COMPLETE, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1); - stack->nst.manager_l3(&stack->nst, dmsg); - free_msg(msg); - return 0; - } - - cb_log(4, stack->port, " --> new_process: New L3Id: %x\n",hh->dinfo); - bc->l3_id=hh->dinfo; - } - break; - - case CC_CONNECT_ACKNOWLEDGE|INDICATION: - break; - - case CC_ALERTING|INDICATION: - case CC_PROCEEDING|INDICATION: - case CC_SETUP_ACKNOWLEDGE|INDICATION: - if(!stack->ptp) break; - case CC_CONNECT|INDICATION: - break; - case CC_DISCONNECT|INDICATION: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - if (!bc) { - bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); - if (bc) { - int myprocid=bc->l3_id&0x0000ffff; - hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; - cb_log(3,stack->port,"Reject dinfo: %x cause:%d\n",hh->dinfo,bc->cause); - reject=1; - } - } - } - break; - - case CC_FACILITY|INDICATION: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - if (!bc) { - bc=find_bc_by_masked_l3id(stack, hh->dinfo, 0xffff0000); - if (bc) { - int myprocid=bc->l3_id&0x0000ffff; - hh->dinfo=(hh->dinfo&0xffff0000)|myprocid; - cb_log(4,bc->port,"Repaired reject Bug, new dinfo: %x\n",hh->dinfo); - } - } - } - break; - - case CC_RELEASE_COMPLETE|INDICATION: - break; - - case CC_SUSPEND|INDICATION: - { - msg_t *dmsg; - cb_log(4, stack->port, " --> Got Suspend, sending Reject for now\n"); - dmsg = create_l3msg(CC_SUSPEND_REJECT | REQUEST,MT_SUSPEND_REJECT, hh->dinfo,sizeof(RELEASE_COMPLETE_t), 1); - stack->nst.manager_l3(&stack->nst, dmsg); - free_msg(msg); - return 0; - } - break; - case CC_RESUME|INDICATION: - break; - - case CC_RELEASE|CONFIRM: - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - - if (bc) { - cb_log(1, stack->port, "CC_RELEASE|CONFIRM (l3id:%x), sending RELEASE_COMPLETE\n", hh->dinfo); - misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); - } - } - break; - - case CC_RELEASE|INDICATION: - break; - - case CC_RELEASE_CR|INDICATION: - release_cr(stack, hh); - free_msg(msg); - return 0 ; - break; - - case CC_NEW_CR|INDICATION: - /* Got New CR for bchan, for now I handle this one in */ - /* connect_ack, Need to be changed */ - { - struct misdn_bchannel *bc=find_bc_by_l3id(stack, hh->dinfo); - int l3id = *((int *)(((u_char *)msg->data)+ mISDNUSER_HEAD_SIZE)); - if (!bc) { cb_log(0, stack->port, " --> In NEW_CR: didn't found bc ??\n"); return -1;}; - if (((l3id&0xff00)!=0xff00) && ((bc->l3_id&0xff00)==0xff00)) { - cb_log(4, stack->port, " --> Removing Process Id:%x on this port.\n", 0xff&bc->l3_id); - stack->procids[bc->l3_id&0xff] = 0 ; - } - cb_log(4, stack->port, "lib: Event_ind:CC_NEW_CR : very new L3ID is %x\n",l3id ); - - bc->l3_id =l3id; - cb_event(EVENT_NEW_L3ID, bc, glob_mgr->user_data); - - free_msg(msg); - return 0; - } - - case DL_ESTABLISH | INDICATION: - case DL_ESTABLISH | CONFIRM: - { - cb_log(3, stack->port, "%% GOT L2 Activate Info.\n"); - - if (stack->ptp && stack->l2link) { - cb_log(0, stack->port, "%% GOT L2 Activate Info. but we're activated already.. this l2 is faulty, blocking port\n"); - cb_event(EVENT_PORT_ALARM, &stack->bc[0], glob_mgr->user_data); - } - - if (stack->ptp && !stack->restart_sent) { - /* make sure we restart the interface of the - * other side */ - stack->restart_sent=1; - misdn_lib_send_restart(stack->port, -1); - - } - - /* when we get the L2 UP, the L1 is UP definitely too*/ - stack->l1link = 1; - stack->l2link = 1; - stack->l2upcnt=0; - - free_msg(msg); - return 0; - } - break; - - - case DL_RELEASE | INDICATION: - case DL_RELEASE | CONFIRM: - { - if (stack->ptp) { - cb_log(3 , stack->port, "%% GOT L2 DeActivate Info.\n"); - - if (stack->l2upcnt>3) { - cb_log(0 , stack->port, "!!! Could not Get the L2 up after 3 Attempts!!!\n"); - } else { -#if 0 - if (stack->nt) misdn_lib_reinit_nt_stack(stack->port); -#endif - if (stack->l1link) { - misdn_lib_get_l2_up(stack); - stack->l2upcnt++; - } - } - - } else - cb_log(3, stack->port, "%% GOT L2 DeActivate Info.\n"); - - stack->l2link = 0; - free_msg(msg); - return 0; - } - break; - } - } - - { - /* Parse Events and fire_up to App. */ - struct misdn_bchannel *bc; - struct misdn_bchannel dummybc; - - enum event_e event = isdn_msg_get_event(msgs_g, msg, 1); - - bc=find_bc_by_l3id(stack, hh->dinfo); - - if (!bc) { - cb_log(4, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x).\n", hh->dinfo); - misdn_make_dummy(&dummybc, stack->port, hh->dinfo, stack->nt, 0); - bc=&dummybc; - } - if (bc ) { - isdn_msg_parse_event(msgs_g,msg,bc, 1); - - switch (event) { - case EVENT_SETUP: - if (bc->channel<=0 || bc->channel==0xff) - bc->channel=0; - - if (find_free_chan_in_stack(stack,bc, bc->channel,0)<0) - goto ERR_NO_CHANNEL; - break; - case EVENT_RELEASE: - case EVENT_RELEASE_COMPLETE: - { - int channel=bc->channel; - int tmpcause=bc->cause; - empty_bc(bc); - bc->cause=tmpcause; - clean_up_bc(bc); - - if (channel>0) - empty_chan_in_stack(stack,channel); - bc->in_use=0; - } - break; - - default: - break; - } - - if(!isdn_get_info(msgs_g,event,1)) { - cb_log(4, stack->port, "Unknown Event Ind: prim %x dinfo %x\n",hh->prim, hh->dinfo); - } else { - if (reject) { - switch(bc->cause){ - case AST_CAUSE_USER_BUSY: - cb_log(1, stack->port, "Siemens Busy reject..\n"); - - break; - default: - break; - } - } - cb_event(event, bc, glob_mgr->user_data); - } - } else { - cb_log(4, stack->port, "No BC found with l3id: prim %x dinfo %x\n",hh->prim, hh->dinfo); - } - - free_msg(msg); - } - - - return 0; -} - - -static int handle_timers(msg_t* msg) -{ - iframe_t *frm= (iframe_t*)msg->data; - struct misdn_stack *stack; - - /* Timer Stuff */ - switch (frm->prim) { - case MGR_INITTIMER | CONFIRM: - case MGR_ADDTIMER | CONFIRM: - case MGR_DELTIMER | CONFIRM: - case MGR_REMOVETIMER | CONFIRM: - free_msg(msg); - return(1); - } - - - - if (frm->prim==(MGR_TIMER | INDICATION) ) { - for (stack = glob_mgr->stack_list; - stack; - stack = stack->next) { - itimer_t *it; - - if (!stack->nt) continue; - - it = stack->nst.tlist; - /* find timer */ - for(it=stack->nst.tlist; - it; - it=it->next) { - if (it->id == (int)frm->addr) - break; - } - if (it) { - int ret; - ret = mISDN_write_frame(stack->midev, msg->data, frm->addr, - MGR_TIMER | RESPONSE, 0, 0, NULL, TIMEOUT_1SEC); - test_and_clear_bit(FLG_TIMER_RUNING, (long unsigned int *)&it->Flags); - ret = it->function(it->data); - free_msg(msg); - return 1; - } - } - - cb_log(0, 0, "Timer Msg without Timer ??\n"); - free_msg(msg); - return 1; - } - - return 0; -} - - - -void misdn_lib_tone_generator_start(struct misdn_bchannel *bc) -{ - bc->generate_tone=1; -} - -void misdn_lib_tone_generator_stop(struct misdn_bchannel *bc) -{ - bc->generate_tone=0; -} - - -static int do_tone(struct misdn_bchannel *bc, int len) -{ - bc->tone_cnt=len; - - if (bc->generate_tone) { - cb_event(EVENT_TONE_GENERATE, bc, glob_mgr->user_data); - - if ( !bc->nojitter ) { - misdn_tx_jitter(bc,len); - } - - return 1; - } - - return 0; -} - - -#ifdef MISDN_SAVE_DATA -static void misdn_save_data(int id, char *p1, int l1, char *p2, int l2) -{ - char n1[32],n2[32]; - FILE *rx, *tx; - - sprintf(n1,"/tmp/misdn-rx-%d.raw",id); - sprintf(n2,"/tmp/misdn-tx-%d.raw",id); - - rx = fopen(n1,"a+"); - tx = fopen(n2,"a+"); - - if (!rx || !tx) { - cb_log(0,0,"Couldn't open files: %s\n",strerror(errno)); - return ; - } - - fwrite(p1,1,l1,rx); - fwrite(p2,1,l2,tx); - - fclose(rx); - fclose(tx); - -} -#endif - -void misdn_tx_jitter(struct misdn_bchannel *bc, int len) -{ - char buf[4096 + mISDN_HEADER_LEN]; - char *data=&buf[mISDN_HEADER_LEN]; - iframe_t *txfrm= (iframe_t*)buf; - int jlen, r; - - jlen=cb_jb_empty(bc,data,len); - - if (jlen) { -#ifdef MISDN_SAVE_DATA - misdn_save_data((bc->port*100+bc->channel), data, jlen, bc->bframe, bc->bframe_len); -#endif - flip_buf_bits( data, jlen); - - if (jlen < len) { - cb_log(7,bc->port,"Jitterbuffer Underrun.\n"); - } - - txfrm->prim = DL_DATA|REQUEST; - - txfrm->dinfo = 0; - - txfrm->addr = bc->addr|FLG_MSG_DOWN; /* | IF_DOWN; */ - - txfrm->len =jlen; - cb_log(9, bc->port, "Transmitting %d samples 2 misdn\n", txfrm->len); - - r=mISDN_write( glob_mgr->midev, buf, txfrm->len + mISDN_HEADER_LEN, 8000 ); - } else { -#define MISDN_GEN_SILENCE -#ifdef MISDN_GEN_SILENCE - int cnt=len/TONE_SILENCE_SIZE; - int rest=len%TONE_SILENCE_SIZE; - int i; - - for (i=0; i<cnt; i++) { - memcpy(data, tone_silence_flip, TONE_SILENCE_SIZE ); - data +=TONE_SILENCE_SIZE; - } - - if (rest) { - memcpy(data, tone_silence_flip, rest); - } - - txfrm->prim = DL_DATA|REQUEST; - - txfrm->dinfo = 0; - - txfrm->addr = bc->addr|FLG_MSG_DOWN; /* | IF_DOWN; */ - - txfrm->len =len; - cb_log(9, bc->port, "Transmitting %d samples 2 misdn\n", txfrm->len); - - r=mISDN_write( glob_mgr->midev, buf, txfrm->len + mISDN_HEADER_LEN, 8000 ); -#endif - - } -} - -static int handle_bchan(msg_t *msg) -{ - iframe_t *frm= (iframe_t*)msg->data; - struct misdn_bchannel *bc=find_bc_by_addr(frm->addr); - struct misdn_stack *stack; - - if (!bc) { - cb_log(1,0,"handle_bchan: BC not found for prim:%x with addr:%x dinfo:%x\n", frm->prim, frm->addr, frm->dinfo); - return 0 ; - } - - stack = get_stack_by_bc(bc); - - if (!stack) { - cb_log(0, bc->port,"handle_bchan: STACK not found for prim:%x with addr:%x dinfo:%x\n", frm->prim, frm->addr, frm->dinfo); - return 0; - } - - switch (frm->prim) { - - case MGR_SETSTACK| CONFIRM: - cb_log(3, stack->port, "BCHAN: MGR_SETSTACK|CONFIRM pid:%d\n",bc->pid); - break; - - case MGR_SETSTACK| INDICATION: - cb_log(3, stack->port, "BCHAN: MGR_SETSTACK|IND pid:%d\n",bc->pid); - break; -#if 0 - AGAIN: - bc->addr = mISDN_get_layerid(stack->midev, bc->b_stid, bc->layer); - if (!bc->addr) { - - if (errno == EAGAIN) { - usleep(1000); - goto AGAIN; - } - - cb_log(0,stack->port,"$$$ Get Layer (%d) Id Error: %s\n",bc->layer,strerror(errno)); - - /* we kill the channel later, when we received some - data. */ - bc->addr= frm->addr; - } else if ( bc->addr < 0) { - cb_log(0, stack->port,"$$$ bc->addr <0 Error:%s\n",strerror(errno)); - bc->addr=0; - } - - cb_log(4, stack->port," --> Got Adr %x\n", bc->addr); - - free_msg(msg); - - - switch(bc->bc_state) { - case BCHAN_SETUP: - bc_state_change(bc,BCHAN_SETUPED); - break; - - case BCHAN_CLEAN_REQUEST: - default: - cb_log(0, stack->port," --> STATE WASN'T SETUP (but %s) in SETSTACK|IND pid:%d\n",bc_state2str(bc->bc_state), bc->pid); - clean_up_bc(bc); - } - return 1; -#endif - - case MGR_DELLAYER| INDICATION: - cb_log(3, stack->port, "BCHAN: MGR_DELLAYER|IND pid:%d\n",bc->pid); - break; - - case MGR_DELLAYER| CONFIRM: - cb_log(3, stack->port, "BCHAN: MGR_DELLAYER|CNF pid:%d\n",bc->pid); - - bc->pid=0; - bc->addr=0; - - free_msg(msg); - return 1; - - case PH_ACTIVATE | INDICATION: - case DL_ESTABLISH | INDICATION: - cb_log(3, stack->port, "BCHAN: ACT Ind pid:%d\n", bc->pid); - - free_msg(msg); - return 1; - - case PH_ACTIVATE | CONFIRM: - case DL_ESTABLISH | CONFIRM: - - cb_log(3, stack->port, "BCHAN: bchan ACT Confirm pid:%d\n",bc->pid); - free_msg(msg); - - return 1; - - case DL_ESTABLISH | REQUEST: - { - char buf[128]; - mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_TARGET | FLG_MSG_DOWN, DL_ESTABLISH | CONFIRM, 0,0, NULL, TIMEOUT_1SEC); - } - free_msg(msg); - return 1; - - case DL_RELEASE|REQUEST: - { - char buf[128]; - mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_TARGET | FLG_MSG_DOWN, DL_RELEASE| CONFIRM, 0,0, NULL, TIMEOUT_1SEC); - } - free_msg(msg); - return 1; - - case PH_DEACTIVATE | INDICATION: - case DL_RELEASE | INDICATION: - cb_log (3, stack->port, "BCHAN: DeACT Ind pid:%d\n",bc->pid); - - free_msg(msg); - return 1; - - case PH_DEACTIVATE | CONFIRM: - case DL_RELEASE | CONFIRM: - cb_log(3, stack->port, "BCHAN: DeACT Conf pid:%d\n",bc->pid); - - free_msg(msg); - return 1; - - case PH_CONTROL|INDICATION: - { - unsigned int *cont = (unsigned int *) &frm->data.p; - - cb_log(4, stack->port, "PH_CONTROL: channel:%d oad%d:%s dad%d:%s \n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); - - if ((*cont & ~DTMF_TONE_MASK) == DTMF_TONE_VAL) { - int dtmf = *cont & DTMF_TONE_MASK; - cb_log(4, stack->port, " --> DTMF TONE: %c\n",dtmf); - bc->dtmf=dtmf; - cb_event(EVENT_DTMF_TONE, bc, glob_mgr->user_data); - - free_msg(msg); - return 1; - } - if (*cont == BF_REJECT) { - cb_log(4, stack->port, " --> BF REJECT\n"); - free_msg(msg); - return 1; - } - if (*cont == BF_ACCEPT) { - cb_log(4, stack->port, " --> BF ACCEPT\n"); - free_msg(msg); - return 1; - } - } - break; - - case PH_DATA|REQUEST: - case DL_DATA|REQUEST: - cb_log(0, stack->port, "DL_DATA REQUEST \n"); - do_tone(bc, 64); - - free_msg(msg); - return 1; - - - case PH_DATA|INDICATION: - case DL_DATA|INDICATION: - { - bc->bframe = (void*)&frm->data.i; - bc->bframe_len = frm->len; - - /** Anyway flip the bufbits **/ - if ( misdn_cap_is_speech(bc->capability) ) - flip_buf_bits(bc->bframe, bc->bframe_len); - - - if (!bc->bframe_len) { - cb_log(2, stack->port, "DL_DATA INDICATION bc->addr:%x frm->addr:%x\n", bc->addr, frm->addr); - free_msg(msg); - return 1; - } - - if ( (bc->addr&STACK_ID_MASK) != (frm->addr&STACK_ID_MASK) ) { - cb_log(2, stack->port, "DL_DATA INDICATION bc->addr:%x frm->addr:%x\n", bc->addr, frm->addr); - free_msg(msg); - return 1; - } - -#if MISDN_DEBUG - cb_log(0, stack->port, "DL_DATA INDICATION Len %d\n", frm->len); - -#endif - - if ( (bc->bc_state == BCHAN_ACTIVATED) && frm->len > 0) { - int t; - -#ifdef MISDN_B_DEBUG - cb_log(0,bc->port,"do_tone START\n"); -#endif - t=do_tone(bc,frm->len); - -#ifdef MISDN_B_DEBUG - cb_log(0,bc->port,"do_tone STOP (%d)\n",t); -#endif - if ( !t ) { - int i; - - if ( misdn_cap_is_speech(bc->capability)) { - if ( !bc->nojitter ) { -#ifdef MISDN_B_DEBUG - cb_log(0,bc->port,"tx_jitter START\n"); -#endif - misdn_tx_jitter(bc,frm->len); -#ifdef MISDN_B_DEBUG - cb_log(0,bc->port,"tx_jitter STOP\n"); -#endif - } - } - -#ifdef MISDN_B_DEBUG - cb_log(0,bc->port,"EVENT_B_DATA START\n"); -#endif - - i = cb_event(EVENT_BCHAN_DATA, bc, glob_mgr->user_data); -#ifdef MISDN_B_DEBUG - cb_log(0,bc->port,"EVENT_B_DATA STOP\n"); -#endif - - if (i<0) { - cb_log(10,stack->port,"cb_event returned <0\n"); - /*clean_up_bc(bc);*/ - } - } - } - free_msg(msg); - return 1; - } - - - case PH_CONTROL | CONFIRM: - cb_log(4, stack->port, "PH_CONTROL|CNF bc->addr:%x\n", frm->addr); - free_msg(msg); - return 1; - - case PH_DATA | CONFIRM: - case DL_DATA|CONFIRM: -#if MISDN_DEBUG - - cb_log(0, stack->port, "Data confirmed\n"); - -#endif - free_msg(msg); - return 1; - case DL_DATA|RESPONSE: -#if MISDN_DEBUG - cb_log(0, stack->port, "Data response\n"); - -#endif - break; - } - - return 0; -} - - - -static int handle_frm_nt(msg_t *msg) -{ - iframe_t *frm= (iframe_t*)msg->data; - struct misdn_stack *stack; - int err=0; - - stack=find_stack_by_addr( frm->addr ); - - - - if (!stack || !stack->nt) { - return 0; - } - - - if ((err=stack->nst.l1_l2(&stack->nst,msg))) { - - if (nt_err_cnt > 0 ) { - if (nt_err_cnt < 100) { - nt_err_cnt++; - cb_log(0, stack->port, "NT Stack sends us error: %d \n", err); - } else if (nt_err_cnt < 105){ - cb_log(0, stack->port, "NT Stack sends us error: %d over 100 times, so I'll stop this message\n", err); - nt_err_cnt = - 1; - } - } - free_msg(msg); - return 1; - - } - - return 1; -} - - -static int handle_frm(msg_t *msg) -{ - iframe_t *frm = (iframe_t*) msg->data; - - struct misdn_stack *stack=find_stack_by_addr(frm->addr); - - if (!stack || stack->nt) { - return 0; - } - - cb_log(4,stack?stack->port:0,"handle_frm: frm->addr:%x frm->prim:%x\n",frm->addr,frm->prim); - - { - struct misdn_bchannel dummybc; - struct misdn_bchannel *bc; - int ret=handle_cr(stack, frm); - - if (ret<0) { - cb_log(3,stack?stack->port:0,"handle_frm: handle_cr <0 prim:%x addr:%x\n", frm->prim, frm->addr); - - - } - - if(ret) { - free_msg(msg); - return 1; - } - - bc=find_bc_by_l3id(stack, frm->dinfo); - - if (!bc && (frm->prim==(CC_RESTART|CONFIRM)) ) { - misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0); - bc=&dummybc; - } - - if (!bc && (frm->prim==(CC_SETUP|INDICATION)) ) { - misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0); - dummybc.port=stack->port; - dummybc.l3_id=frm->dinfo; - bc=&dummybc; - - misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); - - free_msg(msg); - return 1; - } - - -handle_frm_bc: - if (bc ) { - enum event_e event = isdn_msg_get_event(msgs_g, msg, 0); - enum event_response_e response=RESPONSE_OK; - int ret; - - isdn_msg_parse_event(msgs_g,msg,bc, 0); - - /** Preprocess some Events **/ - ret = handle_event(bc, event, frm); - if (ret<0) { - cb_log(0,stack->port,"couldn't handle event\n"); - free_msg(msg); - return 1; - } - /* shoot up event to App: */ - cb_log(5, stack->port, "lib Got Prim: Addr %x prim %x dinfo %x\n",frm->addr, frm->prim, frm->dinfo); - - if(!isdn_get_info(msgs_g,event,0)) - cb_log(0, stack->port, "Unknown Event Ind: Addr:%x prim %x dinfo %x\n",frm->addr, frm->prim, frm->dinfo); - else - response=cb_event(event, bc, glob_mgr->user_data); -#if 1 - if (event == EVENT_SETUP) { - switch (response) { - case RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE: - - cb_log(0, stack->port, "TOTALLY IGNORING SETUP\n"); - - break; - case RESPONSE_IGNORE_SETUP: - /* I think we should send CC_RELEASE_CR, but am not sure*/ - bc->out_cause = AST_CAUSE_NORMAL_CLEARING; - - case RESPONSE_RELEASE_SETUP: - misdn_lib_send_event(bc,EVENT_RELEASE_COMPLETE); - if (bc->channel>0) - empty_chan_in_stack(stack, bc->channel); - empty_bc(bc); - bc_state_change(bc,BCHAN_CLEANED); - bc->in_use=0; - - cb_log(0, stack->port, "GOT IGNORE SETUP\n"); - break; - case RESPONSE_OK: - cb_log(4, stack->port, "GOT SETUP OK\n"); - - - break; - default: - break; - } - } - - if (event == EVENT_RELEASE_COMPLETE) { - /* release bchannel only after we've announced the RELEASE_COMPLETE */ - int channel=bc->channel; - int tmpcause=bc->cause; - int tmp_out_cause=bc->out_cause; - empty_bc(bc); - bc->cause=tmpcause; - bc->out_cause=tmp_out_cause; - clean_up_bc(bc); - - if (tmpcause == AST_CAUSE_REQUESTED_CHAN_UNAVAIL) { - cb_log(0,stack->port,"**** Received CAUSE:%d, so not cleaning up channel %d\n", AST_CAUSE_REQUESTED_CHAN_UNAVAIL, channel); - cb_log(0,stack->port,"**** This channel is now no longer available,\nplease try to restart it with 'misdn send restart <port> <channel>'\n"); - set_chan_in_stack(stack, channel); - bc->channel=channel; - misdn_lib_send_restart(stack->port, channel); - } else { - if (channel>0) - empty_chan_in_stack(stack, channel); - } - bc->in_use=0; - } - - if (event == EVENT_RESTART) { - cb_log(0, stack->port, "**** Received RESTART_ACK channel:%d\n", bc->restart_channel); - empty_chan_in_stack(stack, bc->restart_channel); - } - - cb_log(5, stack->port, "Freeing Msg on prim:%x \n",frm->prim); - - - free_msg(msg); - return 1; -#endif - - } else { - struct misdn_bchannel dummybc; - cb_log(0, stack->port, " --> Didn't find BC so temporarily creating dummy BC (l3id:%x) on this port.\n", frm->dinfo); - memset (&dummybc,0,sizeof(dummybc)); - dummybc.port=stack->port; - dummybc.l3_id=frm->dinfo; - bc=&dummybc; - goto handle_frm_bc; - } - } - - cb_log(4, stack->port, "TE_FRM_HANDLER: Returning 0 on prim:%x \n",frm->prim); - return 0; -} - - -static int handle_l1(msg_t *msg) -{ - iframe_t *frm = (iframe_t*) msg->data; - struct misdn_stack *stack = find_stack_by_addr(frm->addr); - int i ; - - if (!stack) return 0 ; - - switch (frm->prim) { - case PH_ACTIVATE | CONFIRM: - case PH_ACTIVATE | INDICATION: - cb_log (3, stack->port, "L1: PH L1Link Up!\n"); - stack->l1link=1; - - if (stack->nt) { - - if (stack->nst.l1_l2(&stack->nst, msg)) - free_msg(msg); - - if (stack->ptp) - misdn_lib_get_l2_up(stack); - } else { - free_msg(msg); - } - - for (i=0;i<=stack->b_num; i++) { - if (stack->bc[i].evq != EVENT_NOTHING) { - cb_log(4, stack->port, "Firing Queued Event %s because L1 got up\n", isdn_get_info(msgs_g, stack->bc[i].evq, 0)); - misdn_lib_send_event(&stack->bc[i],stack->bc[i].evq); - stack->bc[i].evq=EVENT_NOTHING; - } - - } - return 1; - - case PH_ACTIVATE | REQUEST: - free_msg(msg); - cb_log(3,stack->port,"L1: PH_ACTIVATE|REQUEST \n"); - return 1; - - case PH_DEACTIVATE | REQUEST: - free_msg(msg); - cb_log(3,stack->port,"L1: PH_DEACTIVATE|REQUEST \n"); - return 1; - - case PH_DEACTIVATE | CONFIRM: - case PH_DEACTIVATE | INDICATION: - cb_log (3, stack->port, "L1: PH L1Link Down! \n"); - -#if 0 - for (i=0; i<=stack->b_num; i++) { - if (global_state == MISDN_INITIALIZED) { - cb_event(EVENT_CLEANUP, &stack->bc[i], glob_mgr->user_data); - } - } -#endif - - if (stack->nt) { - if (stack->nst.l1_l2(&stack->nst, msg)) - free_msg(msg); - } else { - free_msg(msg); - } - - stack->l1link=0; - stack->l2link=0; - return 1; - } - - return 0; -} - -static int handle_l2(msg_t *msg) -{ - iframe_t *frm = (iframe_t*) msg->data; - - struct misdn_stack *stack = find_stack_by_addr(frm->addr); - - if (!stack) { - return 0 ; - } - - switch(frm->prim) { - - case DL_ESTABLISH | REQUEST: - cb_log(1,stack->port,"DL_ESTABLISH|REQUEST \n"); - return 1; - case DL_RELEASE | REQUEST: - cb_log(1,stack->port,"DL_RELEASE|REQUEST \n"); - return 1; - - case DL_ESTABLISH | INDICATION: - case DL_ESTABLISH | CONFIRM: - { - cb_log (3, stack->port, "L2: L2Link Up! \n"); - if (stack->ptp && stack->l2link) { - cb_log (-1, stack->port, "L2: L2Link Up! but it's already UP.. must be faulty, blocking port\n"); - cb_event(EVENT_PORT_ALARM, &stack->bc[0], glob_mgr->user_data); - } - stack->l2link=1; - free_msg(msg); - return 1; - } - break; - - case DL_RELEASE | INDICATION: - case DL_RELEASE | CONFIRM: - { - cb_log (3, stack->port, "L2: L2Link Down! \n"); - stack->l2link=0; - - free_msg(msg); - return 1; - } - break; - } - return 0; -} - -static int handle_mgmt(msg_t *msg) -{ - iframe_t *frm = (iframe_t*) msg->data; - struct misdn_stack *stack; - - if ( (frm->addr == 0) && (frm->prim == (MGR_DELLAYER|CONFIRM)) ) { - cb_log(2, 0, "MGMT: DELLAYER|CONFIRM Addr: 0 !\n") ; - free_msg(msg); - return 1; - } - - stack = find_stack_by_addr(frm->addr); - - if (!stack) { - if (frm->prim == (MGR_DELLAYER|CONFIRM)) { - cb_log(2, 0, "MGMT: DELLAYER|CONFIRM Addr: %x !\n", - frm->addr) ; - free_msg(msg); - return 1; - } - - return 0; - } - - switch(frm->prim) { - case MGR_SHORTSTATUS | INDICATION: - case MGR_SHORTSTATUS | CONFIRM: - cb_log(5, 0, "MGMT: Short status dinfo %x\n",frm->dinfo); - - switch (frm->dinfo) { - case SSTATUS_L1_ACTIVATED: - cb_log(3, 0, "MGMT: SSTATUS: L1_ACTIVATED \n"); - stack->l1link=1; - - break; - case SSTATUS_L1_DEACTIVATED: - cb_log(3, 0, "MGMT: SSTATUS: L1_DEACTIVATED \n"); - stack->l1link=0; -#if 0 - clear_l3(stack); -#endif - break; - - case SSTATUS_L2_ESTABLISHED: - cb_log(3, stack->port, "MGMT: SSTATUS: L2_ESTABLISH \n"); - - /*when the L2 goes UP, L1 needs to be UP too*/ - stack->l1link=1; - stack->l2link=1; - break; - - case SSTATUS_L2_RELEASED: - cb_log(3, stack->port, "MGMT: SSTATUS: L2_RELEASED \n"); - stack->l2link=0; - break; - } - - free_msg(msg); - return 1; - - case MGR_SETSTACK | INDICATION: - cb_log(4, stack->port, "MGMT: SETSTACK|IND dinfo %x\n",frm->dinfo); - free_msg(msg); - return 1; - case MGR_DELLAYER | CONFIRM: - cb_log(4, stack->port, "MGMT: DELLAYER|CNF dinfo %x\n",frm->dinfo) ; - free_msg(msg); - return 1; - - } - - /* - if ( (frm->prim & 0x0f0000) == 0x0f0000) { - cb_log(5, 0, "$$$ MGMT FRAME: prim %x addr %x dinfo %x\n",frm->prim, frm->addr, frm->dinfo) ; - free_msg(msg); - return 1; - } */ - - return 0; -} - - -static msg_t *fetch_msg(int midev) -{ - msg_t *msg=alloc_msg(MAX_MSG_SIZE); - int r; - - if (!msg) { - cb_log(0, 0, "fetch_msg: alloc msg failed !!"); - return NULL; - } - - AGAIN: - r=mISDN_read(midev,msg->data,MAX_MSG_SIZE, TIMEOUT_10SEC); - msg->len=r; - - if (r==0) { - free_msg(msg); /* danger, cause usually freeing in main_loop */ - cb_log(6,0,"Got empty Msg..\n"); - return NULL; - } - - if (r<0) { - if (errno == EAGAIN) { - /*we wait for mISDN here*/ - cb_log(4,0,"mISDN_read wants us to wait\n"); - usleep(5000); - goto AGAIN; - } - - cb_log(0,0,"mISDN_read returned :%d error:%s (%d)\n",r,strerror(errno),errno); - } - -#if 0 - if (!(frm->prim == (DL_DATA|INDICATION) )|| (frm->prim == (PH_DATA|INDICATION))) - cb_log(0,0,"prim: %x dinfo:%x addr:%x msglen:%d frm->len:%d\n",frm->prim, frm->dinfo, frm->addr, msg->len,frm->len ); -#endif - return msg; -} - -void misdn_lib_isdn_l1watcher(int port) -{ - struct misdn_stack *stack; - - for (stack = glob_mgr->stack_list; stack && (stack->port != port); stack = stack->next) - ; - - if (stack) { - cb_log(4, port, "Checking L1 State\n"); - if (!stack->l1link) { - cb_log(4, port, "L1 State Down, trying to get it up again\n"); - misdn_lib_get_short_status(stack); - misdn_lib_get_l1_up(stack); - misdn_lib_get_l2_up(stack); - } - } -} - -/* This is a thread */ -static void misdn_lib_isdn_event_catcher(void *arg) -{ - struct misdn_lib *mgr = arg; - int zero_frm=0 , fff_frm=0 ; - int midev= mgr->midev; - int port=0; - - while (1) { - msg_t *msg = fetch_msg(midev); - iframe_t *frm; - - - if (!msg) continue; - - frm = (iframe_t*) msg->data; - - /** When we make a call from NT2Ast we get these frames **/ - if (frm->len == 0 && frm->addr == 0 && frm->dinfo == 0 && frm->prim == 0 ) { - zero_frm++; - free_msg(msg); - continue; - } else { - if (zero_frm) { - cb_log(0, port, "*** Alert: %d zero_frms caught\n", zero_frm); - zero_frm = 0 ; - } - } - - /** I get this sometimes after setup_bc **/ - if (frm->len == 0 && frm->dinfo == 0 && frm->prim == 0xffffffff ) { - fff_frm++; - free_msg(msg); - continue; - } else { - if (fff_frm) { - cb_log(0, port, "*** Alert: %d fff_frms caught\n", fff_frm); - fff_frm = 0 ; - } - } - - manager_isdn_handler(frm, msg); - } - -} - - -/** App Interface **/ - -int te_lib_init() { - char buff[1025] = ""; - iframe_t *frm=(iframe_t*)buff; - int midev=mISDN_open(); - int ret; - - if (midev<=0) return midev; - -/* create entity for layer 3 TE-mode */ - mISDN_write_frame(midev, buff, 0, MGR_NEWENTITY | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - ret = mISDN_read_frame(midev, frm, sizeof(iframe_t), 0, MGR_NEWENTITY | CONFIRM, TIMEOUT_1SEC); - - if (ret < mISDN_HEADER_LEN) { - noentity: - fprintf(stderr, "cannot request MGR_NEWENTITY from mISDN: %s\n",strerror(errno)); - exit(-1); - } - - entity = frm->dinfo & 0xffff ; - - if (!entity) - goto noentity; - - return midev; - -} - -void te_lib_destroy(int midev) -{ - char buf[1024]; - mISDN_write_frame(midev, buf, 0, MGR_DELENTITY | REQUEST, entity, 0, NULL, TIMEOUT_1SEC); - - cb_log(4, 0, "Entity deleted\n"); - mISDN_close(midev); - cb_log(4, 0, "midev closed\n"); -} - - - -void misdn_lib_transfer(struct misdn_bchannel* holded_bc) -{ - holded_bc->holded=0; -} - -struct misdn_bchannel *manager_find_bc_by_pid(int pid) -{ - struct misdn_stack *stack; - int i; - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next) { - for (i=0; i<=stack->b_num; i++) - if (stack->bc[i].pid == pid) return &stack->bc[i]; - } - - return NULL; -} - -struct misdn_bchannel *manager_find_bc_holded(struct misdn_bchannel* bc) -{ - struct misdn_stack *stack=get_stack_by_bc(bc); - return find_bc_holded(stack); -} - - - -static int test_inuse(struct misdn_bchannel *bc) -{ - struct timeval now; - gettimeofday(&now, NULL); - if (!bc->in_use) { - if (misdn_lib_port_is_pri(bc->port) && bc->last_used.tv_sec == now.tv_sec ) { - cb_log(2,bc->port, "channel with stid:%x for one second still in use! (n:%d lu:%d)\n", bc->b_stid, (int) now.tv_sec, (int) bc->last_used.tv_sec); - return 1; - } - - - cb_log(3,bc->port, "channel with stid:%x not in use!\n", bc->b_stid); - return 0; - } - - cb_log(2,bc->port, "channel with stid:%x in use!\n", bc->b_stid); - return 1; -} - - -static void prepare_bc(struct misdn_bchannel*bc, int channel) -{ - bc->channel = channel; - bc->channel_preselected = channel?1:0; - bc->in_use = 1; - bc->need_disconnect=1; - bc->need_release=1; - bc->need_release_complete=1; - bc->cause = AST_CAUSE_NORMAL_CLEARING; - - if (++mypid>5000) mypid=1; - bc->pid=mypid; - -#if 0 - bc->addr=0; - bc->b_stid=0; - bc->layer_id=0; -#endif -} - -struct misdn_bchannel* misdn_lib_get_free_bc(int port, int channel, int inout, int dec) -{ - struct misdn_stack *stack; - int i; - - if (channel < 0 || channel > MAX_BCHANS) { - cb_log(0,port,"Requested channel out of bounds (%d)\n",channel); - return NULL; - } - - usleep(1000); - - for (stack=glob_mgr->stack_list; stack; stack=stack->next) { - - if (stack->port == port) { - int maxnum; - - if (stack->blocked) { - cb_log(0,port,"Port is blocked\n"); - return NULL; - } - - if (channel > 0) { - if (channel <= stack->b_num) { - for (i = 0; i < stack->b_num; i++) { - if ( stack->bc[i].channel == channel) { - if (test_inuse(&stack->bc[i])) { - cb_log(0,port,"Requested channel:%d on port:%d is already in use\n",channel, port); - return NULL; - - } else { - prepare_bc(&stack->bc[i], channel); - return &stack->bc[i]; - } - } - } - } else { - cb_log(0,port,"Requested channel:%d is out of bounds on port:%d\n",channel, port); - return NULL; - } - } - - maxnum = inout && !stack->pri && !stack->ptp ? stack->b_num + 1 : stack->b_num; - - if (dec) { - for (i = maxnum-1; i>=0; i--) { - if (!test_inuse(&stack->bc[i])) { - /* 3. channel on bri means CW*/ - if (!stack->pri && i==stack->b_num) - stack->bc[i].cw=1; - - prepare_bc(&stack->bc[i], channel); - stack->bc[i].dec=1; - return &stack->bc[i]; - } - } - } else { - for (i = 0; i <maxnum; i++) { - if (!test_inuse(&stack->bc[i])) { - /* 3. channel on bri means CW*/ - if (!stack->pri && i==stack->b_num) - stack->bc[i].cw=1; - - prepare_bc(&stack->bc[i], channel); - return &stack->bc[i]; - } - } - } - - cb_log(1,port,"There is no free channel on port (%d)\n",port); - return NULL; - } - } - - cb_log(0,port,"Port is not configured (%d)\n",port); - return NULL; -} - - - - -/* ******************************************************************* */ -/*! - * \internal - * \brief Convert the facility function enum value into a string. - * - * \return String version of the enum value - */ -static const char *fac2str(enum FacFunction facility) -{ - static const struct { - enum FacFunction facility; - char *name; - } arr[] = { -/* *INDENT-OFF* */ - { Fac_None, "Fac_None" }, - { Fac_GetSupportedServices, "Fac_GetSupportedServices" }, - { Fac_Listen, "Fac_Listen" }, - { Fac_Suspend, "Fac_Suspend" }, - { Fac_Resume, "Fac_Resume" }, - { Fac_CFActivate, "Fac_CFActivate" }, - { Fac_CFDeactivate, "Fac_CFDeactivate" }, - { Fac_CFInterrogateParameters, "Fac_CFInterrogateParameters" }, - { Fac_CFInterrogateNumbers, "Fac_CFInterrogateNumbers" }, - { Fac_CD, "Fac_CD" }, - { Fac_AOCDCurrency, "Fac_AOCDCurrency" }, - { Fac_AOCDChargingUnit, "Fac_AOCDChargingUnit" }, -/* *INDENT-ON* */ - }; - - unsigned index; - - for (index = 0; index < ARRAY_LEN(arr); ++index) { - if (arr[index].facility == facility) { - return arr[index].name; - } - } /* end for */ - - return "unknown"; -} /* end fac2str() */ - -void misdn_lib_log_ies(struct misdn_bchannel *bc) -{ - struct misdn_stack *stack; - - if (!bc) return; - - stack = get_stack_by_bc(bc); - - if (!stack) return; - - cb_log(2, stack->port, " --> channel:%d mode:%s cause:%d ocause:%d rad:%s cad:%s\n", bc->channel, stack->nt?"NT":"TE", bc->cause, bc->out_cause, bc->rad, bc->cad); - - cb_log(2, stack->port, - " --> info_dad:%s onumplan:%c dnumplan:%c rnumplan:%c cpnnumplan:%c\n", - bc->info_dad, - bc->onumplan>=0?'0'+bc->onumplan:' ', - bc->dnumplan>=0?'0'+bc->dnumplan:' ', - bc->rnumplan>=0?'0'+bc->rnumplan:' ', - bc->cpnnumplan>=0?'0'+bc->cpnnumplan:' ' - ); - - cb_log(3, stack->port, " --> caps:%s pi:%x keypad:%s sending_complete:%d\n", bearer2str(bc->capability),bc->progress_indicator, bc->keypad, bc->sending_complete); - cb_log(4, stack->port, " --> screen:%d --> pres:%d\n", - bc->screen, bc->pres); - - cb_log(4, stack->port, " --> addr:%x l3id:%x b_stid:%x layer_id:%x\n", bc->addr, bc->l3_id, bc->b_stid, bc->layer_id); - - cb_log(4, stack->port, " --> facility:%s out_facility:%s\n",fac2str(bc->fac_in.Function),fac2str(bc->fac_out.Function)); - - cb_log(5, stack->port, " --> urate:%d rate:%d mode:%d user1:%d\n", bc->urate, bc->rate, bc->mode,bc->user1); - - cb_log(5, stack->port, " --> bc:%p h:%d sh:%d\n", bc, bc->holded, bc->stack_holder); -} - - -#define RETURN(a,b) {retval=a; goto b;} - -static void misdn_send_lock(struct misdn_bchannel *bc) -{ - //cb_log(0,bc->port,"Locking bc->pid:%d\n", bc->pid); - if (bc->send_lock) - pthread_mutex_lock(&bc->send_lock->lock); -} - -static void misdn_send_unlock(struct misdn_bchannel *bc) -{ - //cb_log(0,bc->port,"UnLocking bc->pid:%d\n", bc->pid); - if (bc->send_lock) - pthread_mutex_unlock(&bc->send_lock->lock); -} - -int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) -{ - msg_t *msg; - int retval=0; - struct misdn_stack *stack; - - if (!bc) RETURN(-1,OUT_POST_UNLOCK); - - stack = get_stack_by_bc(bc); - - if (!stack) { - cb_log(0,bc->port,"SENDEVENT: no Stack for event:%s oad:%s dad:%s \n", isdn_get_info(msgs_g, event, 0), bc->oad, bc->dad); - RETURN(-1,OUT); - } - - misdn_send_lock(bc); - - - cb_log(6,stack->port,"SENDEVENT: stack->nt:%d stack->upperid:%x\n",stack->nt, stack->upper_id); - - if ( stack->nt && !stack->l1link) { - /** Queue Event **/ - bc->evq=event; - cb_log(1, stack->port, "Queueing Event %s because L1 is down (btw. Activating L1)\n", isdn_get_info(msgs_g, event, 0)); - misdn_lib_get_l1_up(stack); - RETURN(0,OUT); - } - - cb_log(1, stack->port, "I SEND:%s oad:%s dad:%s pid:%d\n", isdn_get_info(msgs_g, event, 0), bc->oad, bc->dad, bc->pid); - cb_log(4, stack->port, " --> bc_state:%s\n",bc_state2str(bc->bc_state)); - misdn_lib_log_ies(bc); - - switch (event) { - case EVENT_SETUP: - if (create_process(glob_mgr->midev, bc)<0) { - cb_log(0, stack->port, " No free channel at the moment @ send_event\n"); - - RETURN(-ENOCHAN,OUT); - } - break; - - case EVENT_PROGRESS: - case EVENT_ALERTING: - case EVENT_PROCEEDING: - case EVENT_SETUP_ACKNOWLEDGE: - case EVENT_CONNECT: - if (!stack->nt) break; - - case EVENT_RETRIEVE_ACKNOWLEDGE: - - if (stack->nt) { - if (bc->channel <=0 ) { /* else we have the channel already */ - if (find_free_chan_in_stack(stack, bc, 0, 0)<0) { - cb_log(0, stack->port, " No free channel at the moment\n"); - /*FIXME: add disconnect*/ - RETURN(-ENOCHAN,OUT); - } - } - /* Its that i generate channels */ - } - - retval=setup_bc(bc); - if (retval == -EINVAL) { - cb_log(0,bc->port,"send_event: setup_bc failed\n"); - } - - if (misdn_cap_is_speech(bc->capability)) { - if ((event==EVENT_CONNECT)||(event==EVENT_RETRIEVE_ACKNOWLEDGE)) { - if ( *bc->crypt_key ) { - cb_log(4, stack->port, " --> ENABLING BLOWFISH channel:%d oad%d:%s dad%d:%s \n", bc->channel, bc->onumplan,bc->oad, bc->dnumplan,bc->dad); - - manager_ph_control_block(bc, BF_ENABLE_KEY, bc->crypt_key, strlen(bc->crypt_key) ); - } - - if (!bc->nodsp) manager_ph_control(bc, DTMF_TONE_START, 0); - manager_ec_enable(bc); - - if (bc->txgain != 0) { - cb_log(4, stack->port, "--> Changing txgain to %d\n", bc->txgain); - manager_ph_control(bc, VOL_CHANGE_TX, bc->txgain); - } - - if ( bc->rxgain != 0 ) { - cb_log(4, stack->port, "--> Changing rxgain to %d\n", bc->rxgain); - manager_ph_control(bc, VOL_CHANGE_RX, bc->rxgain); - } - } - } - break; - - case EVENT_HOLD_ACKNOWLEDGE: - { - struct misdn_bchannel *holded_bc=malloc(sizeof(struct misdn_bchannel)); - if (!holded_bc) { - cb_log(0,bc->port, "Could not allocate holded_bc!!!\n"); - RETURN(-1,OUT); - } - - /*backup the bc*/ - memcpy(holded_bc,bc,sizeof(struct misdn_bchannel)); - holded_bc->holded=1; - bc_state_change(holded_bc,BCHAN_CLEANED); - - stack_holder_add(stack,holded_bc); - - /*kill the bridge and clean the bchannel*/ - if (stack->nt) { - int channel; - if (bc->bc_state == BCHAN_BRIDGED) { - struct misdn_bchannel *bc2; - - misdn_split_conf(bc,bc->conf_id); - bc2 = find_bc_by_confid(bc->conf_id); - if (!bc2) { - cb_log(0,bc->port,"We have no second bc in bridge???\n"); - } else { - misdn_split_conf(bc2,bc->conf_id); - } - } - - channel = bc->channel; - - empty_bc(bc); - clean_up_bc(bc); - - if (channel>0) - empty_chan_in_stack(stack,channel); - - bc->in_use=0; - } - - } - break; - - /* finishing the channel eh ? */ - case EVENT_DISCONNECT: - if (!bc->need_disconnect) { - cb_log(0,bc->port," --> we have already send Disconnect\n"); - RETURN(-1,OUT); - } - - bc->need_disconnect=0; - break; - case EVENT_RELEASE: - if (!bc->need_release) { - cb_log(0,bc->port," --> we have already send Release\n"); - RETURN(-1,OUT); - } - bc->need_disconnect=0; - bc->need_release=0; - break; - case EVENT_RELEASE_COMPLETE: - if (!bc->need_release_complete) { - cb_log(0,bc->port," --> we have already send Release_complete\n"); - RETURN(-1,OUT); - } - bc->need_disconnect=0; - bc->need_release=0; - bc->need_release_complete=0; - - if (!stack->nt) { - /*create cleanup in TE*/ - int channel=bc->channel; - - int tmpcause=bc->cause; - int tmp_out_cause=bc->out_cause; - empty_bc(bc); - bc->cause=tmpcause; - bc->out_cause=tmp_out_cause; - clean_up_bc(bc); - - if (channel>0) - empty_chan_in_stack(stack,channel); - - bc->in_use=0; - } - break; - - case EVENT_CONNECT_ACKNOWLEDGE: - - if ( bc->nt || misdn_cap_is_speech(bc->capability)) { - int retval=setup_bc(bc); - if (retval == -EINVAL){ - cb_log(0,bc->port,"send_event: setup_bc failed\n"); - - } - } - - if (misdn_cap_is_speech(bc->capability)) { - if ( !bc->nodsp) manager_ph_control(bc, DTMF_TONE_START, 0); - manager_ec_enable(bc); - - if ( bc->txgain != 0 ) { - cb_log(4, stack->port, "--> Changing txgain to %d\n", bc->txgain); - manager_ph_control(bc, VOL_CHANGE_TX, bc->txgain); - } - if ( bc->rxgain != 0 ) { - cb_log(4, stack->port, "--> Changing rxgain to %d\n", bc->rxgain); - manager_ph_control(bc, VOL_CHANGE_RX, bc->rxgain); - } - } - break; - - default: - break; - } - - /* Later we should think about sending bchannel data directly to misdn. */ - msg = isdn_msg_build_event(msgs_g, bc, event, stack->nt); - msg_queue_tail(&stack->downqueue, msg); - sem_post(&glob_mgr->new_msg); - -OUT: - misdn_send_unlock(bc); - -OUT_POST_UNLOCK: - return retval; -} - - -static int handle_err(msg_t *msg) -{ - iframe_t *frm = (iframe_t*) msg->data; - - - if (!frm->addr) { - static int cnt=0; - if (!cnt) - cb_log(0,0,"mISDN Msg without Address pr:%x dinfo:%x\n",frm->prim,frm->dinfo); - cnt++; - if (cnt>100) { - cb_log(0,0,"mISDN Msg without Address pr:%x dinfo:%x (already more than 100 of them)\n",frm->prim,frm->dinfo); - cnt=0; - } - - free_msg(msg); - return 1; - - } - - switch (frm->prim) { - case MGR_SETSTACK|INDICATION: - return handle_bchan(msg); - break; - - case MGR_SETSTACK|CONFIRM: - case MGR_CLEARSTACK|CONFIRM: - free_msg(msg) ; - return 1; - break; - - case DL_DATA|CONFIRM: - cb_log(4,0,"DL_DATA|CONFIRM\n"); - free_msg(msg); - return 1; - - case PH_CONTROL|CONFIRM: - cb_log(4,0,"PH_CONTROL|CONFIRM\n"); - free_msg(msg); - return 1; - - case DL_DATA|INDICATION: - { - int port=(frm->addr&MASTER_ID_MASK) >> 8; - int channel=(frm->addr&CHILD_ID_MASK) >> 16; - struct misdn_bchannel *bc; - - /*we flush the read buffer here*/ - - cb_log(9,0,"BCHAN DATA without BC: addr:%x port:%d channel:%d\n",frm->addr, port,channel); - - free_msg(msg); - return 1; - - - bc = find_bc_by_channel(port, channel); - - if (!bc) { - struct misdn_stack *stack = find_stack_by_port(port); - - if (!stack) { - cb_log(0,0," --> stack not found\n"); - free_msg(msg); - return 1; - } - - cb_log(0,0," --> bc not found by channel\n"); - if (stack->l2link) - misdn_lib_get_l2_down(stack); - - if (stack->l1link) - misdn_lib_get_l1_down(stack); - - free_msg(msg); - return 1; - } - - cb_log(3,port," --> BC in state:%s\n", bc_state2str(bc->bc_state)); - } - } - - return 0; -} - -#if 0 -static int queue_l2l3(msg_t *msg) -{ - iframe_t *frm= (iframe_t*)msg->data; - struct misdn_stack *stack; - stack=find_stack_by_addr( frm->addr ); - - - if (!stack) { - return 0; - } - - msg_queue_tail(&stack->upqueue, msg); - sem_post(&glob_mgr->new_msg); - return 1; -} -#endif - -int manager_isdn_handler(iframe_t *frm ,msg_t *msg) -{ - - if (frm->dinfo==0xffffffff && frm->prim==(PH_DATA|CONFIRM)) { - cb_log(0,0,"SERIOUS BUG, dinfo == 0xffffffff, prim == PH_DATA | CONFIRM !!!!\n"); - } - - if ( ((frm->addr | ISDN_PID_BCHANNEL_BIT )>> 28 ) == 0x5) { - static int unhandled_bmsg_count=1000; - if (handle_bchan(msg)) { - return 0 ; - } - - if (unhandled_bmsg_count==1000) { - cb_log(0, 0, "received 1k Unhandled Bchannel Messages: prim %x len %d from addr %x, dinfo %x on this port.\n",frm->prim, frm->len, frm->addr, frm->dinfo); - unhandled_bmsg_count=0; - } - - unhandled_bmsg_count++; - free_msg(msg); - return 0; - } - -#ifdef RECV_FRM_SYSLOG_DEBUG - syslog(LOG_NOTICE,"mISDN recv: P(%02d): ADDR:%x PRIM:%x DINFO:%x\n",stack->port, frm->addr, frm->prim, frm->dinfo); -#endif - - if (handle_timers(msg)) - return 0 ; - - - if (handle_mgmt(msg)) - return 0 ; - - if (handle_l2(msg)) - return 0 ; - - /* Its important to handle l1 AFTER l2 */ - if (handle_l1(msg)) - return 0 ; - - if (handle_frm_nt(msg)) { - return 0; - } - - if (handle_frm(msg)) { - return 0; - } - - if (handle_err(msg)) { - return 0 ; - } - - cb_log(0, 0, "Unhandled Message: prim %x len %d from addr %x, dinfo %x on this port.\n",frm->prim, frm->len, frm->addr, frm->dinfo); - free_msg(msg); - - - return 0; -} - - - - -int misdn_lib_get_port_info(int port) -{ - msg_t *msg=alloc_msg(MAX_MSG_SIZE); - iframe_t *frm; - struct misdn_stack *stack=find_stack_by_port(port); - if (!msg) { - cb_log(0, port, "misdn_lib_get_port_info: alloc_msg failed!\n"); - return -1; - } - frm=(iframe_t*)msg->data; - if (!stack ) { - cb_log(0, port, "There is no Stack for this port.\n"); - return -1; - } - /* activate bchannel */ - frm->prim = CC_STATUS_ENQUIRY | REQUEST; - - frm->addr = stack->upper_id| FLG_MSG_DOWN; - - frm->dinfo = 0; - frm->len = 0; - - msg_queue_tail(&glob_mgr->activatequeue, msg); - sem_post(&glob_mgr->new_msg); - - - return 0; -} - - -int queue_cleanup_bc(struct misdn_bchannel *bc) -{ - msg_t *msg=alloc_msg(MAX_MSG_SIZE); - iframe_t *frm; - if (!msg) { - cb_log(0, bc->port, "queue_cleanup_bc: alloc_msg failed!\n"); - return -1; - } - frm=(iframe_t*)msg->data; - - /* activate bchannel */ - frm->prim = MGR_CLEARSTACK| REQUEST; - - frm->addr = bc->l3_id; - - frm->dinfo = bc->port; - frm->len = 0; - - msg_queue_tail(&glob_mgr->activatequeue, msg); - sem_post(&glob_mgr->new_msg); - - return 0; - -} - -int misdn_lib_pid_restart(int pid) -{ - struct misdn_bchannel *bc=manager_find_bc_by_pid(pid); - - if (bc) { - manager_clean_bc(bc); - } - return 0; -} - -/*Sends Restart message for every bchannel*/ -int misdn_lib_send_restart(int port, int channel) -{ - struct misdn_stack *stack=find_stack_by_port(port); - struct misdn_bchannel dummybc; - /*default is all channels*/ - cb_log(0, port, "Sending Restarts on this port.\n"); - - misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0); - - /*default is all channels*/ - if (channel <0) { - dummybc.channel=-1; - cb_log(0, port, "Restarting and all Interfaces\n"); - misdn_lib_send_event(&dummybc, EVENT_RESTART); - - return 0; - } - - /*if a channel is specified we restart only this one*/ - if (channel >0) { - int cnt; - dummybc.channel=channel; - cb_log(0, port, "Restarting and cleaning channel %d\n",channel); - misdn_lib_send_event(&dummybc, EVENT_RESTART); - /* clean up chan in stack, to be sure we don't think it's - * in use anymore */ - for (cnt=0; cnt<=stack->b_num; cnt++) { - if (stack->bc[cnt].channel == channel) { - empty_bc(&stack->bc[cnt]); - clean_up_bc(&stack->bc[cnt]); - stack->bc[cnt].in_use=0; - } - } - } - - return 0; -} - -/*reinitializes the L2/L3*/ -int misdn_lib_port_restart(int port) -{ - struct misdn_stack *stack=find_stack_by_port(port); - - cb_log(0, port, "Restarting this port.\n"); - if (stack) { - cb_log(0, port, "Stack:%p\n",stack); - - clear_l3(stack); - { - msg_t *msg=alloc_msg(MAX_MSG_SIZE); - iframe_t *frm; - - if (!msg) { - cb_log(0, port, "port_restart: alloc_msg failed\n"); - return -1; - } - - frm=(iframe_t*)msg->data; - /* we must activate if we are deactivated */ - /* activate bchannel */ - frm->prim = DL_RELEASE | REQUEST; - frm->addr = stack->upper_id | FLG_MSG_DOWN; - - frm->dinfo = 0; - frm->len = 0; - msg_queue_tail(&glob_mgr->activatequeue, msg); - sem_post(&glob_mgr->new_msg); - } - - if (stack->nt) - misdn_lib_reinit_nt_stack(stack->port); - - } - - return 0; -} - - - -sem_t handler_started; - -/* This is a thread */ -static void manager_event_handler(void *arg) -{ - sem_post(&handler_started); - while (1) { - struct misdn_stack *stack; - msg_t *msg; - - /** wait for events **/ - sem_wait(&glob_mgr->new_msg); - - for (msg=msg_dequeue(&glob_mgr->activatequeue); - msg; - msg=msg_dequeue(&glob_mgr->activatequeue) - ) - { - - iframe_t *frm = (iframe_t*) msg->data ; - - switch ( frm->prim) { - - case MGR_CLEARSTACK | REQUEST: - /*a queued bchannel cleanup*/ - { - struct misdn_stack *stack=find_stack_by_port(frm->dinfo); - struct misdn_bchannel *bc; - if (!stack) { - cb_log(0,0,"no stack found with port [%d]!! so we cannot cleanup the bc\n",frm->dinfo); - free_msg(msg); - break; - } - - bc = find_bc_by_l3id(stack, frm->addr); - if (bc) { - cb_log(1,bc->port,"CLEARSTACK queued, cleaning up\n"); - clean_up_bc(bc); - } else { - cb_log(0,stack->port,"bc could not be cleaned correctly !! addr [%x]\n",frm->addr); - } - } - free_msg(msg); - break; - case MGR_SETSTACK | REQUEST : - /* Warning: memory leak here if we get this message */ - break; - default: - mISDN_write(glob_mgr->midev, frm, mISDN_HEADER_LEN+frm->len, TIMEOUT_1SEC); - free_msg(msg); - } - } - - for (stack=glob_mgr->stack_list; - stack; - stack=stack->next ) { - - while ( (msg=msg_dequeue(&stack->upqueue)) ) { - /** Handle L2/3 Signalling after bchans **/ - if (!handle_frm_nt(msg)) { - /* Maybe it's TE */ - if (!handle_frm(msg)) { - /* wow none! */ - cb_log(0,stack->port,"Wow we've got a strange issue while dequeueing a Frame\n"); - } - } - } - - /* Here we should check if we really want to - send all the messages we've queued, lets - assume we've queued a Disconnect, but - received it already from the other side!*/ - - while ( (msg=msg_dequeue(&stack->downqueue)) ) { - if (stack->nt ) { - if (stack->nst.manager_l3(&stack->nst, msg)) - cb_log(0, stack->port, "Error@ Sending Message in NT-Stack.\n"); - - } else { - iframe_t *frm = (iframe_t *)msg->data; - struct misdn_bchannel *bc = find_bc_by_l3id(stack, frm->dinfo); - if (bc) - send_msg(glob_mgr->midev, bc, msg); - else { - if (frm->dinfo == MISDN_ID_GLOBAL) { - struct misdn_bchannel dummybc; - misdn_make_dummy(&dummybc, stack->port, MISDN_ID_GLOBAL, stack->nt, 0); - send_msg(glob_mgr->midev, &dummybc, msg); - } - } - } - } - } - } -} - - -int misdn_lib_maxports_get(void) -{ - /* BE AWARE WE HAVE NO cb_log() HERE! */ - - int i = mISDN_open(); - int max=0; - - if (i<0) - return -1; - - max = mISDN_get_stack_count(i); - - mISDN_close(i); - - return max; -} - - -void misdn_lib_nt_keepcalls( int kc) -{ -#ifdef FEATURE_NET_KEEPCALLS - if (kc) { - struct misdn_stack *stack=get_misdn_stack(); - for ( ; stack; stack=stack->next) { - stack->nst.feature |= FEATURE_NET_KEEPCALLS; - } - } -#endif -} - -void misdn_lib_nt_debug_init( int flags, char *file ) -{ - static int init=0; - char *f; - - if (!flags) - f=NULL; - else - f=file; - - if (!init) { - debug_init( flags , f, f, f); - init=1; - } else { - debug_close(); - debug_init( flags , f, f, f); - } -} - - -int misdn_lib_init(char *portlist, struct misdn_lib_iface *iface, void *user_data) -{ - struct misdn_lib *mgr=calloc(1, sizeof(struct misdn_lib)); - char *tok, *tokb; - char plist[1024]; - int midev; - int port_count=0; - - cb_log = iface->cb_log; - cb_event = iface->cb_event; - cb_jb_empty = iface->cb_jb_empty; - - glob_mgr = mgr; - - msg_init(); - - misdn_lib_nt_debug_init(0,NULL); - - if (!portlist || (*portlist == 0) ) return 1; - - init_flip_bits(); - - { - strncpy(plist,portlist, 1024); - plist[1023] = 0; - } - - memcpy(tone_425_flip,tone_425,TONE_425_SIZE); - flip_buf_bits(tone_425_flip,TONE_425_SIZE); - - memcpy(tone_silence_flip,tone_SILENCE,TONE_SILENCE_SIZE); - flip_buf_bits(tone_silence_flip,TONE_SILENCE_SIZE); - - midev=te_lib_init(); - mgr->midev=midev; - - port_count=mISDN_get_stack_count(midev); - - msg_queue_init(&mgr->activatequeue); - - if (sem_init(&mgr->new_msg, 1, 0)<0) - sem_init(&mgr->new_msg, 0, 0); - - for (tok=strtok_r(plist," ,",&tokb ); - tok; - tok=strtok_r(NULL," ,",&tokb)) { - int port = atoi(tok); - struct misdn_stack *stack; - static int first=1; - int ptp=0; - - if (strstr(tok, "ptp")) - ptp=1; - - if (port > port_count) { - cb_log(0, port, "Couldn't Initialize this port since we have only %d ports\n", port_count); - exit(1); - } - stack=stack_init(midev, port, ptp); - - if (!stack) { - perror("stack_init"); - exit(1); - } - - { - int i; - for(i=0;i<=stack->b_num; i++) { - int r; - if ((r=init_bc(stack, &stack->bc[i], stack->midev,port,i, "", 1))<0) { - cb_log(0, port, "Got Err @ init_bc :%d\n",r); - exit(1); - } - } - } - - if (stack && first) { - mgr->stack_list=stack; - first=0; - continue; - } - - if (stack) { - struct misdn_stack * help; - for ( help=mgr->stack_list; help; help=help->next ) - if (help->next == NULL) break; - help->next=stack; - } - - } - - if (sem_init(&handler_started, 1, 0)<0) - sem_init(&handler_started, 0, 0); - - cb_log(8, 0, "Starting Event Handler\n"); - pthread_create( &mgr->event_handler_thread, NULL,(void*)manager_event_handler, mgr); - - sem_wait(&handler_started) ; - cb_log(8, 0, "Starting Event Catcher\n"); - pthread_create( &mgr->event_thread, NULL, (void*)misdn_lib_isdn_event_catcher, mgr); - - cb_log(8, 0, "Event Catcher started\n"); - - global_state= MISDN_INITIALIZED; - - return (mgr == NULL); -} - -void misdn_lib_destroy(void) -{ - struct misdn_stack *help; - int i; - - for ( help=glob_mgr->stack_list; help; help=help->next ) { - for(i=0;i<=help->b_num; i++) { - char buf[1024]; - mISDN_write_frame(help->midev, buf, help->bc[i].addr, MGR_DELLAYER | REQUEST, 0, 0, NULL, TIMEOUT_1SEC); - help->bc[i].addr = 0; - } - cb_log (1, help->port, "Destroying this port.\n"); - stack_destroy(help); - } - - if (global_state == MISDN_INITIALIZED) { - cb_log(4, 0, "Killing Handler Thread\n"); - if ( pthread_cancel(glob_mgr->event_handler_thread) == 0 ) { - cb_log(4, 0, "Joining Handler Thread\n"); - pthread_join(glob_mgr->event_handler_thread, NULL); - } - - cb_log(4, 0, "Killing Main Thread\n"); - if ( pthread_cancel(glob_mgr->event_thread) == 0 ) { - cb_log(4, 0, "Joining Main Thread\n"); - pthread_join(glob_mgr->event_thread, NULL); - } - } - - cb_log(1, 0, "Closing mISDN device\n"); - te_lib_destroy(glob_mgr->midev); -} - -char *manager_isdn_get_info(enum event_e event) -{ - return isdn_get_info(msgs_g , event, 0); -} - -void manager_bchannel_activate(struct misdn_bchannel *bc) -{ - char buf[128]; - - struct misdn_stack *stack=get_stack_by_bc(bc); - - if (!stack) { - cb_log(0, bc->port, "bchannel_activate: Stack not found !"); - return ; - } - - /* we must activate if we are deactivated */ - clear_ibuffer(bc->astbuf); - - cb_log(5, stack->port, "$$$ Bchan Activated addr %x\n", bc->addr); - - mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_DOWN, DL_ESTABLISH | REQUEST, 0,0, NULL, TIMEOUT_1SEC); - - return ; -} - - -void manager_bchannel_deactivate(struct misdn_bchannel * bc) -{ - struct misdn_stack *stack=get_stack_by_bc(bc); - iframe_t dact; - char buf[128]; - - switch (bc->bc_state) { - case BCHAN_ACTIVATED: - break; - case BCHAN_BRIDGED: - misdn_split_conf(bc,bc->conf_id); - break; - default: - cb_log( 4, bc->port,"bchan_deactivate: called but not activated\n"); - return ; - - } - - cb_log(5, stack->port, "$$$ Bchan deActivated addr %x\n", bc->addr); - - bc->generate_tone=0; - - dact.prim = DL_RELEASE | REQUEST; - dact.addr = bc->addr | FLG_MSG_DOWN; - dact.dinfo = 0; - dact.len = 0; - mISDN_write_frame(stack->midev, buf, bc->addr | FLG_MSG_DOWN, DL_RELEASE|REQUEST,0,0,NULL, TIMEOUT_1SEC); - - clear_ibuffer(bc->astbuf); - - bc_state_change(bc,BCHAN_RELEASE); - - return; -} - - -int misdn_lib_tx2misdn_frm(struct misdn_bchannel *bc, void *data, int len) -{ - struct misdn_stack *stack=get_stack_by_bc(bc); - char buf[4096 + mISDN_HEADER_LEN]; - iframe_t *frm = (iframe_t*)buf; - int r; - - switch (bc->bc_state) { - case BCHAN_ACTIVATED: - case BCHAN_BRIDGED: - break; - default: - cb_log(3, bc->port, "BC not yet activated (state:%s)\n",bc_state2str(bc->bc_state)); - return -1; - } - - frm->prim = DL_DATA|REQUEST; - frm->dinfo = 0; - frm->addr = bc->addr | FLG_MSG_DOWN ; - - frm->len = len; - memcpy(&buf[mISDN_HEADER_LEN], data,len); - - if ( misdn_cap_is_speech(bc->capability) ) - flip_buf_bits( &buf[mISDN_HEADER_LEN], len); - else - cb_log(6, stack->port, "Writing %d data bytes\n",len); - - cb_log(9, stack->port, "Writing %d bytes 2 mISDN\n",len); - r=mISDN_write(stack->midev, buf, frm->len + mISDN_HEADER_LEN, TIMEOUT_INFINIT); - return 0; -} - - - -/* - * send control information to the channel (dsp-module) - */ -void manager_ph_control(struct misdn_bchannel *bc, int c1, int c2) -{ - unsigned char buffer[mISDN_HEADER_LEN+2*sizeof(int)]; - iframe_t *ctrl = (iframe_t *)buffer; /* preload data */ - unsigned int *d = (unsigned int*)&ctrl->data.p; - /*struct misdn_stack *stack=get_stack_by_bc(bc);*/ - - cb_log(4,bc->port,"ph_control: c1:%x c2:%x\n",c1,c2); - - ctrl->prim = PH_CONTROL | REQUEST; - ctrl->addr = bc->addr | FLG_MSG_DOWN; - ctrl->dinfo = 0; - ctrl->len = sizeof(unsigned int)*2; - *d++ = c1; - *d++ = c2; - mISDN_write(glob_mgr->midev, ctrl, mISDN_HEADER_LEN+ctrl->len, TIMEOUT_1SEC); -} - -/* - * allow live control of channel parameters - */ -void isdn_lib_update_rxgain (struct misdn_bchannel *bc) -{ - manager_ph_control(bc, VOL_CHANGE_RX, bc->rxgain); -} - -void isdn_lib_update_txgain (struct misdn_bchannel *bc) -{ - manager_ph_control(bc, VOL_CHANGE_TX, bc->txgain); -} - -void isdn_lib_update_ec (struct misdn_bchannel *bc) -{ -#ifdef MISDN_1_2 - if (*bc->pipeline) -#else - if (bc->ec_enable) -#endif - manager_ec_enable(bc); - else - manager_ec_disable(bc); -} - -void isdn_lib_stop_dtmf (struct misdn_bchannel *bc) -{ - manager_ph_control(bc, DTMF_TONE_STOP, 0); -} - -/* - * send control information to the channel (dsp-module) - */ -void manager_ph_control_block(struct misdn_bchannel *bc, int c1, void *c2, int c2_len) -{ - unsigned char buffer[mISDN_HEADER_LEN+sizeof(int)+c2_len]; - iframe_t *ctrl = (iframe_t *)buffer; - unsigned int *d = (unsigned int *)&ctrl->data.p; - /*struct misdn_stack *stack=get_stack_by_bc(bc);*/ - - ctrl->prim = PH_CONTROL | REQUEST; - ctrl->addr = bc->addr | FLG_MSG_DOWN; - ctrl->dinfo = 0; - ctrl->len = sizeof(unsigned int) + c2_len; - *d++ = c1; - memcpy(d, c2, c2_len); - mISDN_write(glob_mgr->midev, ctrl, mISDN_HEADER_LEN+ctrl->len, TIMEOUT_1SEC); -} - - - - -void manager_clean_bc(struct misdn_bchannel *bc ) -{ - struct misdn_stack *stack=get_stack_by_bc(bc); - - if (bc->channel>0) - empty_chan_in_stack(stack, bc->channel); - empty_bc(bc); - bc->in_use=0; - - cb_event(EVENT_CLEANUP, bc, NULL); -} - - -void stack_holder_add(struct misdn_stack *stack, struct misdn_bchannel *holder) -{ - struct misdn_bchannel *help; - cb_log(4,stack->port, "*HOLDER: add %x\n",holder->l3_id); - - holder->stack_holder=1; - holder->next=NULL; - - if (!stack->holding) { - stack->holding = holder; - return; - } - - for (help=stack->holding; - help; - help=help->next) { - if (!help->next) { - help->next=holder; - break; - } - } - -} - -void stack_holder_remove(struct misdn_stack *stack, struct misdn_bchannel *holder) -{ - struct misdn_bchannel *h1; - - if (!holder->stack_holder) return; - - holder->stack_holder=0; - - cb_log(4,stack->port, "*HOLDER: remove %x\n",holder->l3_id); - if (!stack || ! stack->holding) return; - - if (holder == stack->holding) { - stack->holding = stack->holding->next; - return; - } - - for (h1=stack->holding; - h1; - h1=h1->next) { - if (h1->next == holder) { - h1->next=h1->next->next; - return ; - } - } -} - -struct misdn_bchannel *stack_holder_find_bychan(struct misdn_stack *stack, int chan) -{ - struct misdn_bchannel *help; - - cb_log(4,stack?stack->port:0, "*HOLDER: find_bychan %c\n", chan); - - if (!stack) return NULL; - - for (help=stack->holding; - help; - help=help->next) { - if (help->channel == chan) { - cb_log(4,stack->port, "*HOLDER: found_bychan bc\n"); - return help; - } - } - - cb_log(4,stack->port, "*HOLDER: find_bychan nothing\n"); - return NULL; - -} - -struct misdn_bchannel *stack_holder_find(struct misdn_stack *stack, unsigned long l3id) -{ - struct misdn_bchannel *help; - - cb_log(4,stack?stack->port:0, "*HOLDER: find %lx\n",l3id); - - if (!stack) return NULL; - - for (help=stack->holding; - help; - help=help->next) { - if (help->l3_id == l3id) { - cb_log(4,stack->port, "*HOLDER: found bc\n"); - return help; - } - } - - cb_log(4,stack->port, "*HOLDER: find nothing\n"); - return NULL; -} - - - -void misdn_lib_send_tone(struct misdn_bchannel *bc, enum tone_e tone) -{ - char buf[mISDN_HEADER_LEN + 128] = ""; - iframe_t *frm = (iframe_t*)buf; - - switch(tone) { - case TONE_DIAL: - manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_DIALTONE); - break; - - case TONE_ALERTING: - manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_RINGING); - break; - - case TONE_HANGUP: - manager_ph_control(bc, TONE_PATT_ON, TONE_GERMAN_HANGUP); - break; - - case TONE_NONE: - default: - manager_ph_control(bc, TONE_PATT_OFF, TONE_GERMAN_HANGUP); - } - - frm->prim=DL_DATA|REQUEST; - frm->addr=bc->addr|FLG_MSG_DOWN; - frm->dinfo=0; - frm->len=128; - - mISDN_write(glob_mgr->midev, frm, mISDN_HEADER_LEN+frm->len, TIMEOUT_1SEC); -} - - -void manager_ec_enable(struct misdn_bchannel *bc) -{ - struct misdn_stack *stack=get_stack_by_bc(bc); - - cb_log(4, stack?stack->port:0,"ec_enable\n"); - - if (!misdn_cap_is_speech(bc->capability)) { - cb_log(1, stack?stack->port:0, " --> no speech? cannot enable EC\n"); - } else { - -#ifdef MISDN_1_2 - if (*bc->pipeline) { - cb_log(3, stack?stack->port:0,"Sending Control PIPELINE_CFG %s\n",bc->pipeline); - manager_ph_control_block(bc, PIPELINE_CFG, bc->pipeline, strlen(bc->pipeline) + 1); - } -#else - int ec_arr[2]; - - if (bc->ec_enable) { - cb_log(3, stack?stack->port:0,"Sending Control ECHOCAN_ON taps:%d\n",bc->ec_deftaps); - - switch (bc->ec_deftaps) { - case 4: - case 8: - case 16: - case 32: - case 64: - case 128: - case 256: - case 512: - case 1024: - cb_log(4, stack->port, "Taps is %d\n",bc->ec_deftaps); - break; - default: - cb_log(0, stack->port, "Taps should be power of 2\n"); - bc->ec_deftaps=128; - } - - ec_arr[0]=bc->ec_deftaps; - ec_arr[1]=0; - - manager_ph_control_block(bc, ECHOCAN_ON, ec_arr, sizeof(ec_arr)); - } -#endif - } -} - - - -void manager_ec_disable(struct misdn_bchannel *bc) -{ - struct misdn_stack *stack=get_stack_by_bc(bc); - - cb_log(4, stack?stack->port:0," --> ec_disable\n"); - - if (!misdn_cap_is_speech(bc->capability)) { - cb_log(1, stack?stack->port:0, " --> no speech? cannot disable EC\n"); - return; - } - -#ifdef MISDN_1_2 - manager_ph_control_block(bc, PIPELINE_CFG, "", 0); -#else - if ( ! bc->ec_enable) { - cb_log(3, stack?stack->port:0, "Sending Control ECHOCAN_OFF\n"); - manager_ph_control(bc, ECHOCAN_OFF, 0); - } -#endif -} - -struct misdn_stack* get_misdn_stack() { - return glob_mgr->stack_list; -} - - - -void misdn_join_conf(struct misdn_bchannel *bc, int conf_id) -{ - char data[16] = ""; - - bc_state_change(bc,BCHAN_BRIDGED); - manager_ph_control(bc, CMX_RECEIVE_OFF, 0); - manager_ph_control(bc, CMX_CONF_JOIN, conf_id); - - cb_log(3,bc->port, "Joining bc:%x in conf:%d\n",bc->addr,conf_id); - - misdn_lib_tx2misdn_frm(bc, data, sizeof(data) - 1); -} - - -void misdn_split_conf(struct misdn_bchannel *bc, int conf_id) -{ - bc_state_change(bc,BCHAN_ACTIVATED); - manager_ph_control(bc, CMX_RECEIVE_ON, 0); - manager_ph_control(bc, CMX_CONF_SPLIT, conf_id); - - cb_log(4,bc->port, "Splitting bc:%x in conf:%d\n",bc->addr,conf_id); -} - -void misdn_lib_bridge( struct misdn_bchannel * bc1, struct misdn_bchannel *bc2) -{ - int conf_id = bc1->pid + 1; - struct misdn_bchannel *bc_list[] = { bc1, bc2, NULL }; - struct misdn_bchannel **bc; - - cb_log(4, bc1->port, "I Send: BRIDGE from:%d to:%d\n",bc1->port,bc2->port); - - for (bc=bc_list; *bc; bc++) { - (*bc)->conf_id=conf_id; - cb_log(4, (*bc)->port, " --> bc_addr:%x\n",(*bc)->addr); - - switch((*bc)->bc_state) { - case BCHAN_ACTIVATED: - misdn_join_conf(*bc,conf_id); - break; - default: - bc_next_state_change(*bc,BCHAN_BRIDGED); - break; - } - } -} - -void misdn_lib_split_bridge( struct misdn_bchannel * bc1, struct misdn_bchannel *bc2) -{ - - struct misdn_bchannel *bc_list[]={ - bc1,bc2,NULL - }; - struct misdn_bchannel **bc; - - for (bc=bc_list; *bc; bc++) { - if ( (*bc)->bc_state == BCHAN_BRIDGED){ - misdn_split_conf( *bc, (*bc)->conf_id); - } else { - cb_log( 2, (*bc)->port, "BC not bridged (state:%s) so not splitting it\n",bc_state2str((*bc)->bc_state)); - } - } - -} - - - -void misdn_lib_echo(struct misdn_bchannel *bc, int onoff) -{ - cb_log(3,bc->port, " --> ECHO %s\n", onoff?"ON":"OFF"); - manager_ph_control(bc, onoff?CMX_ECHO_ON:CMX_ECHO_OFF, 0); -} - - - -void misdn_lib_reinit_nt_stack(int port) -{ - struct misdn_stack *stack=find_stack_by_port(port); - - if (stack) { - stack->l2link=0; - stack->blocked=0; - - cleanup_Isdnl3(&stack->nst); - cleanup_Isdnl2(&stack->nst); - - - memset(&stack->nst, 0, sizeof(net_stack_t)); - memset(&stack->mgr, 0, sizeof(manager_t)); - - stack->mgr.nst = &stack->nst; - stack->nst.manager = &stack->mgr; - - stack->nst.l3_manager = handle_event_nt; - stack->nst.device = glob_mgr->midev; - stack->nst.cardnr = port; - stack->nst.d_stid = stack->d_stid; - - stack->nst.feature = FEATURE_NET_HOLD; - if (stack->ptp) - stack->nst.feature |= FEATURE_NET_PTP; - if (stack->pri) - stack->nst.feature |= FEATURE_NET_CRLEN2 | FEATURE_NET_EXTCID; - - stack->nst.l1_id = stack->lower_id; - stack->nst.l2_id = stack->upper_id; - - msg_queue_init(&stack->nst.down_queue); - - Isdnl2Init(&stack->nst); - Isdnl3Init(&stack->nst); - - if (!stack->ptp) - misdn_lib_get_l1_up(stack); - misdn_lib_get_l2_up(stack); - } -} - - diff --git a/1.4.23-rc4/channels/misdn/isdn_lib.h b/1.4.23-rc4/channels/misdn/isdn_lib.h deleted file mode 100644 index 451876888..000000000 --- a/1.4.23-rc4/channels/misdn/isdn_lib.h +++ /dev/null @@ -1,674 +0,0 @@ -/* - * Chan_Misdn -- Channel Driver for Asterisk - * - * Interface to mISDN - * - * Copyright (C) 2004, Christian Richter - * - * Christian Richter <crich@beronet.com> - * - * This program is free software, distributed under the terms of - * the GNU General Public License - */ - -/*! \file - * \brief Interface to mISDN - * - * \author Christian Richter <crich@beronet.com> - */ - -#ifndef TE_LIB -#define TE_LIB - -#include <mISDNuser/suppserv.h> - -/** For initialization usage **/ -/* typedef int ie_nothing_t ;*/ -/** end of init usage **/ - - -/* - * uncomment the following to make chan_misdn create - * record files in /tmp/misdn-{rx|tx}-PortChannel format - * */ - -/*#define MISDN_SAVE_DATA*/ - -#ifdef WITH_BEROEC -typedef int beroec_t; - - -enum beroec_type { - BEROEC_FULLBAND=0, - BEROEC_SUBBAND, - BEROEC_FASTSUBBAND -}; - -void beroec_init(void); -void beroec_exit(void); -beroec_t *beroec_new(int tail, enum beroec_type type, int anti_howl, - int tonedisable, int zerocoeff, int adapt, int nlp); - -void beroec_destroy(beroec_t *ec); -int beroec_cancel_alaw_chunk(beroec_t *ec, - char *send, - char *receive, - int len); - -int beroec_version(void); -#endif - - - -enum tone_e { - TONE_NONE=0, - TONE_DIAL, - TONE_ALERTING, - TONE_FAR_ALERTING, - TONE_BUSY, - TONE_HANGUP, - TONE_CUSTOM, - TONE_FILE -}; - - - -#define MAX_BCHANS 31 - -enum bchannel_state { - BCHAN_CLEANED=0, - BCHAN_EMPTY, - BCHAN_SETUP, - BCHAN_SETUPED, - BCHAN_ACTIVE, - BCHAN_ACTIVATED, - BCHAN_BRIDGE, - BCHAN_BRIDGED, - BCHAN_RELEASE, - BCHAN_RELEASED, - BCHAN_CLEAN, - BCHAN_CLEAN_REQUEST, - BCHAN_ERROR -}; - - -enum misdn_err_e { - ENOCHAN=1 -}; - - -enum mISDN_NUMBER_PLAN { - NUMPLAN_UNINITIALIZED=-1, - NUMPLAN_INTERNATIONAL=0x1, - NUMPLAN_NATIONAL=0x2, - NUMPLAN_SUBSCRIBER=0x4, - NUMPLAN_UNKNOWN=0x0 -}; - - -enum event_response_e { - RESPONSE_IGNORE_SETUP_WITHOUT_CLOSE, - RESPONSE_IGNORE_SETUP, - RESPONSE_RELEASE_SETUP, - RESPONSE_ERR, - RESPONSE_OK -}; - - -enum event_e { - EVENT_NOTHING, - EVENT_TONE_GENERATE, - EVENT_BCHAN_DATA, - EVENT_BCHAN_ACTIVATED, - EVENT_BCHAN_ERROR, - EVENT_CLEANUP, - EVENT_PROCEEDING, - EVENT_PROGRESS, - EVENT_SETUP, - EVENT_ALERTING, - EVENT_CONNECT, - EVENT_SETUP_ACKNOWLEDGE, - EVENT_CONNECT_ACKNOWLEDGE , - EVENT_USER_INFORMATION, - EVENT_SUSPEND_REJECT, - EVENT_RESUME_REJECT, - EVENT_HOLD, - EVENT_SUSPEND, - EVENT_RESUME, - EVENT_HOLD_ACKNOWLEDGE, - EVENT_SUSPEND_ACKNOWLEDGE, - EVENT_RESUME_ACKNOWLEDGE, - EVENT_HOLD_REJECT, - EVENT_RETRIEVE, - EVENT_RETRIEVE_ACKNOWLEDGE, - EVENT_RETRIEVE_REJECT, - EVENT_DISCONNECT, - EVENT_RESTART, - EVENT_RELEASE, - EVENT_RELEASE_COMPLETE, - EVENT_FACILITY, - EVENT_NOTIFY, - EVENT_STATUS_ENQUIRY, - EVENT_INFORMATION, - EVENT_STATUS, - EVENT_TIMEOUT, - EVENT_DTMF_TONE, - EVENT_NEW_L3ID, - EVENT_NEW_BC, - EVENT_PORT_ALARM, - EVENT_NEW_CHANNEL, - EVENT_UNKNOWN -}; - - -enum ie_name_e { - IE_DUMMY, - IE_LAST -}; - -enum { /* bearer capability */ - INFO_CAPABILITY_SPEECH=0, - INFO_CAPABILITY_AUDIO_3_1K=0x10 , - INFO_CAPABILITY_AUDIO_7K=0x11 , - INFO_CAPABILITY_VIDEO =0x18, - INFO_CAPABILITY_DIGITAL_UNRESTRICTED =0x8, - INFO_CAPABILITY_DIGITAL_RESTRICTED =0x09, - INFO_CAPABILITY_DIGITAL_UNRESTRICTED_TONES -}; - -enum { /* progress indicators */ - INFO_PI_CALL_NOT_E2E_ISDN =0x01, - INFO_PI_CALLED_NOT_ISDN =0x02, - INFO_PI_CALLER_NOT_ISDN =0x03, - INFO_PI_CALLER_RETURNED_TO_ISDN =0x04, - INFO_PI_INBAND_AVAILABLE =0x08, - INFO_PI_DELAY_AT_INTERF =0x0a, - INFO_PI_INTERWORKING_WITH_PUBLIC =0x10, - INFO_PI_INTERWORKING_NO_RELEASE =0x11, - INFO_PI_INTERWORKING_NO_RELEASE_PRE_ANSWER =0x12, - INFO_PI_INTERWORKING_NO_RELEASE_POST_ANSWER =0x13 -}; - -enum { /*CODECS*/ - INFO_CODEC_ULAW=2, - INFO_CODEC_ALAW=3 -}; - - -enum layer_e { - L3, - L2, - L1, - UNKNOWN -}; - - - -struct misdn_bchannel { - /*! \brief B channel send locking structure */ - struct send_lock *send_lock; - - /*! \brief TRUE if this is a dummy BC record */ - int dummy; - - /*! \brief TRUE if NT side of protocol (TE otherwise) */ - int nt; - - /*! \brief TRUE if ISDN-PRI (ISDN-BRI otherwise) */ - int pri; - - /*! \brief Logical Layer 1 port associated with this B channel */ - int port; - - /** init stuff **/ - /*! \brief B Channel mISDN driver stack ID */ - int b_stid; - - /* int b_addr; */ - - /*! \brief B Channel mISDN driver layer ID from mISDN_new_layer() */ - int layer_id; - - /*! \brief B channel layer; set to 3 or 4 */ - int layer; - - /* state stuff */ - /*! \brief TRUE if DISCONNECT needs to be sent to clear a call */ - int need_disconnect; - - /*! \brief TRUE if RELEASE needs to be sent to clear a call */ - int need_release; - - /*! \brief TRUE if RELEASE_COMPLETE needs to be sent to clear a call */ - int need_release_complete; - - /*! \brief TRUE if allocate higher B channels first */ - int dec; - - /* var stuff */ - /*! \brief Layer 3 process ID */ - int l3_id; - - /*! \brief B channel process ID (1-5000) */ - int pid; - - /*! \brief Not used. Saved mISDN stack CONNECT_t ces value */ - int ces; - - /*! \brief B channel to restart if received a RESTART message */ - int restart_channel; - - /*! \brief Assigned B channel number B1, B2... 0 if not assigned */ - int channel; - - /*! \brief TRUE if the B channel number is preselected */ - int channel_preselected; - - /*! \brief TRUE if B channel record is in use */ - int in_use; - - /*! \brief Time when empty_bc() last called on this record */ - struct timeval last_used; - - /*! \brief TRUE if call waiting */ - int cw; - - /*! \brief B Channel mISDN driver layer ID from mISDN_get_layerid() */ - int addr; - - /*! \brief B channel speech sample data buffer */ - char *bframe; - - /*! \brief B channel speech sample data buffer size */ - int bframe_len; - int time_usec; /* Not used */ - - /*! \brief Not used. Contents are setup but not used. */ - void *astbuf; - - void *misdnbuf; /* Not used */ - - /*! \brief TRUE if the TE side should choose the B channel to use - * \note This value is user configurable in /etc/asterisk/misdn.conf - */ - int te_choose_channel; - - /*! \brief TRUE if the call progress indicators can indicate an inband audio message for the user to listen to - * \note This value is user configurable in /etc/asterisk/misdn.conf - */ - int early_bconnect; - - /*! \brief Last decoded DTMF digit from mISDN driver */ - int dtmf; - - /*! \brief TRUE if we should produce DTMF tones ourselves - * \note This value is user configurable in /etc/asterisk/misdn.conf - */ - int send_dtmf; - - /*! \brief TRUE if we send SETUP_ACKNOWLEDGE on incoming calls anyway (instead of PROCEEDING). - * - * This requests additional INFORMATION messages, so we can - * wait for digits without issues. - * \note This value is user configurable in /etc/asterisk/misdn.conf - */ - int need_more_infos; - - /*! \brief TRUE if all digits necessary to complete the call are available. - * No more INFORMATION messages are needed. - */ - int sending_complete; - - - /*! \brief TRUE if we will not use jollys dsp */ - int nodsp; - - /*! \brief TRUE if we will not use the jitter buffer system */ - int nojitter; - - /*! \brief Type-of-number in ISDN terms for the dialed/called number - * \note This value is set to "dialplan" in /etc/asterisk/misdn.conf for outgoing calls - */ - enum mISDN_NUMBER_PLAN dnumplan; - - /*! \brief Type-of-number in ISDN terms for the redirecting number which a call diversion or transfer was invoked. - * \note Collected from the incoming SETUP message but not used. - */ - enum mISDN_NUMBER_PLAN rnumplan; - - /*! \brief Type-of-number in ISDN terms for the originating/calling number (Caller-ID) - * \note This value is set to "localdialplan" in /etc/asterisk/misdn.conf for outgoing calls - */ - enum mISDN_NUMBER_PLAN onumplan; - - /*! \brief Type-of-number in ISDN terms for the connected party number - * \note This value is set to "cpndialplan" in /etc/asterisk/misdn.conf for outgoing calls - */ - enum mISDN_NUMBER_PLAN cpnnumplan; - - /*! \brief Progress Indicator IE coding standard field. - * \note Collected from the incoming messages but not used. - */ - int progress_coding; - - /*! \brief Progress Indicator IE location field. - * \note Collected from the incoming messages but not used. - */ - int progress_location; - - /*! \brief Progress Indicator IE progress description field. - * Used to determine if there is an inband audio message present. - */ - int progress_indicator; - - /*! \brief Inbound FACILITY message function type and contents */ - struct FacParm fac_in; - - /*! \brief Outbound FACILITY message function type and contents. - * \note Filled in by misdn facility commands before FACILITY message sent. - */ - struct FacParm fac_out; - - /* storing the current AOCD info here */ - enum FacFunction AOCDtype; - union { - struct FacAOCDCurrency currency; - struct FacAOCDChargingUnit chargingUnit; - } AOCD; - - /*! \brief Event waiting for Layer 1 to come up */ - enum event_e evq; - - /*** CRYPTING STUFF ***/ - int crypt; /* Initialized, Not used */ - int curprx; /* Initialized, Not used */ - int curptx; /* Initialized, Not used */ - - /*! \brief Blowfish encryption key string (secret) */ - char crypt_key[255]; - - int crypt_state; /* Not used */ - /*** CRYPTING STUFF END***/ - - /*! \brief Seems to have been intended for something to do with the jitter buffer. - * \note Used as a boolean. Only initialized to 0 and referenced in a couple places - */ - int active; - int upset; /* Not used */ - - /*! \brief TRUE if tone generator allowed to start */ - int generate_tone; - - /*! \brief Number of tone samples to generate */ - int tone_cnt; - - /*! \brief Current B Channel state */ - enum bchannel_state bc_state; - - /*! \brief This is used as a pending bridge join request for when bc_state becomes BCHAN_ACTIVATED */ - enum bchannel_state next_bc_state; - - /*! \brief Bridging conference ID */ - int conf_id; - - /*! \brief TRUE if this channel is on hold */ - int holded; - - /*! \brief TRUE if this channel is on the misdn_stack->holding list - * \note If TRUE this implies that the structure is also malloced. - */ - int stack_holder; - - /*! \brief Caller ID presentation restriction code - * 0=Allowed, 1=Restricted, 2=Unavailable - * \note It is settable by the misdn_set_opt() application. - */ - int pres; - - /*! \brief Caller ID screening code - * 0=Unscreened, 1=Passed Screen, 2=Failed Screen, 3=Network Number - */ - int screen; - - /*! \brief SETUP message bearer capability field code value */ - int capability; - - /*! \brief Companding ALaw/uLaw encoding (INFO_CODEC_ALAW / INFO_CODEC_ULAW) */ - int law; - - /* V110 Stuff */ - /*! \brief Q.931 Bearer Capability IE Information Transfer Rate field. Initialized to 0x10 (64kbit). Altered by incoming SETUP messages. */ - int rate; - - /*! \brief Q.931 Bearer Capability IE Transfer Mode field. Initialized to 0 (Circuit). Altered by incoming SETUP messages. */ - int mode; - - /*! \brief Q.931 Bearer Capability IE User Information Layer 1 Protocol field code. - * \note Collected from the incoming SETUP message but not used. - */ - int user1; - - /*! \brief Q.931 Bearer Capability IE Layer 1 User Rate field. - * \note Collected from the incoming SETUP message and exported to Asterisk variable MISDN_URATE. - */ - int urate; - - /*! \brief TRUE if call made in digital HDLC mode - * \note This value is user configurable in /etc/asterisk/misdn.conf. - * It is also settable by the misdn_set_opt() application. - */ - int hdlc; - /* V110 */ - - /*! \brief Display message that can be displayed by the user phone. - * \note Maximum displayable length is 34 or 82 octets. - * It is also settable by the misdn_set_opt() application. - */ - char display[84]; - - /*! \brief Not used. Contents are setup but not used. */ - char msn[32]; - - /*! \brief Originating/Calling Phone Number (Address) - * \note This value can be set to "callerid" in /etc/asterisk/misdn.conf for outgoing calls - */ - char oad[32]; - - /*! \brief Redirecting Phone Number (Address) where a call diversion or transfer was invoked */ - char rad[32]; - - /*! \brief Dialed/Called Phone Number (Address) */ - char dad[32]; - - /*! \brief Connected Party/Line Phone Number (Address) */ - char cad[32]; - - /*! \brief Original Dialed/Called Phone Number (Address) before national/international dialing prefix added. - * \note Not used. Contents are setup but not used. - */ - char orig_dad[32]; - - /*! \brief Q.931 Keypad Facility IE contents - * \note Contents exported and imported to Asterisk variable MISDN_KEYPAD - */ - char keypad[32]; - - /*! \brief Current overlap dialing digits to/from INFORMATION messages */ - char info_dad[64]; - - /*! \brief Collected digits to go into info_dad[] while waiting for a SETUP_ACKNOWLEDGE to come in. */ - char infos_pending[64]; - -/* unsigned char info_keypad[32]; */ -/* unsigned char clisub[24]; */ -/* unsigned char cldsub[24]; */ - - /*! \brief User-User information string. - * \note Contents exported and imported to Asterisk variable MISDN_USERUSER - * \note We only support ASCII strings (IA5 characters). - */ - char uu[256]; - - /*! \brief User-User information string length in uu[] */ - int uulen; - - /*! \brief Q.931 Cause for disconnection code (received) - * \note Need to use the AST_CAUSE_xxx code definitions in causes.h - */ - int cause; - - /*! \brief Q.931 Cause for disconnection code (sent) - * \note Need to use the AST_CAUSE_xxx code definitions in causes.h - * \note -1 is used to suppress including the cause code in the RELEASE message. - */ - int out_cause; - - /* struct misdn_bchannel hold_bc; */ - - /** list stuf **/ - -#ifdef MISDN_1_2 - /*! \brief The configuration string for the mISDN dsp pipeline in /etc/asterisk/misdn.conf. */ - char pipeline[128]; -#else - /*! \brief TRUE if the echo cancellor is enabled */ - int ec_enable; - - /*! \brief Number of taps in the echo cancellor when enabled. - * \note This value is user configurable in /etc/asterisk/misdn.conf (echocancel) - */ - int ec_deftaps; -#endif - - /*! \brief TRUE if the channel was allocated from the available B channels */ - int channel_found; - - /*! \brief Who originated the call (ORG_AST, ORG_MISDN) - * \note Set but not used when the misdn_set_opt() application enables echo cancellation. - */ - int orig; - - /*! \brief Tx gain setting (range -8 to 8) - * \note This value is user configurable in /etc/asterisk/misdn.conf. - * It is also settable by the misdn_set_opt() application. - */ - int txgain; - - /*! \brief Rx gain setting (range -8 to 8) - * \note This value is user configurable in /etc/asterisk/misdn.conf. - * It is also settable by the misdn_set_opt() application. - */ - int rxgain; - - /*! \brief Next node in the misdn_stack.holding list */ - struct misdn_bchannel *next; -}; - - -enum event_response_e (*cb_event) (enum event_e event, struct misdn_bchannel *bc, void *user_data); -void (*cb_log) (int level, int port, char *tmpl, ...) - __attribute__ ((format (printf, 3, 4))); -int (*cb_jb_empty)(struct misdn_bchannel *bc, char *buffer, int len); - -struct misdn_lib_iface { - enum event_response_e (*cb_event)(enum event_e event, struct misdn_bchannel *bc, void *user_data); - void (*cb_log)(int level, int port, char *tmpl, ...) - __attribute__ ((format (printf, 3, 4))); - int (*cb_jb_empty)(struct misdn_bchannel *bc, char *buffer, int len); -}; - -/***** USER IFACE **********/ - -void misdn_lib_nt_keepcalls(int kc); - -void misdn_lib_nt_debug_init( int flags, char *file ); - -int misdn_lib_init(char *portlist, struct misdn_lib_iface* iface, void *user_data); -int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ); -void misdn_lib_destroy(void); - -void misdn_lib_isdn_l1watcher(int port); - -void misdn_lib_log_ies(struct misdn_bchannel *bc); - -char *manager_isdn_get_info(enum event_e event); - -void misdn_lib_transfer(struct misdn_bchannel* holded_bc); - -struct misdn_bchannel* misdn_lib_get_free_bc(int port, int channel, int inout, int dec); - -void manager_bchannel_activate(struct misdn_bchannel *bc); -void manager_bchannel_deactivate(struct misdn_bchannel * bc); - -int misdn_lib_tx2misdn_frm(struct misdn_bchannel *bc, void *data, int len); - -void manager_ph_control(struct misdn_bchannel *bc, int c1, int c2); - -void isdn_lib_update_rxgain (struct misdn_bchannel *bc); -void isdn_lib_update_txgain (struct misdn_bchannel *bc); -void isdn_lib_update_ec (struct misdn_bchannel *bc); -void isdn_lib_stop_dtmf (struct misdn_bchannel *bc); - -int misdn_lib_port_restart(int port); -int misdn_lib_pid_restart(int pid); -int misdn_lib_send_restart(int port, int channel); - -int misdn_lib_get_port_info(int port); - -int misdn_lib_is_port_blocked(int port); -int misdn_lib_port_block(int port); -int misdn_lib_port_unblock(int port); - -int misdn_lib_port_is_pri(int port); - -int misdn_lib_port_up(int port, int notcheck); - -int misdn_lib_get_port_down(int port); - -int misdn_lib_get_port_up (int port) ; - -int misdn_lib_maxports_get(void) ; - -void misdn_lib_release(struct misdn_bchannel *bc); - -int misdn_cap_is_speech(int cap); -int misdn_inband_avail(struct misdn_bchannel *bc); - -void manager_ec_enable(struct misdn_bchannel *bc); -void manager_ec_disable(struct misdn_bchannel *bc); - -void misdn_lib_send_tone(struct misdn_bchannel *bc, enum tone_e tone); - -void get_show_stack_details(int port, char *buf); - - -void misdn_lib_tone_generator_start(struct misdn_bchannel *bc); -void misdn_lib_tone_generator_stop(struct misdn_bchannel *bc); - - -void misdn_lib_setup_bc(struct misdn_bchannel *bc); - -void misdn_lib_bridge( struct misdn_bchannel * bc1, struct misdn_bchannel *bc2); -void misdn_lib_split_bridge( struct misdn_bchannel * bc1, struct misdn_bchannel *bc2); - -void misdn_lib_echo(struct misdn_bchannel *bc, int onoff); - -int misdn_lib_is_ptp(int port); -int misdn_lib_get_maxchans(int port); - -void misdn_lib_reinit_nt_stack(int port); - -#define PRI_TRANS_CAP_SPEECH 0x0 -#define PRI_TRANS_CAP_DIGITAL 0x08 -#define PRI_TRANS_CAP_RESTRICTED_DIGITAL 0x09 -#define PRI_TRANS_CAP_3_1K_AUDIO 0x10 -#define PRI_TRANS_CAP_7K_AUDIO 0x11 - - - -char *bc_state2str(enum bchannel_state state); -void bc_state_change(struct misdn_bchannel *bc, enum bchannel_state state); - -void misdn_dump_chanlist(void); - -#endif diff --git a/1.4.23-rc4/channels/misdn/isdn_lib_intern.h b/1.4.23-rc4/channels/misdn/isdn_lib_intern.h deleted file mode 100644 index 93d879744..000000000 --- a/1.4.23-rc4/channels/misdn/isdn_lib_intern.h +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef ISDN_LIB_INTERN -#define ISDN_LIB_INTERN - - -#include <mISDNuser/mISDNlib.h> -#include <mISDNuser/isdn_net.h> -#include <mISDNuser/l3dss1.h> -#include <mISDNuser/net_l3.h> - -#include <pthread.h> - -#include "isdn_lib.h" - -#if !defined MISDNUSER_VERSION_CODE || (MISDNUSER_VERSION_CODE < MISDNUSER_VERSION(1, 0, 3)) -#error "You need a newer version of mISDNuser ..." -#endif - - -#define QI_ELEMENT(a) a.off - - -#ifndef mISDNUSER_HEAD_SIZE - -#define mISDNUSER_HEAD_SIZE (sizeof(mISDNuser_head_t)) -/*#define mISDNUSER_HEAD_SIZE (sizeof(mISDN_head_t))*/ -#endif - - -ibuffer_t *astbuf; /* Not used */ -ibuffer_t *misdnbuf; /* Not used */ - -struct send_lock { - pthread_mutex_t lock; -}; - - -struct isdn_msg { - unsigned long misdn_msg; - - enum layer_e layer; - enum event_e event; - - void (*msg_parser)(struct isdn_msg *msgs, msg_t *msg, struct misdn_bchannel *bc, int nt); - msg_t *(*msg_builder)(struct isdn_msg *msgs, struct misdn_bchannel *bc, int nt); - char *info; -} ; - -/* for isdn_msg_parser.c */ -msg_t *create_l3msg(int prim, int mt, int dinfo , int size, int nt); - - - -struct misdn_stack { - /** is first element because &nst equals &mISDNlist **/ - net_stack_t nst; - manager_t mgr; - - /*! \brief D Channel mISDN driver stack ID (Parent stack ID) */ - int d_stid; - - /*! /brief Number of B channels supported by this port */ - int b_num; - - /*! \brief B Channel mISDN driver stack IDs (Child stack IDs) */ - int b_stids[MAX_BCHANS + 1]; - - /*! \brief TRUE if Point-To-Point(PTP) (Point-To-Multipoint(PTMP) otherwise) */ - int ptp; - - /*! \brief Number of consecutive times PTP Layer 2 declared down */ - int l2upcnt; - - int l2_id; /* Not used */ - - /*! \brief Lower layer mISDN ID (addr) (Layer 1/3) */ - int lower_id; - - /*! \brief Upper layer mISDN ID (addr) (Layer 2/4) */ - int upper_id; - - /*! \brief TRUE if port is blocked */ - int blocked; - - /*! \brief TRUE if Layer 2 is UP */ - int l2link; - - time_t l2establish; /* Not used */ - - /*! \brief TRUE if Layer 1 is UP */ - int l1link; - - /*! \brief TRUE if restart has been sent to the other side after stack startup */ - int restart_sent; - - /*! \brief mISDN device handle returned by mISDN_open() */ - int midev; - - /*! \brief TRUE if NT side of protocol (TE otherwise) */ - int nt; - - /*! \brief TRUE if ISDN-PRI (ISDN-BRI otherwise) */ - int pri; - - /*! \brief CR Process ID allocation table. TRUE if ID allocated */ - int procids[0x100+1]; - - /*! \brief Queue of Event messages to send to mISDN */ - msg_queue_t downqueue; - msg_queue_t upqueue; /* No code puts anything on this queue */ - int busy; /* Not used */ - - /*! \brief Logical Layer 1 port associated with this stack */ - int port; - - /*! \brief B Channel record pool array */ - struct misdn_bchannel bc[MAX_BCHANS + 1]; - - struct misdn_bchannel* bc_list; /* Not used */ - - /*! \brief Array of B channels in use (a[0] = B1). TRUE if B channel in use */ - int channels[MAX_BCHANS + 1]; - - /*! \brief List of holded channels */ - struct misdn_bchannel *holding; - - /*! \brief Next stack in the list of stacks */ - struct misdn_stack *next; -}; - - -struct misdn_stack* get_stack_by_bc(struct misdn_bchannel *bc); - -int isdn_msg_get_index(struct isdn_msg msgs[], msg_t *frm, int nt); -enum event_e isdn_msg_get_event(struct isdn_msg msgs[], msg_t *frm, int nt); -int isdn_msg_parse_event(struct isdn_msg msgs[], msg_t *frm, struct misdn_bchannel *bc, int nt); -char * isdn_get_info(struct isdn_msg msgs[], enum event_e event, int nt); -msg_t * isdn_msg_build_event(struct isdn_msg msgs[], struct misdn_bchannel *bc, enum event_e event, int nt); -int isdn_msg_get_index_by_event(struct isdn_msg msgs[], enum event_e event, int nt); -char * isdn_msg_get_info(struct isdn_msg msgs[], msg_t *msg, int nt); - - -#endif diff --git a/1.4.23-rc4/channels/misdn/isdn_msg_parser.c b/1.4.23-rc4/channels/misdn/isdn_msg_parser.c deleted file mode 100644 index a587f8eae..000000000 --- a/1.4.23-rc4/channels/misdn/isdn_msg_parser.c +++ /dev/null @@ -1,1348 +0,0 @@ -/* - * Chan_Misdn -- Channel Driver for Asterisk - * - * Interface to mISDN - * - * Copyright (C) 2004, Christian Richter - * - * Christian Richter <crich@beronet.com> - * - * This program is free software, distributed under the terms of - * the GNU General Public License - */ - - -#include "isdn_lib_intern.h" - - -#include "isdn_lib.h" - -#include "ie.c" - - -static void set_channel(struct misdn_bchannel *bc, int channel) -{ - - cb_log(3,bc->port,"set_channel: bc->channel:%d channel:%d\n", bc->channel, channel); - - - if (channel==0xff) { - /* any channel */ - channel=-1; - } - - /* ALERT: is that everytime true ? */ - if (channel > 0 && bc->nt ) { - - if (bc->channel && ( bc->channel != 0xff) ) { - cb_log(0,bc->port,"We already have a channel (%d)\n", bc->channel); - } else { - bc->channel = channel; - cb_event(EVENT_NEW_CHANNEL,bc,NULL); - } - } - - if (channel > 0 && !bc->nt ) { - bc->channel = channel; - cb_event(EVENT_NEW_CHANNEL,bc,NULL); - } -} - -static void parse_proceeding (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - CALL_PROCEEDING_t *proceeding=(CALL_PROCEEDING_t*)((unsigned long)msg->data+ HEADER_LEN); - //struct misdn_stack *stack=get_stack_by_bc(bc); - - { - int exclusive, channel; - dec_ie_channel_id(proceeding->CHANNEL_ID, (Q931_info_t *)proceeding, &exclusive, &channel, nt,bc); - - set_channel(bc,channel); - - } - - dec_ie_progress(proceeding->PROGRESS, (Q931_info_t *)proceeding, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); - - -#if DEBUG - printf("Parsing PROCEEDING Msg\n"); -#endif -} -static msg_t *build_proceeding (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - CALL_PROCEEDING_t *proceeding; - msg_t *msg =(msg_t*)create_l3msg(CC_PROCEEDING | REQUEST, MT_CALL_PROCEEDING, bc?bc->l3_id:-1, sizeof(CALL_PROCEEDING_t) ,nt); - - proceeding=(CALL_PROCEEDING_t*)((msg->data+HEADER_LEN)); - - enc_ie_channel_id(&proceeding->CHANNEL_ID, msg, 1,bc->channel, nt,bc); - - if (nt) - enc_ie_progress(&proceeding->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); - - -#if DEBUG - printf("Building PROCEEDING Msg\n"); -#endif - return msg; -} - -static void parse_alerting (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - ALERTING_t *alerting=(ALERTING_t*)((unsigned long)(msg->data+HEADER_LEN)); - //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN); - - dec_ie_progress(alerting->PROGRESS, (Q931_info_t *)alerting, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); - -#if DEBUG - printf("Parsing ALERTING Msg\n"); -#endif - - -} - -static msg_t *build_alerting (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - ALERTING_t *alerting; - msg_t *msg =(msg_t*)create_l3msg(CC_ALERTING | REQUEST, MT_ALERTING, bc?bc->l3_id:-1, sizeof(ALERTING_t) ,nt); - - alerting=(ALERTING_t*)((msg->data+HEADER_LEN)); - - enc_ie_channel_id(&alerting->CHANNEL_ID, msg, 1,bc->channel, nt,bc); - - if (nt) - enc_ie_progress(&alerting->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); -#if DEBUG - printf("Building ALERTING Msg\n"); -#endif - return msg; -} - - -static void parse_progress (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - PROGRESS_t *progress=(PROGRESS_t*)((unsigned long)(msg->data+HEADER_LEN)); - //Q931_info_t *qi=(Q931_info_t*)(msg->data+HEADER_LEN); - - dec_ie_progress(progress->PROGRESS, (Q931_info_t *)progress, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); - -#if DEBUG - printf("Parsing PROGRESS Msg\n"); -#endif -} - -static msg_t *build_progress (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - PROGRESS_t *progress; - msg_t *msg =(msg_t*)create_l3msg(CC_PROGRESS | REQUEST, MT_PROGRESS, bc?bc->l3_id:-1, sizeof(PROGRESS_t) ,nt); - - progress=(PROGRESS_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building PROGRESS Msg\n"); -#endif - return msg; -} - -static void parse_setup (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - SETUP_t *setup= (SETUP_t*)((unsigned long)msg->data+HEADER_LEN); - Q931_info_t *qi=(Q931_info_t*)((unsigned long)msg->data+HEADER_LEN); - -#if DEBUG - printf("Parsing SETUP Msg\n"); -#endif - { - int type,plan,present, screen; - char id[32]; - dec_ie_calling_pn(setup->CALLING_PN, qi, &type, &plan, &present, &screen, id, sizeof(id)-1, nt,bc); - - bc->onumplan=type; - strcpy(bc->oad, id); - switch (present) { - case 0: - bc->pres=0; /* screened */ - break; - case 1: - bc->pres=1; /* not screened */ - break; - default: - bc->pres=0; - } - switch (screen) { - case 0: - break; - default: - ; - } - } - { - int type, plan; - char number[32]; - dec_ie_called_pn(setup->CALLED_PN, (Q931_info_t *)setup, &type, &plan, number, sizeof(number)-1, nt,bc); - strcpy(bc->dad, number); - bc->dnumplan=type; - } - { - char keypad[32]; - dec_ie_keypad(setup->KEYPAD, (Q931_info_t *)setup, keypad, sizeof(keypad)-1, nt,bc); - strcpy(bc->keypad, keypad); - } - - { - dec_ie_complete(setup->COMPLETE, (Q931_info_t *)setup, &bc->sending_complete, nt,bc); - - } - - { - int type, plan, present, screen, reason; - char id[32]; - dec_ie_redir_nr(setup->REDIR_NR, (Q931_info_t *)setup, &type, &plan, &present, &screen, &reason, id, sizeof(id)-1, nt,bc); - - strcpy(bc->rad, id); - bc->rnumplan=type; - } - { - int coding, capability, mode, rate, multi, user, async, urate, stopbits, dbits, parity; - dec_ie_bearer(setup->BEARER, (Q931_info_t *)setup, &coding, &capability, &mode, &rate, &multi, &user, &async, &urate, &stopbits, &dbits, &parity, nt,bc); - switch (capability) { - case -1: bc->capability=INFO_CAPABILITY_DIGITAL_UNRESTRICTED; - break; - case 0: bc->capability=INFO_CAPABILITY_SPEECH; - break; - case 18: bc->capability=INFO_CAPABILITY_VIDEO; - break; - case 8: bc->capability=INFO_CAPABILITY_DIGITAL_UNRESTRICTED; - bc->user1 = user; - bc->urate = urate; - - bc->rate = rate; - bc->mode = mode; - break; - case 9: bc->capability=INFO_CAPABILITY_DIGITAL_RESTRICTED; - break; - default: - break; - } - - switch(user) { - case 2: - bc->law=INFO_CODEC_ULAW; - break; - case 3: - bc->law=INFO_CODEC_ALAW; - break; - default: - bc->law=INFO_CODEC_ALAW; - - } - - bc->capability=capability; - } - { - int exclusive, channel; - dec_ie_channel_id(setup->CHANNEL_ID, (Q931_info_t *)setup, &exclusive, &channel, nt,bc); - - set_channel(bc,channel); - } - - { - int protocol ; - dec_ie_useruser(setup->USER_USER, (Q931_info_t *)setup, &protocol, bc->uu, &bc->uulen, nt,bc); - if (bc->uulen) cb_log(1,bc->port,"USERUESRINFO:%s\n",bc->uu); - else - cb_log(1,bc->port,"NO USERUESRINFO\n"); - } - - dec_ie_progress(setup->PROGRESS, (Q931_info_t *)setup, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); - -} - -#define ANY_CHANNEL 0xff /* IE attribut for 'any channel' */ -static msg_t *build_setup (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - SETUP_t *setup; - msg_t *msg =(msg_t*)create_l3msg(CC_SETUP | REQUEST, MT_SETUP, bc?bc->l3_id:-1, sizeof(SETUP_t) ,nt); - - setup=(SETUP_t*)((msg->data+HEADER_LEN)); - - if (bc->channel == 0 || bc->channel == ANY_CHANNEL || bc->channel==-1) - enc_ie_channel_id(&setup->CHANNEL_ID, msg, 0, bc->channel, nt,bc); - else - enc_ie_channel_id(&setup->CHANNEL_ID, msg, 1, bc->channel, nt,bc); - - - { - int type=bc->onumplan,plan=1,present=bc->pres,screen=bc->screen; - enc_ie_calling_pn(&setup->CALLING_PN, msg, type, plan, present, - screen, bc->oad, nt, bc); - } - - { - if (bc->dad[0]) - enc_ie_called_pn(&setup->CALLED_PN, msg, bc->dnumplan, 1, bc->dad, nt,bc); - } - - { - if (bc->rad[0]) - enc_ie_redir_nr(&setup->REDIR_NR, msg, 1, 1, bc->pres, bc->screen, 0, bc->rad, nt,bc); - } - - { - if (bc->keypad[0]) - enc_ie_keypad(&setup->KEYPAD, msg, bc->keypad, nt,bc); - } - - - if (*bc->display) { - enc_ie_display(&setup->DISPLAY, msg, bc->display, nt,bc); - } - - { - int coding=0, capability, mode=0 /* 2 for packet ! */ - ,user, rate=0x10; - - switch (bc->law) { - case INFO_CODEC_ULAW: user=2; - break; - case INFO_CODEC_ALAW: user=3; - break; - default: - user=3; - } - - switch (bc->capability) { - case INFO_CAPABILITY_SPEECH: capability = 0; - break; - case INFO_CAPABILITY_DIGITAL_UNRESTRICTED: capability = 8; - user=-1; - mode=bc->mode; - rate=bc->rate; - break; - case INFO_CAPABILITY_DIGITAL_RESTRICTED: capability = 9; - user=-1; - break; - default: - capability=bc->capability; - } - - - - enc_ie_bearer(&setup->BEARER, msg, coding, capability, mode, rate, -1, user, nt,bc); - } - - if (bc->sending_complete) { - enc_ie_complete(&setup->COMPLETE,msg, bc->sending_complete, nt, bc); - } - - if (bc->uulen) { - int protocol=4; - enc_ie_useruser(&setup->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); - cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); - } - -#if DEBUG - printf("Building SETUP Msg\n"); -#endif - return msg; -} - -static void parse_connect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - CONNECT_t *connect=(CONNECT_t*)((unsigned long)(msg->data+HEADER_LEN)); - - int plan,pres,screen; - - bc->ces = connect->ces; - bc->ces = connect->ces; - - dec_ie_progress(connect->PROGRESS, (Q931_info_t *)connect, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); - - dec_ie_connected_pn(connect->CONNECT_PN,(Q931_info_t *)connect, &bc->cpnnumplan, &plan, &pres, &screen, bc->cad, 31, nt, bc); - - /* - cb_log(1,bc->port,"CONNETED PN: %s cpn_dialplan:%d\n", connected_pn, type); - */ - -#if DEBUG - printf("Parsing CONNECT Msg\n"); -#endif -} - -static msg_t *build_connect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - CONNECT_t *connect; - msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | REQUEST, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_t) ,nt); - - cb_log(6,bc->port,"BUILD_CONNECT: bc:%p bc->l3id:%d, nt:%d\n",bc,bc->l3_id,nt); - - connect=(CONNECT_t*)((msg->data+HEADER_LEN)); - - if (nt) { - time_t now; - time(&now); - enc_ie_date(&connect->DATE, msg, now, nt,bc); - } - - { - int type=bc->cpnnumplan, plan=1, present=2, screen=0; - enc_ie_connected_pn(&connect->CONNECT_PN, msg, type,plan, present, screen, bc->cad, nt , bc); - } - -#if DEBUG - printf("Building CONNECT Msg\n"); -#endif - return msg; -} - -static void parse_setup_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - SETUP_ACKNOWLEDGE_t *setup_acknowledge=(SETUP_ACKNOWLEDGE_t*)((unsigned long)(msg->data+HEADER_LEN)); - - { - int exclusive, channel; - dec_ie_channel_id(setup_acknowledge->CHANNEL_ID, (Q931_info_t *)setup_acknowledge, &exclusive, &channel, nt,bc); - - - set_channel(bc, channel); - } - - dec_ie_progress(setup_acknowledge->PROGRESS, (Q931_info_t *)setup_acknowledge, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); -#if DEBUG - printf("Parsing SETUP_ACKNOWLEDGE Msg\n"); -#endif - - -} - -static msg_t *build_setup_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - SETUP_ACKNOWLEDGE_t *setup_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_SETUP_ACKNOWLEDGE | REQUEST, MT_SETUP_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SETUP_ACKNOWLEDGE_t) ,nt); - - setup_acknowledge=(SETUP_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); - - enc_ie_channel_id(&setup_acknowledge->CHANNEL_ID, msg, 1,bc->channel, nt,bc); - - if (nt) - enc_ie_progress(&setup_acknowledge->PROGRESS, msg, 0, nt?1:5, 8, nt,bc); - -#if DEBUG - printf("Building SETUP_ACKNOWLEDGE Msg\n"); -#endif - return msg; -} - -static void parse_connect_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing CONNECT_ACKNOWLEDGE Msg\n"); -#endif - - -} - -static msg_t *build_connect_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - CONNECT_ACKNOWLEDGE_t *connect_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_CONNECT | RESPONSE, MT_CONNECT, bc?bc->l3_id:-1, sizeof(CONNECT_ACKNOWLEDGE_t) ,nt); - - connect_acknowledge=(CONNECT_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); - - enc_ie_channel_id(&connect_acknowledge->CHANNEL_ID, msg, 1, bc->channel, nt,bc); - -#if DEBUG - printf("Building CONNECT_ACKNOWLEDGE Msg\n"); -#endif - return msg; -} - -static void parse_user_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing USER_INFORMATION Msg\n"); -#endif - - -} - -static msg_t *build_user_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - USER_INFORMATION_t *user_information; - msg_t *msg =(msg_t*)create_l3msg(CC_USER_INFORMATION | REQUEST, MT_USER_INFORMATION, bc?bc->l3_id:-1, sizeof(USER_INFORMATION_t) ,nt); - - user_information=(USER_INFORMATION_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building USER_INFORMATION Msg\n"); -#endif - return msg; -} - -static void parse_suspend_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing SUSPEND_REJECT Msg\n"); -#endif - - -} - -static msg_t *build_suspend_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - SUSPEND_REJECT_t *suspend_reject; - msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_REJECT | REQUEST, MT_SUSPEND_REJECT, bc?bc->l3_id:-1, sizeof(SUSPEND_REJECT_t) ,nt); - - suspend_reject=(SUSPEND_REJECT_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building SUSPEND_REJECT Msg\n"); -#endif - return msg; -} - -static void parse_resume_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing RESUME_REJECT Msg\n"); -#endif - - -} - -static msg_t *build_resume_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RESUME_REJECT_t *resume_reject; - msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_REJECT | REQUEST, MT_RESUME_REJECT, bc?bc->l3_id:-1, sizeof(RESUME_REJECT_t) ,nt); - - resume_reject=(RESUME_REJECT_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building RESUME_REJECT Msg\n"); -#endif - return msg; -} - -static void parse_hold (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing HOLD Msg\n"); -#endif - - -} - -static msg_t *build_hold (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - HOLD_t *hold; - msg_t *msg =(msg_t*)create_l3msg(CC_HOLD | REQUEST, MT_HOLD, bc?bc->l3_id:-1, sizeof(HOLD_t) ,nt); - - hold=(HOLD_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building HOLD Msg\n"); -#endif - return msg; -} - -static void parse_suspend (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing SUSPEND Msg\n"); -#endif - - -} - -static msg_t *build_suspend (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - SUSPEND_t *suspend; - msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND | REQUEST, MT_SUSPEND, bc?bc->l3_id:-1, sizeof(SUSPEND_t) ,nt); - - suspend=(SUSPEND_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building SUSPEND Msg\n"); -#endif - return msg; -} - -static void parse_resume (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing RESUME Msg\n"); -#endif - - -} - -static msg_t *build_resume (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RESUME_t *resume; - msg_t *msg =(msg_t*)create_l3msg(CC_RESUME | REQUEST, MT_RESUME, bc?bc->l3_id:-1, sizeof(RESUME_t) ,nt); - - resume=(RESUME_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building RESUME Msg\n"); -#endif - return msg; -} - -static void parse_hold_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing HOLD_ACKNOWLEDGE Msg\n"); -#endif - - -} - -static msg_t *build_hold_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - HOLD_ACKNOWLEDGE_t *hold_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_ACKNOWLEDGE | REQUEST, MT_HOLD_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(HOLD_ACKNOWLEDGE_t) ,nt); - - hold_acknowledge=(HOLD_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building HOLD_ACKNOWLEDGE Msg\n"); -#endif - return msg; -} - -static void parse_suspend_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing SUSPEND_ACKNOWLEDGE Msg\n"); -#endif - - -} - -static msg_t *build_suspend_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - SUSPEND_ACKNOWLEDGE_t *suspend_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_SUSPEND_ACKNOWLEDGE | REQUEST, MT_SUSPEND_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(SUSPEND_ACKNOWLEDGE_t) ,nt); - - suspend_acknowledge=(SUSPEND_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building SUSPEND_ACKNOWLEDGE Msg\n"); -#endif - return msg; -} - -static void parse_resume_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing RESUME_ACKNOWLEDGE Msg\n"); -#endif - - -} - -static msg_t *build_resume_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RESUME_ACKNOWLEDGE_t *resume_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_RESUME_ACKNOWLEDGE | REQUEST, MT_RESUME_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RESUME_ACKNOWLEDGE_t) ,nt); - - resume_acknowledge=(RESUME_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building RESUME_ACKNOWLEDGE Msg\n"); -#endif - return msg; -} - -static void parse_hold_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing HOLD_REJECT Msg\n"); -#endif - - -} - -static msg_t *build_hold_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - HOLD_REJECT_t *hold_reject; - msg_t *msg =(msg_t*)create_l3msg(CC_HOLD_REJECT | REQUEST, MT_HOLD_REJECT, bc?bc->l3_id:-1, sizeof(HOLD_REJECT_t) ,nt); - - hold_reject=(HOLD_REJECT_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building HOLD_REJECT Msg\n"); -#endif - return msg; -} - -static void parse_retrieve (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing RETRIEVE Msg\n"); -#endif - - -} - -static msg_t *build_retrieve (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RETRIEVE_t *retrieve; - msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE | REQUEST, MT_RETRIEVE, bc?bc->l3_id:-1, sizeof(RETRIEVE_t) ,nt); - - retrieve=(RETRIEVE_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building RETRIEVE Msg\n"); -#endif - return msg; -} - -static void parse_retrieve_acknowledge (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing RETRIEVE_ACKNOWLEDGE Msg\n"); -#endif - - -} - -static msg_t *build_retrieve_acknowledge (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RETRIEVE_ACKNOWLEDGE_t *retrieve_acknowledge; - msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_ACKNOWLEDGE | REQUEST, MT_RETRIEVE_ACKNOWLEDGE, bc?bc->l3_id:-1, sizeof(RETRIEVE_ACKNOWLEDGE_t) ,nt); - - retrieve_acknowledge=(RETRIEVE_ACKNOWLEDGE_t*)((msg->data+HEADER_LEN)); - - enc_ie_channel_id(&retrieve_acknowledge->CHANNEL_ID, msg, 1, bc->channel, nt,bc); -#if DEBUG - printf("Building RETRIEVE_ACKNOWLEDGE Msg\n"); -#endif - return msg; -} - -static void parse_retrieve_reject (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing RETRIEVE_REJECT Msg\n"); -#endif - - -} - -static msg_t *build_retrieve_reject (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RETRIEVE_REJECT_t *retrieve_reject; - msg_t *msg =(msg_t*)create_l3msg(CC_RETRIEVE_REJECT | REQUEST, MT_RETRIEVE_REJECT, bc?bc->l3_id:-1, sizeof(RETRIEVE_REJECT_t) ,nt); - - retrieve_reject=(RETRIEVE_REJECT_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building RETRIEVE_REJECT Msg\n"); -#endif - return msg; -} - -static void parse_disconnect (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - DISCONNECT_t *disconnect=(DISCONNECT_t*)((unsigned long)(msg->data+HEADER_LEN)); - int location; - int cause; - dec_ie_cause(disconnect->CAUSE, (Q931_info_t *)(disconnect), &location, &cause, nt,bc); - if (cause>0) bc->cause=cause; - - dec_ie_progress(disconnect->PROGRESS, (Q931_info_t *)disconnect, &bc->progress_coding, &bc->progress_location, &bc->progress_indicator, nt, bc); -#if DEBUG - printf("Parsing DISCONNECT Msg\n"); -#endif - - -} - -static msg_t *build_disconnect (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - DISCONNECT_t *disconnect; - msg_t *msg =(msg_t*)create_l3msg(CC_DISCONNECT | REQUEST, MT_DISCONNECT, bc?bc->l3_id:-1, sizeof(DISCONNECT_t) ,nt); - - disconnect=(DISCONNECT_t*)((msg->data+HEADER_LEN)); - - enc_ie_cause(&disconnect->CAUSE, msg, (nt)?1:0, bc->out_cause,nt,bc); - if (nt) enc_ie_progress(&disconnect->PROGRESS, msg, 0, nt?1:5, 8 ,nt,bc); - - if (bc->uulen) { - int protocol=4; - enc_ie_useruser(&disconnect->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); - cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); - } - -#if DEBUG - printf("Building DISCONNECT Msg\n"); -#endif - return msg; -} - -static void parse_restart (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RESTART_t *restart=(RESTART_t*)((unsigned long)(msg->data+HEADER_LEN)); - - struct misdn_stack *stack=get_stack_by_bc(bc); - -#if DEBUG - printf("Parsing RESTART Msg\n"); -#endif - - { - int exclusive; - dec_ie_channel_id(restart->CHANNEL_ID, (Q931_info_t *)restart, &exclusive, &bc->restart_channel, nt,bc); - cb_log(3, stack->port, "CC_RESTART Request on channel:%d on this port.\n", bc->restart_channel); - } - -} - -static msg_t *build_restart (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RESTART_t *restart; - msg_t *msg =(msg_t*)create_l3msg(CC_RESTART | REQUEST, MT_RESTART, bc?bc->l3_id:-1, sizeof(RESTART_t) ,nt); - - restart=(RESTART_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building RESTART Msg\n"); -#endif - - if (bc->channel > 0) { - enc_ie_channel_id(&restart->CHANNEL_ID, msg, 1,bc->channel, nt,bc); - enc_ie_restart_ind(&restart->RESTART_IND, msg, 0x80, nt, bc); - } else { - enc_ie_restart_ind(&restart->RESTART_IND, msg, 0x87, nt, bc); - } - - cb_log(0,bc->port, "Restarting channel %d\n", bc->channel); - - return msg; -} - -static void parse_release (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RELEASE_t *release=(RELEASE_t*)((unsigned long)(msg->data+HEADER_LEN)); - int location; - int cause; - - dec_ie_cause(release->CAUSE, (Q931_info_t *)(release), &location, &cause, nt,bc); - if (cause>0) bc->cause=cause; -#if DEBUG - printf("Parsing RELEASE Msg\n"); -#endif - - -} - -static msg_t *build_release (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RELEASE_t *release; - msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE | REQUEST, MT_RELEASE, bc?bc->l3_id:-1, sizeof(RELEASE_t) ,nt); - - release=(RELEASE_t*)((msg->data+HEADER_LEN)); - - if (bc->out_cause>= 0) - enc_ie_cause(&release->CAUSE, msg, nt?1:0, bc->out_cause, nt,bc); - - if (bc->uulen) { - int protocol=4; - enc_ie_useruser(&release->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); - cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); - } - -#if DEBUG - printf("Building RELEASE Msg\n"); -#endif - return msg; -} - -static void parse_release_complete (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RELEASE_COMPLETE_t *release_complete=(RELEASE_COMPLETE_t*)((unsigned long)(msg->data+HEADER_LEN)); - int location; - int cause; - iframe_t *frm = (iframe_t*) msg->data; - - struct misdn_stack *stack=get_stack_by_bc(bc); - mISDNuser_head_t *hh; - hh=(mISDNuser_head_t*)msg->data; - - /*hh=(mISDN_head_t*)msg->data; - mISDN_head_t *hh;*/ - - if (nt) { - if (hh->prim == (CC_RELEASE_COMPLETE|CONFIRM)) { - cb_log(0, stack->port, "CC_RELEASE_COMPLETE|CONFIRM [NT] \n"); - return; - } - } else { - if (frm->prim == (CC_RELEASE_COMPLETE|CONFIRM)) { - cb_log(0, stack->port, "CC_RELEASE_COMPLETE|CONFIRM [TE] \n"); - return; - } - } - dec_ie_cause(release_complete->CAUSE, (Q931_info_t *)(release_complete), &location, &cause, nt,bc); - if (cause>0) bc->cause=cause; - -#if DEBUG - printf("Parsing RELEASE_COMPLETE Msg\n"); -#endif -} - -static msg_t *build_release_complete (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - RELEASE_COMPLETE_t *release_complete; - msg_t *msg =(msg_t*)create_l3msg(CC_RELEASE_COMPLETE | REQUEST, MT_RELEASE_COMPLETE, bc?bc->l3_id:-1, sizeof(RELEASE_COMPLETE_t) ,nt); - - release_complete=(RELEASE_COMPLETE_t*)((msg->data+HEADER_LEN)); - - enc_ie_cause(&release_complete->CAUSE, msg, nt?1:0, bc->out_cause, nt,bc); - - if (bc->uulen) { - int protocol=4; - enc_ie_useruser(&release_complete->USER_USER, msg, protocol, bc->uu, bc->uulen, nt,bc); - cb_log(1,bc->port,"ENCODING USERUESRINFO:%s\n",bc->uu); - } - -#if DEBUG - printf("Building RELEASE_COMPLETE Msg\n"); -#endif - return msg; -} - -static void parse_facility (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; - FACILITY_t *facility = (FACILITY_t*)(msg->data+HEADER_LEN); - Q931_info_t *qi = (Q931_info_t*)(msg->data+HEADER_LEN); - unsigned char *p = NULL; - int err; - -#if DEBUG - printf("Parsing FACILITY Msg\n"); -#endif - - if (!bc->nt) { - if (qi->QI_ELEMENT(facility)) - p = (unsigned char *)qi + sizeof(Q931_info_t) + qi->QI_ELEMENT(facility) + 1; - } else { - p = facility->FACILITY; - } - if (!p) - return; - - err = decodeFac(p, &(bc->fac_in)); - if (err) { - cb_log(1, bc->port, "Decoding FACILITY failed! (%d)\n", err); - } -} - -static msg_t *build_facility (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int len, - HEADER_LEN = nt ? mISDNUSER_HEAD_SIZE : mISDN_HEADER_LEN; - unsigned char *ie_fac, - fac_tmp[256]; - msg_t *msg =(msg_t*)create_l3msg(CC_FACILITY | REQUEST, MT_FACILITY, bc?bc->l3_id:-1, sizeof(FACILITY_t) ,nt); - FACILITY_t *facility = (FACILITY_t*)(msg->data+HEADER_LEN); - Q931_info_t *qi; - -#if DEBUG - printf("Building FACILITY Msg\n"); -#endif - - len = encodeFac(fac_tmp, &(bc->fac_out)); - if (len <= 0) - return NULL; - - ie_fac = msg_put(msg, len); - if (bc->nt) { - facility->FACILITY = ie_fac + 1; - } else { - qi = (Q931_info_t *)(msg->data + mISDN_HEADER_LEN); - qi->QI_ELEMENT(facility) = ie_fac - (unsigned char *)qi - sizeof(Q931_info_t); - } - - memcpy(ie_fac, fac_tmp, len); - - if (*bc->display) { - printf("Sending %s as Display\n", bc->display); - enc_ie_display(&facility->DISPLAY, msg, bc->display, nt,bc); - } - - return msg; -} - -static void parse_notify (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing NOTIFY Msg\n"); -#endif -} - -static msg_t *build_notify (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - NOTIFY_t *notify; - msg_t *msg =(msg_t*)create_l3msg(CC_NOTIFY | REQUEST, MT_NOTIFY, bc?bc->l3_id:-1, sizeof(NOTIFY_t) ,nt); - - notify=(NOTIFY_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building NOTIFY Msg\n"); -#endif - return msg; -} - -static void parse_status_enquiry (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing STATUS_ENQUIRY Msg\n"); -#endif -} - -static msg_t *build_status_enquiry (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - STATUS_ENQUIRY_t *status_enquiry; - msg_t *msg =(msg_t*)create_l3msg(CC_STATUS_ENQUIRY | REQUEST, MT_STATUS_ENQUIRY, bc?bc->l3_id:-1, sizeof(STATUS_ENQUIRY_t) ,nt); - - status_enquiry=(STATUS_ENQUIRY_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building STATUS_ENQUIRY Msg\n"); -#endif - return msg; -} - -static void parse_information (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - INFORMATION_t *information=(INFORMATION_t*)((unsigned long)(msg->data+HEADER_LEN)); - { - int type, plan; - char number[32]; - char keypad[32]; - dec_ie_called_pn(information->CALLED_PN, (Q931_info_t *)information, &type, &plan, number, sizeof(number)-1, nt, bc); - dec_ie_keypad(information->KEYPAD, (Q931_info_t *)information, keypad, sizeof(keypad)-1, nt, bc); - strcpy(bc->info_dad, number); - strcpy(bc->keypad,keypad); - } -#if DEBUG - printf("Parsing INFORMATION Msg\n"); -#endif -} - -static msg_t *build_information (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - INFORMATION_t *information; - msg_t *msg =(msg_t*)create_l3msg(CC_INFORMATION | REQUEST, MT_INFORMATION, bc?bc->l3_id:-1, sizeof(INFORMATION_t) ,nt); - - information=(INFORMATION_t*)((msg->data+HEADER_LEN)); - - { - enc_ie_called_pn(&information->CALLED_PN, msg, 0, 1, bc->info_dad, nt,bc); - } - - { - if (*bc->display) { - printf("Sending %s as Display\n", bc->display); - enc_ie_display(&information->DISPLAY, msg, bc->display, nt,bc); - } - } - -#if DEBUG - printf("Building INFORMATION Msg\n"); -#endif - return msg; -} - -static void parse_status (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - STATUS_t *status=(STATUS_t*)((unsigned long)(msg->data+HEADER_LEN)); - int location; - int cause; - - dec_ie_cause(status->CAUSE, (Q931_info_t *)(status), &location, &cause, nt,bc); - if (cause>0) bc->cause=cause; - ; - -#if DEBUG - printf("Parsing STATUS Msg\n"); -#endif -} - -static msg_t *build_status (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - STATUS_t *status; - msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt); - - status=(STATUS_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building STATUS Msg\n"); -#endif - return msg; -} - -static void parse_timeout (struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ -#if DEBUG - printf("Parsing STATUS Msg\n"); -#endif -} - -static msg_t *build_timeout (struct isdn_msg msgs[], struct misdn_bchannel *bc, int nt) -{ - int HEADER_LEN = nt?mISDNUSER_HEAD_SIZE:mISDN_HEADER_LEN; - STATUS_t *status; - msg_t *msg =(msg_t*)create_l3msg(CC_STATUS | REQUEST, MT_STATUS, bc?bc->l3_id:-1, sizeof(STATUS_t) ,nt); - - status=(STATUS_t*)((msg->data+HEADER_LEN)); - -#if DEBUG - printf("Building STATUS Msg\n"); -#endif - return msg; -} - - -/************************************/ - - - - -/** Msg Array **/ - -struct isdn_msg msgs_g[] = { - {CC_PROCEEDING,L3,EVENT_PROCEEDING, - parse_proceeding,build_proceeding, - "PROCEEDING"}, - {CC_ALERTING,L3,EVENT_ALERTING, - parse_alerting,build_alerting, - "ALERTING"}, - {CC_PROGRESS,L3,EVENT_PROGRESS, - parse_progress,build_progress, - "PROGRESS"}, - {CC_SETUP,L3,EVENT_SETUP, - parse_setup,build_setup, - "SETUP"}, - {CC_CONNECT,L3,EVENT_CONNECT, - parse_connect,build_connect, - "CONNECT"}, - {CC_SETUP_ACKNOWLEDGE,L3,EVENT_SETUP_ACKNOWLEDGE, - parse_setup_acknowledge,build_setup_acknowledge, - "SETUP_ACKNOWLEDGE"}, - {CC_CONNECT_ACKNOWLEDGE ,L3,EVENT_CONNECT_ACKNOWLEDGE , - parse_connect_acknowledge ,build_connect_acknowledge, - "CONNECT_ACKNOWLEDGE "}, - {CC_USER_INFORMATION,L3,EVENT_USER_INFORMATION, - parse_user_information,build_user_information, - "USER_INFORMATION"}, - {CC_SUSPEND_REJECT,L3,EVENT_SUSPEND_REJECT, - parse_suspend_reject,build_suspend_reject, - "SUSPEND_REJECT"}, - {CC_RESUME_REJECT,L3,EVENT_RESUME_REJECT, - parse_resume_reject,build_resume_reject, - "RESUME_REJECT"}, - {CC_HOLD,L3,EVENT_HOLD, - parse_hold,build_hold, - "HOLD"}, - {CC_SUSPEND,L3,EVENT_SUSPEND, - parse_suspend,build_suspend, - "SUSPEND"}, - {CC_RESUME,L3,EVENT_RESUME, - parse_resume,build_resume, - "RESUME"}, - {CC_HOLD_ACKNOWLEDGE,L3,EVENT_HOLD_ACKNOWLEDGE, - parse_hold_acknowledge,build_hold_acknowledge, - "HOLD_ACKNOWLEDGE"}, - {CC_SUSPEND_ACKNOWLEDGE,L3,EVENT_SUSPEND_ACKNOWLEDGE, - parse_suspend_acknowledge,build_suspend_acknowledge, - "SUSPEND_ACKNOWLEDGE"}, - {CC_RESUME_ACKNOWLEDGE,L3,EVENT_RESUME_ACKNOWLEDGE, - parse_resume_acknowledge,build_resume_acknowledge, - "RESUME_ACKNOWLEDGE"}, - {CC_HOLD_REJECT,L3,EVENT_HOLD_REJECT, - parse_hold_reject,build_hold_reject, - "HOLD_REJECT"}, - {CC_RETRIEVE,L3,EVENT_RETRIEVE, - parse_retrieve,build_retrieve, - "RETRIEVE"}, - {CC_RETRIEVE_ACKNOWLEDGE,L3,EVENT_RETRIEVE_ACKNOWLEDGE, - parse_retrieve_acknowledge,build_retrieve_acknowledge, - "RETRIEVE_ACKNOWLEDGE"}, - {CC_RETRIEVE_REJECT,L3,EVENT_RETRIEVE_REJECT, - parse_retrieve_reject,build_retrieve_reject, - "RETRIEVE_REJECT"}, - {CC_DISCONNECT,L3,EVENT_DISCONNECT, - parse_disconnect,build_disconnect, - "DISCONNECT"}, - {CC_RESTART,L3,EVENT_RESTART, - parse_restart,build_restart, - "RESTART"}, - {CC_RELEASE,L3,EVENT_RELEASE, - parse_release,build_release, - "RELEASE"}, - {CC_RELEASE_COMPLETE,L3,EVENT_RELEASE_COMPLETE, - parse_release_complete,build_release_complete, - "RELEASE_COMPLETE"}, - {CC_FACILITY,L3,EVENT_FACILITY, - parse_facility,build_facility, - "FACILITY"}, - {CC_NOTIFY,L3,EVENT_NOTIFY, - parse_notify,build_notify, - "NOTIFY"}, - {CC_STATUS_ENQUIRY,L3,EVENT_STATUS_ENQUIRY, - parse_status_enquiry,build_status_enquiry, - "STATUS_ENQUIRY"}, - {CC_INFORMATION,L3,EVENT_INFORMATION, - parse_information,build_information, - "INFORMATION"}, - {CC_STATUS,L3,EVENT_STATUS, - parse_status,build_status, - "STATUS"}, - {CC_TIMEOUT,L3,EVENT_TIMEOUT, - parse_timeout,build_timeout, - "TIMEOUT"}, - {0,0,0,NULL,NULL,NULL} -}; - -#define msgs_max (sizeof(msgs_g)/sizeof(struct isdn_msg)) - -/** INTERFACE FCTS ***/ -int isdn_msg_get_index(struct isdn_msg msgs[], msg_t *msg, int nt) -{ - int i; - - if (nt){ - mISDNuser_head_t *hh = (mISDNuser_head_t*)msg->data; - - for (i=0; i< msgs_max -1; i++) { - if ( (hh->prim&COMMAND_MASK)==(msgs[i].misdn_msg&COMMAND_MASK)) return i; - } - - } else { - iframe_t *frm = (iframe_t*)msg->data; - - for (i=0; i< msgs_max -1; i++) - if ( (frm->prim&COMMAND_MASK)==(msgs[i].misdn_msg&COMMAND_MASK)) return i; - } - - return -1; -} - -int isdn_msg_get_index_by_event(struct isdn_msg msgs[], enum event_e event, int nt) -{ - int i; - for (i=0; i< msgs_max; i++) - if ( event == msgs[i].event) return i; - - cb_log(10,0, "get_index: event not found!\n"); - - return -1; -} - -enum event_e isdn_msg_get_event(struct isdn_msg msgs[], msg_t *msg, int nt) -{ - int i=isdn_msg_get_index(msgs, msg, nt); - if(i>=0) return msgs[i].event; - return EVENT_UNKNOWN; -} - -char * isdn_msg_get_info(struct isdn_msg msgs[], msg_t *msg, int nt) -{ - int i=isdn_msg_get_index(msgs, msg, nt); - if(i>=0) return msgs[i].info; - return NULL; -} - - -char EVENT_CLEAN_INFO[] = "CLEAN_UP"; -char EVENT_DTMF_TONE_INFO[] = "DTMF_TONE"; -char EVENT_NEW_L3ID_INFO[] = "NEW_L3ID"; -char EVENT_NEW_BC_INFO[] = "NEW_BC"; -char EVENT_PORT_ALARM_INFO[] = "ALARM"; -char EVENT_NEW_CHANNEL_INFO[] = "NEW_CHANNEL"; -char EVENT_BCHAN_DATA_INFO[] = "BCHAN_DATA"; -char EVENT_BCHAN_ACTIVATED_INFO[] = "BCHAN_ACTIVATED"; -char EVENT_TONE_GENERATE_INFO[] = "TONE_GENERATE"; -char EVENT_BCHAN_ERROR_INFO[] = "BCHAN_ERROR"; - -char * isdn_get_info(struct isdn_msg msgs[], enum event_e event, int nt) -{ - int i=isdn_msg_get_index_by_event(msgs, event, nt); - - if(i>=0) return msgs[i].info; - - if (event == EVENT_CLEANUP) return EVENT_CLEAN_INFO; - if (event == EVENT_DTMF_TONE) return EVENT_DTMF_TONE_INFO; - if (event == EVENT_NEW_L3ID) return EVENT_NEW_L3ID_INFO; - if (event == EVENT_NEW_BC) return EVENT_NEW_BC_INFO; - if (event == EVENT_NEW_CHANNEL) return EVENT_NEW_CHANNEL_INFO; - if (event == EVENT_BCHAN_DATA) return EVENT_BCHAN_DATA_INFO; - if (event == EVENT_BCHAN_ACTIVATED) return EVENT_BCHAN_ACTIVATED_INFO; - if (event == EVENT_TONE_GENERATE) return EVENT_TONE_GENERATE_INFO; - if (event == EVENT_PORT_ALARM) return EVENT_PORT_ALARM_INFO; - if (event == EVENT_BCHAN_ERROR) return EVENT_BCHAN_ERROR_INFO; - - return NULL; -} - -int isdn_msg_parse_event(struct isdn_msg msgs[], msg_t *msg, struct misdn_bchannel *bc, int nt) -{ - int i=isdn_msg_get_index(msgs, msg, nt); - if(i<0) return -1; - - msgs[i].msg_parser(msgs, msg, bc, nt); - return 0; -} - -msg_t * isdn_msg_build_event(struct isdn_msg msgs[], struct misdn_bchannel *bc, enum event_e event, int nt) -{ - int i=isdn_msg_get_index_by_event(msgs, event, nt); - if(i<0) return NULL; - - return msgs[i].msg_builder(msgs, bc, nt); -} diff --git a/1.4.23-rc4/channels/misdn/portinfo.c b/1.4.23-rc4/channels/misdn/portinfo.c deleted file mode 100644 index bcb9f0313..000000000 --- a/1.4.23-rc4/channels/misdn/portinfo.c +++ /dev/null @@ -1,198 +0,0 @@ - - -#include "isdn_lib.h" -#include "isdn_lib_intern.h" - - -/* - * global function to show all available isdn ports - */ -void isdn_port_info(void) -{ - int err; - int i, ii, p; - int useable, nt, pri; - unsigned char buff[1025]; - iframe_t *frm = (iframe_t *)buff; - stack_info_t *stinf; - int device; - - /* open mISDN */ - if ((device = mISDN_open()) < 0) - { - fprintf(stderr, "mISDN_open() failed: ret=%d errno=%d (%s) Check for mISDN modules and device.\n", device, errno, strerror(errno)); - exit(-1); - } - - /* get number of stacks */ - i = 1; - ii = mISDN_get_stack_count(device); - printf("\n"); - if (ii <= 0) - { - printf("Found no card. Please be sure to load card drivers.\n"); - } - - /* loop the number of cards and get their info */ - while(i <= ii) - { - err = mISDN_get_stack_info(device, i, buff, sizeof(buff)); - if (err <= 0) - { - fprintf(stderr, "mISDN_get_stack_info() failed: port=%d err=%d\n", i, err); - break; - } - stinf = (stack_info_t *)&frm->data.p; - - nt = pri = 0; - useable = 1; - - /* output the port info */ - printf("Port %2d: ", i); - switch(stinf->pid.protocol[0] & ~ISDN_PID_FEATURE_MASK) - { - case ISDN_PID_L0_TE_S0: - printf("TE-mode BRI S/T interface line (for phone lines)"); -#if 0 - if (stinf->pid.protocol[0] & ISDN_PID_L0_TE_S0_HFC & ISDN_PID_FEATURE_MASK) - printf(" HFC multiport card"); -#endif - break; - case ISDN_PID_L0_NT_S0: - nt = 1; - printf("NT-mode BRI S/T interface port (for phones)"); -#if 0 - if (stinf->pid.protocol[0] & ISDN_PID_L0_NT_S0_HFC & ISDN_PID_FEATURE_MASK) - printf(" HFC multiport card"); -#endif - break; - case ISDN_PID_L0_TE_U: - printf("TE-mode BRI U interface line"); - break; - case ISDN_PID_L0_NT_U: - nt = 1; - printf("NT-mode BRI U interface port"); - break; - case ISDN_PID_L0_TE_UP2: - printf("TE-mode BRI Up2 interface line"); - break; - case ISDN_PID_L0_NT_UP2: - nt = 1; - printf("NT-mode BRI Up2 interface port"); - break; - case ISDN_PID_L0_TE_E1: - pri = 1; - printf("TE-mode PRI E1 interface line (for phone lines)"); -#if 0 - if (stinf->pid.protocol[0] & ISDN_PID_L0_TE_E1_HFC & ISDN_PID_FEATURE_MASK) - printf(" HFC-E1 card"); -#endif - break; - case ISDN_PID_L0_NT_E1: - nt = 1; - pri = 1; - printf("NT-mode PRI E1 interface port (for phones)"); -#if 0 - if (stinf->pid.protocol[0] & ISDN_PID_L0_NT_E1_HFC & ISDN_PID_FEATURE_MASK) - printf(" HFC-E1 card"); -#endif - break; - default: - useable = 0; - printf("unknown type 0x%08x",stinf->pid.protocol[0]); - } - printf("\n"); - - if (nt) - { - if (stinf->pid.protocol[1] == 0) - { - useable = 0; - printf(" -> Missing layer 1 NT-mode protocol.\n"); - } - p = 2; - while(p <= MAX_LAYER_NR) { - if (stinf->pid.protocol[p]) - { - useable = 0; - printf(" -> Layer %d protocol 0x%08x is detected, but not allowed for NT lib.\n", p, stinf->pid.protocol[p]); - } - p++; - } - if (useable) - { - if (pri) - printf(" -> Interface is Point-To-Point (PRI).\n"); - else - printf(" -> Interface can be Poin-To-Point/Multipoint.\n"); - } - } else - { - if (stinf->pid.protocol[1] == 0) - { - useable = 0; - printf(" -> Missing layer 1 protocol.\n"); - } - if (stinf->pid.protocol[2] == 0) - { - useable = 0; - printf(" -> Missing layer 2 protocol.\n"); - } - if (stinf->pid.protocol[2] & ISDN_PID_L2_DF_PTP) - { - printf(" -> Interface is Poin-To-Point.\n"); - } - if (stinf->pid.protocol[3] == 0) - { - useable = 0; - printf(" -> Missing layer 3 protocol.\n"); - } else - { - printf(" -> Protocol: "); - switch(stinf->pid.protocol[3] & ~ISDN_PID_FEATURE_MASK) - { - case ISDN_PID_L3_DSS1USER: - printf("DSS1 (Euro ISDN)"); - break; - - default: - useable = 0; - printf("unknown protocol 0x%08x",stinf->pid.protocol[3]); - } - printf("\n"); - } - p = 4; - while(p <= MAX_LAYER_NR) { - if (stinf->pid.protocol[p]) - { - useable = 0; - printf(" -> Layer %d protocol 0x%08x is detected, but not allowed for TE lib.\n", p, stinf->pid.protocol[p]); - } - p++; - } - printf(" -> childcnt: %d\n",stinf->childcnt); - } - - if (!useable) - printf(" * Port NOT useable for PBX\n"); - - printf("--------\n"); - - i++; - } - printf("\n"); - - /* close mISDN */ - if ((err = mISDN_close(device))) - { - fprintf(stderr, "mISDN_close() failed: err=%d '%s'\n", err, strerror(err)); - exit(-1); - } -} - - -int main() -{ - isdn_port_info(); - return 0; -} diff --git a/1.4.23-rc4/channels/misdn_config.c b/1.4.23-rc4/channels/misdn_config.c deleted file mode 100644 index 0924ad983..000000000 --- a/1.4.23-rc4/channels/misdn_config.c +++ /dev/null @@ -1,1160 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2005, Christian Richter - * - * Christian Richter <crich@beronet.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - * - */ - -/*! - * \file - * - * \brief chan_misdn configuration management - * \author Christian Richter <crich@beronet.com> - * - * \ingroup channel_drivers - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> - -#include "chan_misdn_config.h" - -#include "asterisk/config.h" -#include "asterisk/channel.h" -#include "asterisk/logger.h" -#include "asterisk/lock.h" -#include "asterisk/pbx.h" -#include "asterisk/strings.h" -#include "asterisk/utils.h" - -#define AST_LOAD_CFG ast_config_load -#define AST_DESTROY_CFG ast_config_destroy - -#define NO_DEFAULT "<>" -#define NONE 0 - -#define GEN_CFG 1 -#define PORT_CFG 2 -#define NUM_GEN_ELEMENTS (sizeof(gen_spec) / sizeof(struct misdn_cfg_spec)) -#define NUM_PORT_ELEMENTS (sizeof(port_spec) / sizeof(struct misdn_cfg_spec)) - -enum misdn_cfg_type { - MISDN_CTYPE_STR, - MISDN_CTYPE_INT, - MISDN_CTYPE_BOOL, - MISDN_CTYPE_BOOLINT, - MISDN_CTYPE_MSNLIST, - MISDN_CTYPE_ASTGROUP -}; - -struct msn_list { - char *msn; - struct msn_list *next; -}; - -union misdn_cfg_pt { - char *str; - int *num; - struct msn_list *ml; - ast_group_t *grp; - void *any; -}; - -struct misdn_cfg_spec { - char name[BUFFERSIZE]; - enum misdn_cfg_elements elem; - enum misdn_cfg_type type; - char def[BUFFERSIZE]; - int boolint_def; - char desc[BUFFERSIZE]; -}; - - -static const char ports_description[] = - "Define your ports, e.g. 1,2 (depends on mISDN-driver loading order)."; - -static const struct misdn_cfg_spec port_spec[] = { - { "name", MISDN_CFG_GROUPNAME, MISDN_CTYPE_STR, "default", NONE, - "Name of the portgroup." }, - { "allowed_bearers", MISDN_CFG_ALLOWED_BEARERS, MISDN_CTYPE_STR, "all", NONE, - "Here you can list which bearer capabilities should be allowed:\n" - "\t all - allow any bearer capability\n" - "\t speech - allow speech\n" - "\t 3_1khz - allow 3.1KHz audio\n" - "\t digital_unrestricted - allow unrestricted digital\n" - "\t digital_restricted - allow restricted digital\n" - "\t video - allow video" }, - { "rxgain", MISDN_CFG_RXGAIN, MISDN_CTYPE_INT, "0", NONE, - "Set this between -8 and 8 to change the RX Gain." }, - { "txgain", MISDN_CFG_TXGAIN, MISDN_CTYPE_INT, "0", NONE, - "Set this between -8 and 8 to change the TX Gain." }, - { "te_choose_channel", MISDN_CFG_TE_CHOOSE_CHANNEL, MISDN_CTYPE_BOOL, "no", NONE, - "Some telcos especially in NL seem to need this set to yes,\n" - "\talso in Switzerland this seems to be important." }, - { "far_alerting", MISDN_CFG_FAR_ALERTING, MISDN_CTYPE_BOOL, "no", NONE, - "If we should generate ringing for chan_sip and others." }, - { "pmp_l1_check", MISDN_CFG_PMP_L1_CHECK, MISDN_CTYPE_BOOL, "no", NONE, - "This option defines, if chan_misdn should check the L1 on a PMP\n" - "\tbefore making a group call on it. The L1 may go down for PMP Ports\n" - "\tso we might need this.\n" - "\tBut be aware! a broken or plugged off cable might be used for a group call\n" - "\tas well, since chan_misdn has no chance to distinguish if the L1 is down\n" - "\tbecause of a lost Link or because the Provider shut it down..." }, - { "block_on_alarm", MISDN_CFG_ALARM_BLOCK, MISDN_CTYPE_BOOL, "no", NONE , - "Block this port if we have an alarm on it." }, - { "hdlc", MISDN_CFG_HDLC, MISDN_CTYPE_BOOL, "no", NONE, - "Set this to yes, if you want to bridge a mISDN data channel to\n" - "\tanother channel type or to an application." }, - { "context", MISDN_CFG_CONTEXT, MISDN_CTYPE_STR, "default", NONE, - "Context to use for incoming calls." }, - { "language", MISDN_CFG_LANGUAGE, MISDN_CTYPE_STR, "en", NONE, - "Language." }, - { "musicclass", MISDN_CFG_MUSICCLASS, MISDN_CTYPE_STR, "default", NONE, - "Sets the musiconhold class." }, - { "callerid", MISDN_CFG_CALLERID, MISDN_CTYPE_STR, "", NONE, - "Sets the caller ID." }, - { "method", MISDN_CFG_METHOD, MISDN_CTYPE_STR, "standard", NONE, - "Sets the method to use for channel selection:\n" - "\t standard - always choose the first free channel with the lowest number\n" - "\t round_robin - use the round robin algorithm to select a channel. use this\n" - "\t if you want to balance your load." }, - { "dialplan", MISDN_CFG_DIALPLAN, MISDN_CTYPE_INT, "0", NONE, - "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n" - "\n" - "\tThere are different types of the dialplan:\n" - "\n" - "\tdialplan -> outgoing Number\n" - "\tlocaldialplan -> callerid\n" - "\tcpndialplan -> connected party number\n" - "\n" - "\tdialplan options:\n" - "\n" - "\t0 - unknown\n" - "\t1 - International\n" - "\t2 - National\n" - "\t4 - Subscriber\n" - "\n" - "\tThis setting is used for outgoing calls." }, - { "localdialplan", MISDN_CFG_LOCALDIALPLAN, MISDN_CTYPE_INT, "0", NONE, - "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n" - "\n" - "\tThere are different types of the dialplan:\n" - "\n" - "\tdialplan -> outgoing Number\n" - "\tlocaldialplan -> callerid\n" - "\tcpndialplan -> connected party number\n" - "\n" - "\tdialplan options:\n" - "\n" - "\t0 - unknown\n" - "\t1 - International\n" - "\t2 - National\n" - "\t4 - Subscriber\n" - "\n" - "\tThis setting is used for outgoing calls." }, - { "cpndialplan", MISDN_CFG_CPNDIALPLAN, MISDN_CTYPE_INT, "0", NONE, - "Dialplan means Type Of Number in ISDN Terms (for outgoing calls)\n" - "\n" - "\tThere are different types of the dialplan:\n" - "\n" - "\tdialplan -> outgoing Number\n" - "\tlocaldialplan -> callerid\n" - "\tcpndialplan -> connected party number\n" - "\n" - "\tdialplan options:\n" - "\n" - "\t0 - unknown\n" - "\t1 - International\n" - "\t2 - National\n" - "\t4 - Subscriber\n" - "\n" - "\tThis setting is used for outgoing calls." }, - { "nationalprefix", MISDN_CFG_NATPREFIX, MISDN_CTYPE_STR, "0", NONE, - "Prefix for national, this is put before the\n" - "\toad if an according dialplan is set by the other end." }, - { "internationalprefix", MISDN_CFG_INTERNATPREFIX, MISDN_CTYPE_STR, "00", NONE, - "Prefix for international, this is put before the\n" - "\toad if an according dialplan is set by the other end." }, - { "presentation", MISDN_CFG_PRES, MISDN_CTYPE_INT, "-1", NONE, - "These (presentation and screen) are the exact isdn screening and presentation\n" - "\tindicators.\n" - "\tIf -1 is given for either value, the presentation indicators are used from\n" - "\tAsterisk's SetCallerPres application.\n" - "\n" - "\tscreen=0, presentation=0 -> callerid presented\n" - "\tscreen=1, presentation=1 -> callerid restricted (the remote end doesn't see it!)" }, - { "screen", MISDN_CFG_SCREEN, MISDN_CTYPE_INT, "-1", NONE, - "These (presentation and screen) are the exact isdn screening and presentation\n" - "\tindicators.\n" - "\tIf -1 is given for either value, the presentation indicators are used from\n" - "\tAsterisk's SetCallerPres application.\n" - "\n" - "\tscreen=0, presentation=0 -> callerid presented\n" - "\tscreen=1, presentation=1 -> callerid restricted (the remote end doesn't see it!)" }, - { "always_immediate", MISDN_CFG_ALWAYS_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE, - "Enable this to get into the s dialplan-extension.\n" - "\tThere you can use DigitTimeout if you can't or don't want to use\n" - "\tisdn overlap dial.\n" - "\tNOTE: This will jump into the s extension for every exten!" }, - { "nodialtone", MISDN_CFG_NODIALTONE, MISDN_CTYPE_BOOL, "no", NONE, - "Enable this to prevent chan_misdn to generate the dialtone\n" - "\tThis makes only sense together with the always_immediate=yes option\n" - "\tto generate your own dialtone with Playtones or so."}, - { "immediate", MISDN_CFG_IMMEDIATE, MISDN_CTYPE_BOOL, "no", NONE, - "Enable this if you want callers which called exactly the base\n" - "\tnumber (so no extension is set) to jump into the s extension.\n" - "\tIf the user dials something more, it jumps to the correct extension\n" - "\tinstead." }, - { "senddtmf", MISDN_CFG_SENDDTMF, MISDN_CTYPE_BOOL, "no", NONE, - "Enable this if we should produce DTMF Tones ourselves." }, - { "astdtmf", MISDN_CFG_ASTDTMF, MISDN_CTYPE_BOOL, "no", NONE, - "Enable this if you want to use the Asterisk dtmf detector\n" - "instead of the mISDN_dsp/hfcmulti one." - }, - { "hold_allowed", MISDN_CFG_HOLD_ALLOWED, MISDN_CTYPE_BOOL, "no", NONE, - "Enable this to have support for hold and retrieve." }, - { "early_bconnect", MISDN_CFG_EARLY_BCONNECT, MISDN_CTYPE_BOOL, "yes", NONE, - "Disable this if you don't mind correct handling of Progress Indicators." }, - { "incoming_early_audio", MISDN_CFG_INCOMING_EARLY_AUDIO, MISDN_CTYPE_BOOL, "no", NONE, - "Turn this on if you like to send Tone Indications to a Incoming\n" - "\tisdn channel on a TE Port. Rarely used, only if the Telco allows\n" - "\tyou to send indications by yourself, normally the Telco sends the\n" - "\tindications to the remote party." }, - { "echocancel", MISDN_CFG_ECHOCANCEL, MISDN_CTYPE_BOOLINT, "0", 128, - "This enables echo cancellation with the given number of taps.\n" - "\tBe aware: Move this setting only to outgoing portgroups!\n" - "\tA value of zero turns echo cancellation off.\n" - "\n" - "\tPossible values are: 0,32,64,128,256,yes(=128),no(=0)" }, -#ifdef MISDN_1_2 - { "pipeline", MISDN_CFG_PIPELINE, MISDN_CTYPE_STR, NO_DEFAULT, NONE, - "Set the configuration string for the mISDN dsp pipeline.\n" - "\n" - "\tExample for enabling the mg2 echo cancellation module with deftaps\n" - "\tset to 128:\n" - "\t\tmg2ec(deftaps=128)" }, -#endif -#ifdef WITH_BEROEC - { "bnechocancel", MISDN_CFG_BNECHOCANCEL, MISDN_CTYPE_BOOLINT, "yes", 64, - "echotail in ms (1-200)\n"}, - { "bnec_antihowl", MISDN_CFG_BNEC_ANTIHOWL, MISDN_CTYPE_INT, "0", NONE, - "Use antihowl\n"}, - { "bnec_nlp", MISDN_CFG_BNEC_NLP, MISDN_CTYPE_BOOL, "yes", NONE, - "Nonlinear Processing (much faster adaption)"}, - { "bnec_zerocoeff", MISDN_CFG_BNEC_ZEROCOEFF, MISDN_CTYPE_BOOL, "no", NONE, - "ZeroCoeffeciens\n"}, - { "bnec_tonedisabler", MISDN_CFG_BNEC_TD, MISDN_CTYPE_BOOL, "no", NONE, - "Disable Tone\n"}, - { "bnec_adaption", MISDN_CFG_BNEC_ADAPT, MISDN_CTYPE_INT, "1", NONE, - "Adaption mode (0=no,1=full,2=fast)\n"}, -#endif - { "need_more_infos", MISDN_CFG_NEED_MORE_INFOS, MISDN_CTYPE_BOOL, "0", NONE, - "Send Setup_Acknowledge on incoming calls anyway (instead of PROCEEDING),\n" - "\tthis requests additional Infos, so we can waitfordigits without much\n" - "\tissues. This works only for PTP Ports" }, - { "noautorespond_on_setup", MISDN_CFG_NOAUTORESPOND_ON_SETUP, MISDN_CTYPE_BOOL, "0", NONE, - "Do not send SETUP_ACKNOWLEDGE or PROCEEDING automatically to the calling Party.\n" - "Instead we directly jump into the dialplan. This might be useful for fast call\n" - "rejection, or for some broken switches, that need hangup causes like busy in the.\n" - "RELEASE_COMPLETE Message, instead of the DISCONNECT Message."}, - { "jitterbuffer", MISDN_CFG_JITTERBUFFER, MISDN_CTYPE_INT, "4000", NONE, - "The jitterbuffer." }, - { "jitterbuffer_upper_threshold", MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD, MISDN_CTYPE_INT, "0", NONE, - "Change this threshold to enable dejitter functionality." }, - { "callgroup", MISDN_CFG_CALLGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE, - "Callgroup." }, - { "pickupgroup", MISDN_CFG_PICKUPGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE, - "Pickupgroup." }, - { "max_incoming", MISDN_CFG_MAX_IN, MISDN_CTYPE_INT, "-1", NONE, - "Defines the maximum amount of incoming calls per port for this group.\n" - "\tCalls which exceed the maximum will be marked with the channel variable\n" - "\tMAX_OVERFLOW. It will contain the amount of overflowed calls" }, - { "max_outgoing", MISDN_CFG_MAX_OUT, MISDN_CTYPE_INT, "-1", NONE, - "Defines the maximum amount of outgoing calls per port for this group\n" - "\texceeding calls will be rejected" }, - - { "reject_cause", MISDN_CFG_REJECT_CAUSE, MISDN_CTYPE_INT, "21", NONE, - "Defines the cause with which a 3. call is rejected on PTMP BRI."}, - { "faxdetect", MISDN_CFG_FAXDETECT, MISDN_CTYPE_STR, "no", NONE, - "Setup fax detection:\n" - "\t no - no fax detection\n" - "\t incoming - fax detection for incoming calls\n" - "\t outgoing - fax detection for outgoing calls\n" - "\t both - fax detection for incoming and outgoing calls\n" - "\tAdd +nojump to your value (i.e. faxdetect=both+nojump) if you don't want to jump into the\n" - "\tfax-extension but still want to detect the fax and prepare the channel for fax transfer." }, - { "faxdetect_timeout", MISDN_CFG_FAXDETECT_TIMEOUT, MISDN_CTYPE_INT, "5", NONE, - "Number of seconds the fax detection should do its job. After the given period of time,\n" - "\twe assume that it's not a fax call and save some CPU time by turning off fax detection.\n" - "\tSet this to 0 if you don't want a timeout (never stop detecting)." }, - { "faxdetect_context", MISDN_CFG_FAXDETECT_CONTEXT, MISDN_CTYPE_STR, NO_DEFAULT, NONE, - "Context to jump into if we detect a fax. Don't set this if you want to stay in the current context." }, - { "l1watcher_timeout", MISDN_CFG_L1_TIMEOUT, MISDN_CTYPE_BOOLINT, "0", 4, - "Watches the layer 1. If the layer 1 is down, it tries to\n" - "\tget it up. The timeout is given in seconds. with 0 as value it\n" - "\tdoes not watch the l1 at all\n" - "\n" - "\tThis option is only read at loading time of chan_misdn, which\n" - "\tmeans you need to unload and load chan_misdn to change the value,\n" - "\tan Asterisk restart should do the trick." }, - { "overlapdial", MISDN_CFG_OVERLAP_DIAL, MISDN_CTYPE_BOOLINT, "0", 4, - "Enables overlap dial for the given amount of seconds.\n" - "\tPossible values are positive integers or:\n" - "\t yes (= 4 seconds)\n" - "\t no (= 0 seconds = disabled)" }, - { "nttimeout", MISDN_CFG_NTTIMEOUT, MISDN_CTYPE_BOOL, "no", NONE , - "Set this to yes if you want calls disconnected in overlap mode" - "when a timeout happens."}, - { "bridging", MISDN_CFG_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE, - "Set this to yes/no, default is yes.\n" - "This can be used to have bridging enabled in general and to\n" - "disable it for specific ports. It makes sense to disable\n" - "bridging on NT Port where you plan to use the HOLD/RETRIEVE\n" - "features with ISDN phones." }, - { "msns", MISDN_CFG_MSNS, MISDN_CTYPE_MSNLIST, "*", NONE, - "MSN's for TE ports, listen on those numbers on the above ports, and\n" - "\tindicate the incoming calls to Asterisk.\n" - "\tHere you can give a comma separated list, or simply an '*' for any msn." }, -}; - -static const struct misdn_cfg_spec gen_spec[] = { - { "debug", MISDN_GEN_DEBUG, MISDN_CTYPE_INT, "0", NONE, - "Sets the debugging flag:\n" - "\t0 - No Debug\n" - "\t1 - mISDN Messages and * - Messages, and * - State changes\n" - "\t2 - Messages + Message specific Informations (e.g. bearer capability)\n" - "\t3 - very Verbose, the above + lots of Driver specific infos\n" - "\t4 - even more Verbose than 3" }, -#ifndef MISDN_1_2 - { "misdn_init", MISDN_GEN_MISDN_INIT, MISDN_CTYPE_STR, "/etc/misdn-init.conf", NONE, - "Set the path to the misdn-init.conf (for nt_ptp mode checking)." }, -#endif - { "tracefile", MISDN_GEN_TRACEFILE, MISDN_CTYPE_STR, "/var/log/asterisk/misdn.log", NONE, - "Set the path to the massively growing trace file, if you want that." }, - { "bridging", MISDN_GEN_BRIDGING, MISDN_CTYPE_BOOL, "yes", NONE, - "Set this to yes if you want mISDN_dsp to bridge the calls in HW." }, - { "stop_tone_after_first_digit", MISDN_GEN_STOP_TONE, MISDN_CTYPE_BOOL, "yes", NONE, - "Stops dialtone after getting first digit on NT Port." }, - { "append_digits2exten", MISDN_GEN_APPEND_DIGITS2EXTEN, MISDN_CTYPE_BOOL, "yes", NONE, - "Whether to append overlapdialed Digits to Extension or not." }, - { "dynamic_crypt", MISDN_GEN_DYNAMIC_CRYPT, MISDN_CTYPE_BOOL, "no", NONE, - "Whether to look out for dynamic crypting attempts." }, - { "crypt_prefix", MISDN_GEN_CRYPT_PREFIX, MISDN_CTYPE_STR, NO_DEFAULT, NONE, - "What is used for crypting Protocol." }, - { "crypt_keys", MISDN_GEN_CRYPT_KEYS, MISDN_CTYPE_STR, NO_DEFAULT, NONE, - "Keys for cryption, you reference them in the dialplan\n" - "\tLater also in dynamic encr." }, - { "ntkeepcalls", MISDN_GEN_NTKEEPCALLS, MISDN_CTYPE_BOOL, "no", NONE, - "avoid dropping calls if the L2 goes down. some Nortel pbx\n" - "do put down the L2/L1 for some milliseconds even if there\n" - "are running calls. with this option you can avoid dropping them" }, - { "ntdebugflags", MISDN_GEN_NTDEBUGFLAGS, MISDN_CTYPE_INT, "0", NONE, - "No description yet."}, - { "ntdebugfile", MISDN_GEN_NTDEBUGFILE, MISDN_CTYPE_STR, "/var/log/misdn-nt.log", NONE, - "No description yet." } -}; - - -/* array of port configs, default is at position 0. */ -static union misdn_cfg_pt **port_cfg; -/* max number of available ports, is set on init */ -static int max_ports; -/* general config */ -static union misdn_cfg_pt *general_cfg; -/* storing the ptp flag separated to save memory */ -static int *ptp; -/* maps enum config elements to array positions */ -static int *map; - -static ast_mutex_t config_mutex; - -#define CLI_ERROR(name, value, section) ({ \ - ast_log(LOG_WARNING, "misdn.conf: \"%s=%s\" (section: %s) invalid or out of range. " \ - "Please edit your misdn.conf and then do a \"misdn reload\".\n", name, value, section); \ -}) - -static int _enum_array_map (void) -{ - int i, j, ok; - - for (i = MISDN_CFG_FIRST + 1; i < MISDN_CFG_LAST; ++i) { - if (i == MISDN_CFG_PTP) - continue; - ok = 0; - for (j = 0; j < NUM_PORT_ELEMENTS; ++j) { - if (port_spec[j].elem == i) { - map[i] = j; - ok = 1; - break; - } - } - if (!ok) { - ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (port section) has no corresponding element in the config struct!\n", i); - return -1; - } - } - for (i = MISDN_GEN_FIRST + 1; i < MISDN_GEN_LAST; ++i) { - ok = 0; - for (j = 0; j < NUM_GEN_ELEMENTS; ++j) { - if (gen_spec[j].elem == i) { - map[i] = j; - ok = 1; - break; - } - } - if (!ok) { - ast_log(LOG_WARNING, "Enum element %d in misdn_cfg_elements (general section) has no corresponding element in the config struct!\n", i); - return -1; - } - } - return 0; -} - -static int get_cfg_position (char *name, int type) -{ - int i; - - switch (type) { - case PORT_CFG: - for (i = 0; i < NUM_PORT_ELEMENTS; ++i) { - if (!strcasecmp(name, port_spec[i].name)) - return i; - } - break; - case GEN_CFG: - for (i = 0; i < NUM_GEN_ELEMENTS; ++i) { - if (!strcasecmp(name, gen_spec[i].name)) - return i; - } - } - - return -1; -} - -static inline void misdn_cfg_lock (void) -{ - ast_mutex_lock(&config_mutex); -} - -static inline void misdn_cfg_unlock (void) -{ - ast_mutex_unlock(&config_mutex); -} - -static void _free_msn_list (struct msn_list* iter) -{ - if (iter->next) - _free_msn_list(iter->next); - if (iter->msn) - free(iter->msn); - free(iter); -} - -static void _free_port_cfg (void) -{ - int i, j; - int gn = map[MISDN_CFG_GROUPNAME]; - union misdn_cfg_pt* free_list[max_ports + 2]; - - memset(free_list, 0, sizeof(free_list)); - free_list[0] = port_cfg[0]; - for (i = 1; i <= max_ports; ++i) { - if (port_cfg[i][gn].str) { - /* we always have a groupname in the non-default case, so this is fine */ - for (j = 1; j <= max_ports; ++j) { - if (free_list[j] && free_list[j][gn].str == port_cfg[i][gn].str) - break; - else if (!free_list[j]) { - free_list[j] = port_cfg[i]; - break; - } - } - } - } - for (j = 0; free_list[j]; ++j) { - for (i = 0; i < NUM_PORT_ELEMENTS; ++i) { - if (free_list[j][i].any) { - if (port_spec[i].type == MISDN_CTYPE_MSNLIST) - _free_msn_list(free_list[j][i].ml); - else - free(free_list[j][i].any); - } - } - } -} - -static void _free_general_cfg (void) -{ - int i; - - for (i = 0; i < NUM_GEN_ELEMENTS; i++) - if (general_cfg[i].any) - free(general_cfg[i].any); -} - -void misdn_cfg_get (int port, enum misdn_cfg_elements elem, void *buf, int bufsize) -{ - int place; - - if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) { - memset(buf, 0, bufsize); - ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Port number %d is not valid.\n", port); - return; - } - - misdn_cfg_lock(); - if (elem == MISDN_CFG_PTP) { - if (!memcpy(buf, &ptp[port], (bufsize > ptp[port]) ? sizeof(ptp[port]) : bufsize)) - memset(buf, 0, bufsize); - } else { - if ((place = map[elem]) < 0) { - memset (buf, 0, bufsize); - ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get! Invalid element (%d) requested.\n", elem); - } else { - if (elem < MISDN_CFG_LAST) { - switch (port_spec[place].type) { - case MISDN_CTYPE_STR: - if (port_cfg[port][place].str) { - if (!memccpy(buf, port_cfg[port][place].str, 0, bufsize)) - memset(buf, 0, 1); - } else if (port_cfg[0][place].str) { - if (!memccpy(buf, port_cfg[0][place].str, 0, bufsize)) - memset(buf, 0, 1); - } else - memset(buf, 0, bufsize); - break; - default: - if (port_cfg[port][place].any) - memcpy(buf, port_cfg[port][place].any, bufsize); - else if (port_cfg[0][place].any) - memcpy(buf, port_cfg[0][place].any, bufsize); - else - memset(buf, 0, bufsize); - } - } else { - switch (gen_spec[place].type) { - case MISDN_CTYPE_STR: - if (!general_cfg[place].str || !memccpy(buf, general_cfg[place].str, 0, bufsize)) - memset(buf, 0, 1); - break; - default: - if (general_cfg[place].any) - memcpy(buf, general_cfg[place].any, bufsize); - else - memset(buf, 0, bufsize); - } - } - } - } - misdn_cfg_unlock(); -} - -enum misdn_cfg_elements misdn_cfg_get_elem (char *name) -{ - int pos; - - /* here comes a hack to replace the (not existing) "name" element with the "ports" element */ - if (!strcmp(name, "ports")) - return MISDN_CFG_GROUPNAME; - if (!strcmp(name, "name")) - return MISDN_CFG_FIRST; - - pos = get_cfg_position (name, PORT_CFG); - if (pos >= 0) - return port_spec[pos].elem; - - pos = get_cfg_position (name, GEN_CFG); - if (pos >= 0) - return gen_spec[pos].elem; - - return MISDN_CFG_FIRST; -} - -void misdn_cfg_get_name (enum misdn_cfg_elements elem, void *buf, int bufsize) -{ - struct misdn_cfg_spec *spec = NULL; - int place = map[elem]; - - /* the ptp hack */ - if (elem == MISDN_CFG_PTP) { - memset(buf, 0, 1); - return; - } - - /* here comes a hack to replace the (not existing) "name" element with the "ports" element */ - if (elem == MISDN_CFG_GROUPNAME) { - if (!snprintf(buf, bufsize, "ports")) - memset(buf, 0, 1); - return; - } - - if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST)) - spec = (struct misdn_cfg_spec *)port_spec; - else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST)) - spec = (struct misdn_cfg_spec *)gen_spec; - - if (!spec || !memccpy(buf, spec[place].name, 0, bufsize)) - memset(buf, 0, 1); -} - -void misdn_cfg_get_desc (enum misdn_cfg_elements elem, void *buf, int bufsize, void *buf_default, int bufsize_default) -{ - int place = map[elem]; - struct misdn_cfg_spec *spec = NULL; - - /* here comes a hack to replace the (not existing) "name" element with the "ports" element */ - if (elem == MISDN_CFG_GROUPNAME) { - if (!memccpy(buf, ports_description, 0, bufsize)) - memset(buf, 0, 1); - if (buf_default && bufsize_default) - memset(buf_default, 0, 1); - return; - } - - if ((elem > MISDN_CFG_FIRST) && (elem < MISDN_CFG_LAST)) - spec = (struct misdn_cfg_spec *)port_spec; - else if ((elem > MISDN_GEN_FIRST) && (elem < MISDN_GEN_LAST)) - spec = (struct misdn_cfg_spec *)gen_spec; - - if (!spec || !spec[place].desc) - memset(buf, 0, 1); - else { - if (!memccpy(buf, spec[place].desc, 0, bufsize)) - memset(buf, 0, 1); - if (buf_default && bufsize) { - if (!strcmp(spec[place].def, NO_DEFAULT)) - memset(buf_default, 0, 1); - else if (!memccpy(buf_default, spec[place].def, 0, bufsize_default)) - memset(buf_default, 0, 1); - } - } -} - -int misdn_cfg_is_msn_valid (int port, char* msn) -{ - int re = 0; - struct msn_list *iter; - - if (!misdn_cfg_is_port_valid(port)) { - ast_log(LOG_WARNING, "Invalid call to misdn_cfg_is_msn_valid! Port number %d is not valid.\n", port); - return 0; - } - - misdn_cfg_lock(); - if (port_cfg[port][map[MISDN_CFG_MSNS]].ml) - iter = port_cfg[port][map[MISDN_CFG_MSNS]].ml; - else - iter = port_cfg[0][map[MISDN_CFG_MSNS]].ml; - for (; iter; iter = iter->next) - if (*(iter->msn) == '*' || ast_extension_match(iter->msn, msn)) { - re = 1; - break; - } - misdn_cfg_unlock(); - - return re; -} - -int misdn_cfg_is_port_valid (int port) -{ - int gn = map[MISDN_CFG_GROUPNAME]; - - return (port >= 1 && port <= max_ports && port_cfg[port][gn].str); -} - -int misdn_cfg_is_group_method (char *group, enum misdn_cfg_method meth) -{ - int i, re = 0; - char *method ; - - misdn_cfg_lock(); - - method = port_cfg[0][map[MISDN_CFG_METHOD]].str; - - for (i = 1; i <= max_ports; i++) { - if (port_cfg[i] && port_cfg[i][map[MISDN_CFG_GROUPNAME]].str) { - if (!strcasecmp(port_cfg[i][map[MISDN_CFG_GROUPNAME]].str, group)) - method = (port_cfg[i][map[MISDN_CFG_METHOD]].str ? - port_cfg[i][map[MISDN_CFG_METHOD]].str : port_cfg[0][map[MISDN_CFG_METHOD]].str); - } - } - - if (method) { - switch (meth) { - case METHOD_STANDARD: re = !strcasecmp(method, "standard"); - break; - case METHOD_ROUND_ROBIN: re = !strcasecmp(method, "round_robin"); - break; - case METHOD_STANDARD_DEC: re = !strcasecmp(method, "standard_dec"); - break; - } - } - misdn_cfg_unlock(); - - return re; -} - -/*! - * \brief Generate a comma separated list of all active ports - */ -void misdn_cfg_get_ports_string (char *ports) -{ - char tmp[16]; - int l, i; - int gn = map[MISDN_CFG_GROUPNAME]; - - *ports = 0; - - misdn_cfg_lock(); - for (i = 1; i <= max_ports; i++) { - if (port_cfg[i][gn].str) { - if (ptp[i]) - sprintf(tmp, "%dptp,", i); - else - sprintf(tmp, "%d,", i); - strcat(ports, tmp); - } - } - misdn_cfg_unlock(); - - if ((l = strlen(ports))) { - /* Strip trailing ',' */ - ports[l-1] = 0; - } -} - -void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char* buf, int bufsize) -{ - int place; - char tempbuf[BUFFERSIZE] = ""; - struct msn_list *iter; - - if ((elem < MISDN_CFG_LAST) && !misdn_cfg_is_port_valid(port)) { - *buf = 0; - ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Port number %d is not valid.\n", port); - return; - } - - place = map[elem]; - - misdn_cfg_lock(); - if (elem == MISDN_CFG_PTP) { - snprintf(buf, bufsize, " -> ptp: %s", ptp[port] ? "yes" : "no"); - } - else if (elem > MISDN_CFG_FIRST && elem < MISDN_CFG_LAST) { - switch (port_spec[place].type) { - case MISDN_CTYPE_INT: - case MISDN_CTYPE_BOOLINT: - if (port_cfg[port][place].num) - snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[port][place].num); - else if (port_cfg[0][place].num) - snprintf(buf, bufsize, " -> %s: %d", port_spec[place].name, *port_cfg[0][place].num); - else - snprintf(buf, bufsize, " -> %s:", port_spec[place].name); - break; - case MISDN_CTYPE_BOOL: - if (port_cfg[port][place].num) - snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[port][place].num ? "yes" : "no"); - else if (port_cfg[0][place].num) - snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, *port_cfg[0][place].num ? "yes" : "no"); - else - snprintf(buf, bufsize, " -> %s:", port_spec[place].name); - break; - case MISDN_CTYPE_ASTGROUP: - if (port_cfg[port][place].grp) - snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, - ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[port][place].grp)); - else if (port_cfg[0][place].grp) - snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, - ast_print_group(tempbuf, sizeof(tempbuf), *port_cfg[0][place].grp)); - else - snprintf(buf, bufsize, " -> %s:", port_spec[place].name); - break; - case MISDN_CTYPE_MSNLIST: - if (port_cfg[port][place].ml) - iter = port_cfg[port][place].ml; - else - iter = port_cfg[0][place].ml; - if (iter) { - for (; iter; iter = iter->next) { - strncat(tempbuf, iter->msn, sizeof(tempbuf) - strlen(tempbuf) - 1); - } - tempbuf[strlen(tempbuf)-2] = 0; - } - snprintf(buf, bufsize, " -> msns: %s", *tempbuf ? tempbuf : "none"); - break; - case MISDN_CTYPE_STR: - if ( port_cfg[port][place].str) { - snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[port][place].str); - } else if (port_cfg[0][place].str) { - snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name, port_cfg[0][place].str); - } else { - snprintf(buf, bufsize, " -> %s:", port_spec[place].name); - } - break; - } - } else if (elem > MISDN_GEN_FIRST && elem < MISDN_GEN_LAST) { - switch (gen_spec[place].type) { - case MISDN_CTYPE_INT: - case MISDN_CTYPE_BOOLINT: - if (general_cfg[place].num) - snprintf(buf, bufsize, " -> %s: %d", gen_spec[place].name, *general_cfg[place].num); - else - snprintf(buf, bufsize, " -> %s:", gen_spec[place].name); - break; - case MISDN_CTYPE_BOOL: - if (general_cfg[place].num) - snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, *general_cfg[place].num ? "yes" : "no"); - else - snprintf(buf, bufsize, " -> %s:", gen_spec[place].name); - break; - case MISDN_CTYPE_STR: - if ( general_cfg[place].str) { - snprintf(buf, bufsize, " -> %s: %s", gen_spec[place].name, general_cfg[place].str); - } else { - snprintf(buf, bufsize, " -> %s:", gen_spec[place].name); - } - break; - default: - snprintf(buf, bufsize, " -> type of %s not handled yet", gen_spec[place].name); - break; - } - } else { - *buf = 0; - ast_log(LOG_WARNING, "Invalid call to misdn_cfg_get_config_string! Invalid config element (%d) requested.\n", elem); - } - misdn_cfg_unlock(); -} - -int misdn_cfg_get_next_port (int port) -{ - int p = -1; - int gn = map[MISDN_CFG_GROUPNAME]; - - misdn_cfg_lock(); - for (port++; port <= max_ports; port++) { - if (port_cfg[port][gn].str) { - p = port; - break; - } - } - misdn_cfg_unlock(); - - return p; -} - -int misdn_cfg_get_next_port_spin (int port) -{ - int p = misdn_cfg_get_next_port(port); - return (p > 0) ? p : misdn_cfg_get_next_port(0); -} - -static int _parse (union misdn_cfg_pt *dest, char *value, enum misdn_cfg_type type, int boolint_def) -{ - int re = 0; - int len, tmp; - char *valtmp; - - switch (type) { - case MISDN_CTYPE_STR: - if ((len = strlen(value))) { - dest->str = (char *)malloc((len + 1) * sizeof(char)); - strncpy(dest->str, value, len); - dest->str[len] = 0; - } else { - dest->str = (char *)malloc( sizeof(char)); - dest->str[0] = 0; - } - break; - case MISDN_CTYPE_INT: - { - char *pat; - if (strchr(value,'x')) - pat="%x"; - else - pat="%d"; - if (sscanf(value, pat, &tmp)) { - dest->num = (int *)malloc(sizeof(int)); - memcpy(dest->num, &tmp, sizeof(int)); - } else - re = -1; - } - break; - case MISDN_CTYPE_BOOL: - dest->num = (int *)malloc(sizeof(int)); - *(dest->num) = (ast_true(value) ? 1 : 0); - break; - case MISDN_CTYPE_BOOLINT: - dest->num = (int *)malloc(sizeof(int)); - if (sscanf(value, "%d", &tmp)) { - memcpy(dest->num, &tmp, sizeof(int)); - } else { - *(dest->num) = (ast_true(value) ? boolint_def : 0); - } - break; - case MISDN_CTYPE_MSNLIST: - for (valtmp = strsep(&value, ","); valtmp; valtmp = strsep(&value, ",")) { - if ((len = strlen(valtmp))) { - struct msn_list *ml = (struct msn_list *)malloc(sizeof(struct msn_list)); - ml->msn = (char *)calloc(len+1, sizeof(char)); - strncpy(ml->msn, valtmp, len); - ml->next = dest->ml; - dest->ml = ml; - } - } - break; - case MISDN_CTYPE_ASTGROUP: - dest->grp = (ast_group_t *)malloc(sizeof(ast_group_t)); - *(dest->grp) = ast_get_group(value); - break; - } - - return re; -} - -static void _build_general_config (struct ast_variable *v) -{ - int pos; - - for (; v; v = v->next) { - if (((pos = get_cfg_position(v->name, GEN_CFG)) < 0) || - (_parse(&general_cfg[pos], v->value, gen_spec[pos].type, gen_spec[pos].boolint_def) < 0)) - CLI_ERROR(v->name, v->value, "general"); - } -} - -static void _build_port_config (struct ast_variable *v, char *cat) -{ - int pos, i; - union misdn_cfg_pt cfg_tmp[NUM_PORT_ELEMENTS]; - int cfg_for_ports[max_ports + 1]; - - if (!v || !cat) - return; - - memset(cfg_tmp, 0, sizeof(cfg_tmp)); - memset(cfg_for_ports, 0, sizeof(cfg_for_ports)); - - if (!strcasecmp(cat, "default")) { - cfg_for_ports[0] = 1; - } - - if (((pos = get_cfg_position("name", PORT_CFG)) < 0) || - (_parse(&cfg_tmp[pos], cat, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) { - CLI_ERROR(v->name, v->value, cat); - return; - } - - for (; v; v = v->next) { - if (!strcasecmp(v->name, "ports")) { - char *token; - char ptpbuf[BUFFERSIZE] = ""; - int start, end; - for (token = strsep(&v->value, ","); token; token = strsep(&v->value, ","), *ptpbuf = 0) { - if (!*token) - continue; - if (sscanf(token, "%d-%d%s", &start, &end, ptpbuf) >= 2) { - for (; start <= end; start++) { - if (start <= max_ports && start > 0) { - cfg_for_ports[start] = 1; - ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0; - } else - CLI_ERROR(v->name, v->value, cat); - } - } else { - if (sscanf(token, "%d%s", &start, ptpbuf)) { - if (start <= max_ports && start > 0) { - cfg_for_ports[start] = 1; - ptp[start] = (strstr(ptpbuf, "ptp")) ? 1 : 0; - } else - CLI_ERROR(v->name, v->value, cat); - } else - CLI_ERROR(v->name, v->value, cat); - } - } - } else { - if (((pos = get_cfg_position(v->name, PORT_CFG)) < 0) || - (_parse(&cfg_tmp[pos], v->value, port_spec[pos].type, port_spec[pos].boolint_def) < 0)) - CLI_ERROR(v->name, v->value, cat); - } - } - - for (i = 0; i < (max_ports + 1); ++i) { - if (cfg_for_ports[i]) { - memcpy(port_cfg[i], cfg_tmp, sizeof(cfg_tmp)); - } - } -} - -void misdn_cfg_update_ptp (void) -{ -#ifndef MISDN_1_2 - char misdn_init[BUFFERSIZE]; - char line[BUFFERSIZE]; - FILE *fp; - char *tok, *p, *end; - int port; - - misdn_cfg_get(0, MISDN_GEN_MISDN_INIT, &misdn_init, sizeof(misdn_init)); - - if (!ast_strlen_zero(misdn_init)) { - fp = fopen(misdn_init, "r"); - if (fp) { - while(fgets(line, sizeof(line), fp)) { - if (!strncmp(line, "nt_ptp", 6)) { - for (tok = strtok_r(line,",=", &p); - tok; - tok = strtok_r(NULL,",=", &p)) { - port = strtol(tok, &end, 10); - if (end != tok && misdn_cfg_is_port_valid(port)) { - misdn_cfg_lock(); - ptp[port] = 1; - misdn_cfg_unlock(); - } - } - } - } - fclose(fp); - } else { - ast_log(LOG_WARNING,"Couldn't open %s: %s\n", misdn_init, strerror(errno)); - } - } -#else - int i; - int proto; - char filename[128]; - FILE *fp; - - for (i = 1; i <= max_ports; ++i) { - snprintf(filename, sizeof(filename), "/sys/class/mISDN-stacks/st-%08x/protocol", i << 8); - fp = fopen(filename, "r"); - if (!fp) { - ast_log(LOG_WARNING, "Could not open %s: %s\n", filename, strerror(errno)); - continue; - } - if (fscanf(fp, "0x%08x", &proto) != 1) - ast_log(LOG_WARNING, "Could not parse contents of %s!\n", filename); - else - ptp[i] = proto & 1<<5 ? 1 : 0; - fclose(fp); - } -#endif -} - -static void _fill_defaults (void) -{ - int i; - - for (i = 0; i < NUM_PORT_ELEMENTS; ++i) { - if (!port_cfg[0][i].any && strcasecmp(port_spec[i].def, NO_DEFAULT)) - _parse(&(port_cfg[0][i]), (char *)port_spec[i].def, port_spec[i].type, port_spec[i].boolint_def); - } - for (i = 0; i < NUM_GEN_ELEMENTS; ++i) { - if (!general_cfg[i].any && strcasecmp(gen_spec[i].def, NO_DEFAULT)) - _parse(&(general_cfg[i]), (char *)gen_spec[i].def, gen_spec[i].type, gen_spec[i].boolint_def); - } -} - -void misdn_cfg_reload (void) -{ - misdn_cfg_init (0); -} - -void misdn_cfg_destroy (void) -{ - misdn_cfg_lock(); - - _free_port_cfg(); - _free_general_cfg(); - - free(port_cfg); - free(general_cfg); - free(ptp); - free(map); - - misdn_cfg_unlock(); - ast_mutex_destroy(&config_mutex); -} - -int misdn_cfg_init (int this_max_ports) -{ - char config[] = "misdn.conf"; - char *cat, *p; - int i; - struct ast_config *cfg; - struct ast_variable *v; - - if (!(cfg = AST_LOAD_CFG(config))) { - ast_log(LOG_WARNING, "missing file: misdn.conf\n"); - return -1; - } - - ast_mutex_init(&config_mutex); - - misdn_cfg_lock(); - - if (this_max_ports) { - /* this is the first run */ - max_ports = this_max_ports; - map = (int *)calloc(MISDN_GEN_LAST + 1, sizeof(int)); - if (_enum_array_map()) - return -1; - p = (char *)calloc(1, (max_ports + 1) * sizeof(union misdn_cfg_pt *) - + (max_ports + 1) * NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt)); - port_cfg = (union misdn_cfg_pt **)p; - p += (max_ports + 1) * sizeof(union misdn_cfg_pt *); - for (i = 0; i <= max_ports; ++i) { - port_cfg[i] = (union misdn_cfg_pt *)p; - p += NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt); - } - general_cfg = (union misdn_cfg_pt *)calloc(1, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS); - ptp = (int *)calloc(max_ports + 1, sizeof(int)); - } - else { - /* misdn reload */ - _free_port_cfg(); - _free_general_cfg(); - memset(port_cfg[0], 0, NUM_PORT_ELEMENTS * sizeof(union misdn_cfg_pt) * (max_ports + 1)); - memset(general_cfg, 0, sizeof(union misdn_cfg_pt *) * NUM_GEN_ELEMENTS); - memset(ptp, 0, sizeof(int) * (max_ports + 1)); - } - - cat = ast_category_browse(cfg, NULL); - - while(cat) { - v = ast_variable_browse(cfg, cat); - if (!strcasecmp(cat, "general")) { - _build_general_config(v); - } else { - _build_port_config(v, cat); - } - cat = ast_category_browse(cfg, cat); - } - - _fill_defaults(); - - misdn_cfg_unlock(); - AST_DESTROY_CFG(cfg); - - return 0; -} - - diff --git a/1.4.23-rc4/channels/ring10.h b/1.4.23-rc4/channels/ring10.h deleted file mode 100644 index f45f8dbb9..000000000 --- a/1.4.23-rc4/channels/ring10.h +++ /dev/null @@ -1,1752 +0,0 @@ -/*! \file - * \brief Signed 16-bit audio data - * - * Source: /home/markster/ring10.raw - * - * Copyright (C) 1999-2005, Digium, Inc. - * - * Distributed under the terms of the GNU General Public License - * - */ - -static signed short ring10[] = { -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 0x82a0, 0x8dc7, 0x607e, 0xc0c6, 0x2bd3, -0x8df5, 0x828d, 0x0716, 0xaca6, 0xcefe, 0x41df, 0xd185, 0x8aa3, 0x8b1a, 0x789b, -0x82a2, 0x804f, 0x7ea8, 0x8113, 0x7f7d, 0x7fff, 0x801e, 0x801d, 0x7f32, 0x82ec, -0x83e1, 0x7fb0, 0x7f71, 0x80de, 0x7f3d, 0x7fe3, 0x81b4, 0x7c37, 0x8553, 0x7b29, -0x7ede, 0xde6e, 0x0e64, 0xf9f4, 0x015e, 0x00f6, 0xfe56, 0x0019, 0xf8bb, 0xfd90, -0x08cc, 0x05ab, 0xfd0b, 0xf9c6, 0xf875, 0xf789, 0xfc74, 0x032e, 0xf97a, 0xf4bb, -0x0212, 0x006e, 0x03df, 0x17c5, 0x0f50, 0xfb23, 0xfdbd, 0xf7cf, 0xdf5b, 0xe2d3, -0xf111, 0xef27, 0x11c5, 0x33a4, 0x168d, 0x0145, 0x0494, 0xe85c, 0xdac3, 0xf0c7, -0xeea8, 0x0023, 0x3036, 0x252a, 0xffb7, 0x01d1, 0xf637, 0xd506, 0xe8eb, 0xf5ff, -0xe5ca, 0x1ec5, 0x3fa4, 0x0e3c, 0x1570, 0x2b37, 0xea23, 0xca43, 0xf392, 0xdf0e, -0xde40, 0x2e7c, 0x276f, 0x035c, 0x2ccc, 0x1acf, 0xcf4a, 0xeb5b, 0x0fb1, 0xe01a, -0x0c69, 0x3a97, 0xfb54, 0x0751, 0x20f1, 0xdce9, 0xd2a2, 0x19b3, 0x096f, 0xf1b6, -0x38de, 0x1f70, 0xf32b, 0x2569, 0x0650, 0xc3d7, 0xf1ad, 0x1aa5, 0xe87e, 0x0c7f, -0x406d, 0xffaa, 0x0ba8, 0x2e02, 0xe545, 0xcebb, 0x10fc, 0x0102, 0xded8, 0x2b7c, -0x2053, 0xec6f, 0x266e, 0x1770, 0xcb63, 0xf18e, 0x2015, 0xe6ef, 0xfe64, 0x3700, -0xf628, 0xfb00, 0x2e43, 0xee48, 0xcd4a, 0x1867, 0x0ec3, 0xdd77, 0x2291, 0x1c80, -0xe325, 0x19b7, 0x1719, 0xcb88, 0xeded, 0x258c, 0xe7e8, 0xf0c6, 0x2d21, 0xf3d5, -0xf494, 0x290d, 0xef7b, 0xca28, 0x12c8, 0x0d8d, 0xd5f3, 0x171d, 0x1994, 0xe0c0, -0x1348, 0x1929, 0xcf9b, 0xe6fb, 0x20ae, 0xe921, 0xed2b, 0x2c54, 0xf96e, 0xf19f, -0x21b6, 0xf12e, 0xc8b4, 0x0907, 0x0964, 0xd049, 0x0eb8, 0x1fa6, 0xe6b5, 0x0cec, -0x16b6, 0xcd0c, 0xda57, 0x17c9, 0xe440, 0xe2a2, 0x2b4d, 0xffa2, 0xec7e, 0x1ee9, -0xf674, 0xbfcb, 0xf769, 0x0402, 0xcfe8, 0x104b, 0x2734, 0xe7e9, 0x07d9, 0x19f4, -0xd032, 0xd00b, 0x0e46, 0xe17d, 0xe2d8, 0x3456, 0x0781, 0xed01, 0x238d, 0xfa72, -0xbb51, 0xf543, 0x050b, 0xccd5, 0x1491, 0x3358, 0xedad, 0x10c4, 0x283b, 0xd051, -0xc9e9, 0x11f8, 0xe2cb, 0xe534, 0x43aa, 0x1090, 0xf11b, 0x3267, 0x02c3, 0xb72d, -0xf9ac, 0x0fbd, 0xce45, 0x1d7b, 0x4389, 0xef2e, 0x1593, 0x348e, 0xd0cb, 0xca8c, -0x1f61, 0xe981, 0xdef7, 0x4774, 0x15ae, 0xefab, 0x3b28, 0x0a9e, 0xb2f6, 0xf9e9, -0x1976, 0xcc08, 0x15ab, 0x4534, 0xee6c, 0x159b, 0x3753, 0xcf09, 0xc69a, 0x2270, -0xf15c, 0xdee6, 0x48ce, 0x1af4, 0xf169, 0x3da0, 0x0d68, 0xb573, 0xff9e, 0x20ba, -0xcbfe, 0x142d, 0x4879, 0xed49, 0x1434, 0x3d96, 0xd714, 0xca99, 0x298b, 0xf708, -0xd92c, 0x4632, 0x1acc, 0xea6e, 0x3d2c, 0x1412, 0xb534, 0xfbfa, 0x24f9, 0xcd72, -0x0df9, 0x48f8, 0xeb87, 0x0bca, 0x3dd5, 0xd6cc, 0xc015, 0x2605, 0xfa87, 0xd1a9, -0x40d0, 0x1c59, 0xe0de, 0x34f9, 0x14c6, 0xaf61, 0xf2a5, 0x23e6, 0xc929, 0x01be, -0x4423, 0xe53b, 0x0182, 0x3c3a, 0xd758, 0xbb9d, 0x1fa9, 0xf454, 0xc611, 0x36e8, -0x18f7, 0xdac9, 0x2e8a, 0x126d, 0xac14, 0xead6, 0x2215, 0xc990, 0xf9f5, 0x43cb, -0xea01, 0xfcbf, 0x38fd, 0xd9f3, 0xb7cd, 0x1bc4, 0xfd41, 0xca56, 0x31e3, 0x1d4b, -0xdca2, 0x2a9f, 0x1c24, 0xb8aa, 0xeb59, 0x25d5, 0xd2d0, 0xfa10, 0x44fa, 0xefe0, -0xfced, 0x3ef4, 0xe9a1, 0xbdf0, 0x19ac, 0x0198, 0xca6f, 0x2f04, 0x25b6, 0xe187, -0x29ba, 0x250a, 0xbe42, 0xe40e, 0x24ef, 0xd75d, 0xf476, 0x44f8, 0xf719, 0xf7a1, -0x3c94, 0xf20e, 0xbcdf, 0x16a3, 0x07e8, 0xc8d4, 0x2a3e, 0x2b3f, 0xdf4e, 0x235d, -0x2c92, 0xc2c7, 0xdf39, 0x2873, 0xd790, 0xea2a, 0x47fd, 0xfd0e, 0xf0e3, 0x3bd8, -0xf4e9, 0xb265, 0x0c2c, 0x0751, 0xc302, 0x29bb, 0x37bd, 0xe138, 0x1e0c, 0x2d09, -0xbddb, 0xd246, 0x24c4, 0xd87a, 0xe5df, 0x4ff6, 0x08d6, 0xf0d8, 0x3d61, 0xf8bf, -0xaede, 0x0a36, 0x0df3, 0xc0f5, 0x23ec, 0x3e92, 0xe3d7, 0x1cad, 0x348e, 0xc0d6, -0xcd4e, 0x265c, 0xd9b6, 0xdf83, 0x510e, 0x0c41, 0xeece, 0x4153, 0xfeeb, 0xa9f6, -0x04b3, 0x12a4, 0xbf2f, 0x20d1, 0x42f4, 0xe1b1, 0x1b1e, 0x3980, 0xc2b4, 0xcb50, -0x2b74, 0xded0, 0xd835, 0x4e7a, 0x0b46, 0xe555, 0x4015, 0x0517, 0xaa54, 0x0504, -0x1932, 0xbc34, 0x1a77, 0x48b1, 0xe0bb, 0x149b, 0x3ba7, 0xc34a, 0xc481, 0x2bc2, -0xe401, 0xd20e, 0x4f53, 0x1389, 0xe3b7, 0x418b, 0x0a15, 0xa70d, 0x0024, 0x1f9f, -0xbf65, 0x142d, 0x4a81, 0xe0ca, 0x1152, 0x4325, 0xcb03, 0xc18a, 0x2b95, 0xeb45, -0xcf92, 0x4c54, 0x18ad, 0xe08b, 0x3f12, 0x1264, 0xa9fc, 0xfd97, 0x246f, 0xbf86, -0x0ce2, 0x4e7c, 0xe4f3, 0x0c20, 0x44e0, 0xd069, 0xbdcb, 0x2b8e, 0xf32d, 0xcad4, -0x464f, 0x1e76, 0xdf62, 0x3b07, 0x17ea, 0xaafb, 0xf5a0, 0x2835, 0xc7c2, 0x0842, -0x4d2b, 0xe634, 0x03ef, 0x42bc, 0xd7f2, 0xbb73, 0x2662, 0xf892, 0xc8b3, 0x3e30, -0x1f20, 0xdcca, 0x354a, 0x1c6b, 0xaf75, 0xf0f7, 0x2963, 0xc908, 0xfdbf, 0x4c3c, -0xebe5, 0x00e3, 0x44c4, 0xdf15, 0xb9e9, 0x243b, 0x00e9, 0xcb76, 0x3b53, 0x248e, -0xdc27, 0x2fcb, 0x22e5, 0xb66c, 0xec96, 0x2b19, 0xd0ef, 0xf97b, 0x48ae, 0xecc0, -0xf8b4, 0x411d, 0xe769, 0xb9f7, 0x1c41, 0x0022, 0xc369, 0x2ced, 0x23ac, 0xd8eb, -0x2522, 0x232a, 0xb611, 0xe19f, 0x2738, 0xd013, 0xece5, 0x434c, 0xf00e, 0xefcc, -0x3b79, 0xeb32, 0xb19c, 0x135e, 0x04ef, 0xc1b9, 0x27a8, 0x2992, 0xd7b3, 0x1ba5, -0x2481, 0xb8c5, 0xd97d, 0x246f, 0xd113, 0xe45d, 0x4486, 0xf7f7, 0xeb36, 0x395a, -0xf122, 0xaea5, 0x0c28, 0x05eb, 0xbde4, 0x2585, 0x36a2, 0xde67, 0x1b86, 0x2dac, -0xbd03, 0xd2b8, 0x2624, 0xd8b8, 0xe802, 0x521b, 0x0855, 0xefbc, 0x4048, 0xfad2, -0xafe2, 0x0fb1, 0x12b2, 0xc62c, 0x2c2a, 0x43f5, 0xe562, 0x1fcb, 0x3791, 0xc2ac, -0xd4d1, 0x2dfd, 0xde0a, 0xe53f, 0x5578, 0x0f49, 0xf2b6, 0x4609, 0x0105, 0xabf5, -0x09a8, 0x157e, 0xc286, 0x23e7, 0x425f, 0xe36a, 0x1d93, 0x3580, 0xbf80, 0xcaf2, -0x2a04, 0xf16e, 0xd92b, 0x0eaa, 0xf1a7, 0x1ddb, 0x5b52, 0x0665, 0xd2e3, 0x15f8, -0xf606, 0x9d42, 0xdba7, 0xf312, 0xd349, 0x21ed, 0x576a, 0x34e8, 0x2450, 0x2679, -0xdc01, 0xb506, 0xcb0f, 0xa454, 0xccf3, 0x2c13, 0x1673, 0xf8ca, 0x4ff1, 0x63ac, -0xec26, 0xd77c, 0xf1f9, 0xc268, 0xb11a, 0xdfe4, 0x02e7, 0x10f5, 0x3512, 0x19dd, -0x0edc, 0x3568, 0xf6f7, 0xbe10, 0xda93, 0xf4fe, 0xda03, 0xe293, 0x15dd, 0x15f3, -0x1ba5, 0x1521, 0x12e8, 0x23ab, 0x0fc3, 0xdb3e, 0xb671, 0xe960, 0xe13c, 0xc695, -0x1a81, 0x3d23, 0x1c56, 0x190d, 0x4234, 0x1970, 0xd784, 0xd86b, 0xb5e8, 0xc9f3, -0xeb89, 0xe344, 0x17ae, 0x5713, 0x37fc, 0xffe2, 0x36b3, 0x1dfe, 0xb963, 0xbf9c, -0xc9a1, 0xcc7b, 0xe409, 0x08a6, 0x2077, 0x3b4d, 0x3cba, 0x0553, 0x220e, 0x226e, -0xd219, 0xb7ec, 0xcb8b, 0xdf2a, 0xd0c7, 0xf5be, 0x2ff0, 0x42a6, 0x3c24, 0x25ae, -0x2d6d, 0x0d94, 0xde80, 0xb78b, 0xb12b, 0xdf7a, 0xde33, 0x0046, 0x47b1, 0x5170, -0x29c0, 0x2945, 0x3ab5, 0xf08f, 0xc806, 0xc229, 0xbbf4, 0xe40d, 0xf365, 0x0bfe, -0x448d, 0x5cd8, 0x1e52, 0x10ba, 0x3908, 0xefa4, 0xc243, 0xcf89, 0xd02d, 0xde92, -0xf8e0, 0x191e, 0x2f7b, 0x48e6, 0x1e38, 0x1074, 0x3785, 0xf8be, 0xbd1c, 0xc06b, -0xdc36, 0xdb97, 0xe3c0, 0x2042, 0x37c5, 0x36ff, 0x1b73, 0x2064, 0x2c9a, 0xefa2, -0xbf0c, 0xb7f0, 0xe221, 0xe243, 0xd998, 0x2263, 0x4bae, 0x3596, 0x18aa, 0x3763, -0x27d0, 0xdcc6, 0xcacc, 0xc06f, 0xd83d, 0xecfe, 0xeefa, 0x1ffa, 0x5052, 0x393f, -0x0af5, 0x3c9e, 0x316b, 0xd2df, 0xc575, 0xd3c8, 0xddd2, 0xdf98, 0xfbd7, 0x2929, -0x4879, 0x4052, 0x160c, 0x3708, 0x2b31, 0xdac6, 0xc0c3, 0xcfc0, 0xe71d, 0xddec, -0x0145, 0x3847, 0x457c, 0x356b, 0x214a, 0x3a5f, 0x1474, 0xd892, 0xc579, 0xc6a7, -0xe77a, 0xe4dc, 0x00ab, 0x3b89, 0x4eba, 0x290a, 0x16ea, 0x3dc6, 0x0956, 0xcc12, -0xc3bd, 0xc9e9, 0xe4be, 0xe60b, 0x0561, 0x3707, 0x4c82, 0x2444, 0x1406, 0x3a8e, -0xff5b, 0xc494, 0xbf9f, 0xcb26, 0xdfef, 0xe755, 0x1060, 0x334f, 0x40e5, 0x1f87, -0x16b9, 0x33e8, 0xfa6e, 0xc670, 0xb774, 0xcc17, 0xe18f, 0xdd0f, 0x102c, 0x3f0d, -0x4098, 0x1b95, 0x24b2, 0x315a, 0xe9d8, 0xc459, 0xb314, 0xc524, 0xe2a6, 0xe1cf, -0x100a, 0x44af, 0x455c, 0x1551, 0x264f, 0x2ab1, 0xd681, 0xb90c, 0xb4d6, 0xc68d, -0xddac, 0xef74, 0x1f57, 0x4357, 0x4192, 0x0e60, 0x1bcb, 0x20fd, 0xd477, 0xb435, -0xb3e3, 0xcdc3, 0xd9c4, 0xef97, 0x2384, 0x3b60, 0x34c9, 0x119d, 0x1f15, 0x0fb3, -0xd15d, 0xb30d, 0xa9e3, 0xd431, 0xdc02, 0xe98a, 0x2987, 0x4204, 0x290c, 0x1181, -0x2d0c, 0x0800, 0xcb55, 0xb8f5, 0xaaa6, 0xd49f, 0xe57c, 0xf063, 0x281c, 0x4c65, -0x2d19, 0x0cd2, 0x2ddb, 0xfefe, 0xc171, 0xbd4c, 0xb7c2, 0xd4c5, 0xe6f3, 0x0040, -0x2b86, 0x4b6d, 0x2ed1, 0x0ce3, 0x2d97, 0x01f9, 0xc2ad, 0xb8fc, 0xc53e, 0xe1cf, -0xea35, 0x0eb0, 0x38b8, 0x4a3b, 0x2a1e, 0x1457, 0x2a1e, 0xfbca, 0xcdf1, 0xbc93, -0xcc0b, 0xec27, 0xeb05, 0x144b, 0x4443, 0x496d, 0x2233, 0x2180, 0x30b2, 0xf03c, -0xcced, 0xbf0d, 0xcc55, 0xeec3, 0xf367, 0x186f, 0x45cd, 0x4e7d, 0x215a, 0x2485, -0x3122, 0xe7a8, 0xc40a, 0xbf85, 0xd4dd, 0xebe8, 0xf32b, 0x2121, 0x49bb, 0x4c61, -0x1af5, 0x1f88, 0x2c32, 0xe8c5, 0xc512, 0xc0b7, 0xdbf9, 0xe9ea, 0xf2f4, 0x2584, -0x43e2, 0x3e1b, 0x19cf, 0x28d2, 0x2442, 0xe27b, 0xc589, 0xbe8a, 0xdddc, 0xe567, -0xed4e, 0x27f2, 0x48cd, 0x3505, 0x0e88, 0x2cd5, 0x207d, 0xda54, 0xc1cf, 0xb8c1, -0xd925, 0xe569, 0xefd0, 0x2723, 0x4dd1, 0x38b2, 0x0de5, 0x2d90, 0x155b, 0xca06, -0xbab6, 0xbf37, 0xdd46, 0xe3fd, 0xfb50, 0x2e5d, 0x487b, 0x343e, 0x0abe, 0x25e9, -0x0f65, 0xcb83, 0xb474, 0xbc50, 0xe2ab, 0xe1df, 0xfd3e, 0x3672, 0x458b, 0x294e, -0x10fd, 0x2afa, 0x027f, 0xcae8, 0xb95b, 0xbc6f, 0xe536, 0xe3af, 0xfd1c, 0x3b18, -0x4cb1, 0x23ff, 0x13eb, 0x3353, 0xfb34, 0xc4aa, 0xb71a, 0xb9f2, 0xe1d7, 0xe97f, -0x058d, 0x3a0f, 0x4fcd, 0x2408, 0x11a3, 0x2fb9, 0xf271, 0xbb7f, 0xb447, 0xc317, -0xde44, 0xe56a, 0x110a, 0x3ccc, 0x494a, 0x1f80, 0x11af, 0x26a1, 0xeb09, 0xbcd0, -0xaf90, 0xc8d4, 0xe63f, 0xe47d, 0x1435, 0x3f4f, 0x3fbe, 0x17c7, 0x1a4f, 0x2393, -0xe191, 0xbfa1, 0xb0e4, 0xc7c9, 0xe2d9, 0xe363, 0x1625, 0x4320, 0x3da9, 0x11c4, -0x1e02, 0x1d1b, 0xd6be, 0xbe96, 0xb123, 0xc8a4, 0xe6ce, 0xef2e, 0x1c03, 0x4584, -0x3fd1, 0x1006, 0x20d8, 0x197b, 0xcf64, 0xb99e, 0xb693, 0xd396, 0xe8eb, 0xfb01, -0x2aca, 0x4b38, 0x3f87, 0x0de0, 0x1f2f, 0x1503, 0xd574, 0xba46, 0xb72d, 0xf07a, -0xfa16, 0xf608, 0x29c0, 0x3a7e, 0x42a7, 0x43ac, 0x2717, 0xec6f, 0xd732, 0xc1ac, -0xa146, 0xef37, 0x122b, 0x05c1, 0x5c67, 0x8e8c, 0x3d5e, 0x0043, 0x00d0, 0xb9ef, -0xa38d, 0xc8dd, 0xc921, 0x15c9, 0x5fe3, 0x531a, 0x477d, 0x5852, 0x1b9f, 0xb930, -0xd1b6, 0xde60, 0xbcce, 0xe7f7, 0x16b1, 0x2aeb, 0x4605, 0x3592, 0xfe8c, 0x0c1d, -0x1b24, 0xd084, 0xd667, 0x2736, 0x06f7, 0xdfa7, 0x1976, 0x0df9, 0xc5e8, 0x032b, -0x324e, 0xea0e, 0x1ab4, 0x46e4, 0xf72e, 0x0369, 0x0ef3, 0xbe35, 0xbd17, 0x10fd, -0xfb35, 0xeb3f, 0x4e43, 0x2da4, 0xfe31, 0x2f50, 0xf64c, 0xafd6, 0xe267, 0xfd01, -0xca77, 0x1087, 0x48c1, 0xfcf4, 0x1bb0, 0x31af, 0xd234, 0xc0cb, 0x054e, 0xec6b, -0xce29, 0x29db, 0x1bb4, 0xf0fd, 0x3608, 0x12eb, 0xbb40, 0xeaa8, 0x190f, 0xce00, -0xed59, 0x39ef, 0xf1f0, 0xfb2a, 0x3535, 0xe3b3, 0xbf33, 0x1a9b, 0x013b, 0xc2ab, -0x2976, 0x21e0, 0xd3d8, 0x1ca6, 0x14ae, 0xb242, 0xe538, 0x2958, 0xd98c, 0xf279, -0x4106, 0xf13e, 0xf68b, 0x3379, 0xe023, 0xb4a8, 0x104b, 0x0685, 0xcca4, 0x2e61, -0x2d96, 0xe2b8, 0x26ac, 0x2510, 0xc114, 0xd9e5, 0x1f91, 0xdbc9, 0xe515, 0x40bd, -0x0693, 0xff44, 0x3c5e, 0xf664, 0xb8dc, 0x0b37, 0x1314, 0xc29c, 0x161f, 0x3582, -0xe32e, 0x17c0, 0x2de6, 0xc7c1, 0xcfeb, 0x23a6, 0xe644, 0xe65f, 0x4256, 0xf765, -0xe698, 0x4148, 0xfbe1, 0xa6b4, 0x03fa, 0x1c92, 0xcb85, 0x1a54, 0x37af, 0xe830, -0x1b0b, 0x255d, 0xc13f, 0xd3d9, 0x205e, 0xde69, 0xe2ab, 0x48d5, 0x0931, 0xee2f, -0x3d79, 0x0658, 0xb36c, 0xf59e, 0x11f4, 0xd042, 0x110b, 0x2e1b, 0xe763, 0x2269, -0x3bda, 0xcefb, 0xd37b, 0x2d7f, 0xe9d7, 0xd48e, 0x3fd2, 0x0e86, 0xea62, 0x3cd5, -0x11e0, 0xc1dc, 0x08e0, 0x1f68, 0xd3f1, 0x1fc8, 0x3da6, 0xe12f, 0x1d62, 0x4060, -0xccb6, 0xd211, 0x316f, 0xf370, 0xe20e, 0x4657, 0x1280, 0xf30a, 0x3df0, 0x07fc, -0xb956, 0x023e, 0x1978, 0xcbba, 0x137d, 0x3ff7, 0xecbc, 0x1698, 0x3f29, 0xdf9f, -0xcc1c, 0x1bdc, 0xef17, 0xd3da, 0x346b, 0x1296, 0xeb25, 0x3885, 0x190f, 0xbf13, -0xfb71, 0x1df2, 0xc509, 0xffa2, 0x3a66, 0xe5fd, 0x04f6, 0x36be, 0xda99, 0xc67e, -0x1fc2, 0xef95, 0xcfa8, 0x39df, 0x0f1a, 0xd986, 0x2d7b, 0x0e88, 0xb2a2, 0xf40f, -0x1bd3, 0xc95c, 0x0511, 0x408d, 0xec48, 0x03d2, 0x3281, 0xd7a0, 0xb9a0, 0x13ab, -0xf02d, 0xc92c, 0x3af6, 0x26c0, 0xe5f8, 0x2de7, 0x18b9, 0xafd8, 0xddbf, 0x15bc, -0xc4d3, 0xf6dc, 0x4b73, 0xf89f, 0x018a, 0x3c4e, 0xdf11, 0xb20d, 0x12d7, 0xf511, -0xbf7e, 0x33aa, 0x286f, 0xe309, 0x3107, 0x1f74, 0xb1c3, 0xe10f, 0x1fd3, 0xc7d4, -0xef6e, 0x4b78, 0xf32f, 0xf8e5, 0x43cb, 0xe7da, 0xaf46, 0x115a, 0xfeb2, 0xbf7a, -0x2e9a, 0x2ed7, 0xde2f, 0x2807, 0x259c, 0xb09f, 0xd3d4, 0x2606, 0xd544, 0xeb3d, -0x5107, 0xfecf, 0xf63f, 0x4304, 0xedfe, 0xae0d, 0x0d7f, 0x0957, 0xc47d, 0x2f62, -0x3b51, 0xdfea, 0x2a01, 0x3390, 0xb825, 0xd3e9, 0x29f1, 0xd82e, 0xe2e3, 0x509a, -0x061c, 0xf530, 0x48b1, 0xf740, 0xabeb, 0x0d93, 0x0ed4, 0xbed0, 0x274e, 0x3e3b, -0xddc2, 0x2168, 0x35a1, 0xbbb0, 0xcedb, 0x2b94, 0xdd5b, 0xdd2d, 0x4e6a, 0x068d, -0xe741, 0x3eef, 0xfe34, 0xad12, 0x0bb7, 0x1a73, 0xbea5, 0x1c31, 0x4269, 0xdc1a, -0x1611, 0x37d6, 0xc048, 0xcaa3, 0x2f7e, 0xe59c, 0xd94c, 0x4ed8, 0x0af6, 0xe225, -0x3c84, 0xfd49, 0xa4b2, 0x048d, 0x1ed5, 0xc496, 0x1caa, 0x4641, 0xddd4, 0x1578, -0x37dc, 0xc13b, 0xcab7, 0x30dc, 0xfec0, 0xd462, 0x1387, 0x07dd, 0x14c1, 0x4b92, -0x0d74, 0xda49, 0x12de, 0x02fe, 0xb8fe, 0xeaae, 0x0363, 0xdab0, 0x23b0, 0x68fb, -0x3681, 0x1351, 0x29fc, 0xf22e, 0xb781, 0xd225, 0xc11d, 0xd7d8, 0x354d, 0x26b8, -0x09af, 0x60fa, 0x5f8c, 0xe302, 0xde80, 0xff6a, 0xbb95, 0xafec, 0x029f, 0x161d, -0x0fee, 0x3924, 0x2b6c, 0x1ed5, 0x24fe, 0xec7b, 0xc1fe, 0xe22b, 0xfbcd, 0xdc4d, -0xf3f7, 0x210f, 0x1d01, 0x1305, 0x1342, 0x1f6c, 0x0852, 0xfea5, 0xdd42, 0xc083, -0xf243, 0xde95, 0xd818, 0x23f7, 0x3eab, 0x0891, 0x1381, 0x52fd, 0xff10, 0xc983, -0xe091, 0xc3b8, 0xcafc, 0xe7d7, 0xfc8d, 0x2043, 0x559d, 0x2c2e, 0x0418, 0x4485, -0x0b4c, 0xb4e5, 0xc68e, 0xddbf, 0xd0b6, 0xdc81, 0x1e4b, 0x2d10, 0x365b, 0x2c50, -0x170a, 0x303e, 0x0a60, 0xcc89, 0xb88a, 0xdbc7, 0xe3e7, 0xcdd2, 0x0b38, 0x3c7e, -0x392b, 0x254c, 0x3272, 0x2fc9, 0xf0ee, 0xd4d8, 0xb5b4, 0xc03b, 0xdef0, 0xd8e9, -0x0edc, 0x533e, 0x46e4, 0x0fc4, 0x358a, 0x34b8, 0xd1c3, 0xbf29, 0xbb64, 0xbeea, -0xdb1c, 0xf31b, 0x17f1, 0x44fa, 0x4bfb, 0x0a36, 0x1fe2, 0x2ce9, 0xcf0d, 0xb605, -0xc6c6, 0xcc96, 0xcf30, 0xf9cd, 0x25fb, 0x36d1, 0x4086, 0x1499, 0x21d8, 0x287f, -0xde77, 0xb0fd, 0xba6d, 0xe0f5, 0xd3e4, 0xee77, 0x3561, 0x4077, 0x2baa, 0x1d38, -0x3753, 0x1587, 0xd2e2, 0xb252, 0xb44b, 0xe5a7, 0xdbb5, 0xe778, 0x3790, 0x55cb, -0x234e, 0x10ab, 0x42e9, 0x083e, 0xc15a, 0xc2a9, 0xbe30, 0xd7d1, 0xe76a, 0xfa22, -0x2b37, 0x53cb, 0x29a6, 0x0950, 0x4086, 0x0f68, 0xbba0, 0xb824, 0xcc9c, 0xd743, -0xd665, 0x06ae, 0x3597, 0x44f1, 0x2854, 0x19d4, 0x3395, 0xfe8f, 0xc1b9, 0xad2d, -0xc39d, 0xde05, 0xd850, 0x0bf2, 0x4266, 0x457f, 0x1d4b, 0x2284, 0x337f, 0xe442, -0xbc43, 0xb8ba, 0xc33a, 0xe0e4, 0xe8f8, 0x10b5, 0x4262, 0x4afc, 0x1744, 0x1d2b, -0x3125, 0xe2b5, 0xbcb6, 0xbdea, 0xccfd, 0xdfe5, 0xefed, 0x1bae, 0x3f5e, 0x451d, -0x167c, 0x1ea7, 0x2848, 0xdf70, 0xbb35, 0xbbfc, 0xd959, 0xe266, 0xec2b, 0x20e3, -0x435c, 0x3878, 0x0fee, 0x25e8, 0x1ba1, 0xdaf0, 0xc061, 0xb76f, 0xdd9c, 0xe727, -0xece4, 0x247e, 0x48ee, 0x303d, 0x099a, 0x320b, 0x19b9, 0xd0b8, 0xc508, 0xbe20, -0xd52c, 0xe430, 0xf5f1, 0x21d1, 0x4aae, 0x3670, 0x0bc4, 0x349a, 0x16c6, 0xc9e1, -0xbb8f, 0xc44e, 0xdbed, 0xde26, 0x03b2, 0x34c9, 0x4689, 0x30a8, 0x17ea, 0x33bd, -0x0b87, 0xcd79, 0xb9b9, 0xc3c1, 0xe227, 0xdffc, 0x07ae, 0x3deb, 0x4732, 0x25e8, -0x1ef9, 0x370f, 0xfb29, 0xcc78, 0xbf32, 0xc5c0, 0xe807, 0xe571, 0x074b, 0x4121, -0x4902, 0x1968, 0x206c, 0x3da5, 0xf467, 0xc9c7, 0xc240, 0xc6d8, 0xe2b1, 0xeca9, -0x0f7d, 0x3a80, 0x4ac1, 0x1bda, 0x1cdc, 0x3836, 0xee35, 0xc32e, 0xc0a2, 0xce3e, -0xdfd7, 0xe9c8, 0x162c, 0x3eb5, 0x48b0, 0x1a61, 0x1e8f, 0x2cf5, 0xe5c6, 0xbb80, -0xb378, 0xd228, 0xe3dd, 0xeba5, 0x2266, 0x46f5, 0x3e1f, 0x13fa, 0x26ea, 0x21ec, -0xd925, 0xbdc7, 0xb66d, 0xd76b, 0xe81e, 0xf025, 0x269d, 0x4d69, 0x3d40, 0x1027, -0x2c58, 0x1cc9, 0xd265, 0xbfd4, 0xbabe, 0xd919, 0xe822, 0xf931, 0x2bc9, 0x4c69, -0x3d20, 0x158d, 0x31ca, 0x1821, 0xce8a, 0xb8af, 0xba0f, 0xdfb6, 0xe677, 0xfd3b, -0x385e, 0x53d9, 0x3764, 0x14a8, 0x30af, 0x0a51, 0xcb95, 0xbad7, 0xbc48, 0xe366, -0xea7e, 0x06cf, 0x3f08, 0x53c7, 0x2fe0, 0x189d, 0x383f, 0x00fd, 0xc5f3, 0xbf0d, -0xc38f, 0xe4a3, 0xecee, 0x0ef5, 0x432c, 0x54a7, 0x2a15, 0x190d, 0x3675, 0xf7bc, -0xc3e0, 0xbc22, 0xc381, 0xe210, 0xec59, 0x15ed, 0x4300, 0x4fd8, 0x269c, 0x1bda, -0x324a, 0xed57, 0xbb9c, 0xb705, 0xceb8, 0xeb30, 0xed72, 0x1baa, 0x48ad, 0x4bd3, -0x1fde, 0x1ea9, 0x2826, 0xe505, 0xc2b3, 0xb577, 0xceec, 0xeeb9, 0xef73, 0x1fd5, -0x4c99, 0x41f7, 0x12c7, 0x24ad, 0x22eb, 0xd504, 0xbfe3, 0xba2a, 0xd063, 0xea6f, -0xf037, 0x1c9c, 0x4acf, 0x430c, 0x0b68, 0x200d, 0x1c9e, 0xcce4, 0xb9ad, 0xbc29, -0xd211, 0xe475, 0xfc21, 0x2910, 0x443b, 0x3a83, 0x0ef1, 0x2295, 0x15ac, 0xd00d, -0xb774, 0xbaff, 0xded3, 0xe41e, 0xf945, 0x331e, 0x49b7, 0x3276, 0x128d, 0x28c7, -0x08f2, 0xce8e, 0xbb2e, 0xb907, 0xe4c0, 0xe9f8, 0xf98a, 0x3323, 0x4a75, 0x2718, -0x0ddc, 0x3369, 0x0795, 0xc936, 0xc192, 0xc3cc, 0xe2b9, 0xe583, 0xfce9, 0x312f, -0x4951, 0x266f, 0x0ffe, 0x3698, 0x0679, 0xca63, 0xc301, 0xc844, 0xde4c, 0xe26e, -0x076e, 0x3283, 0x4507, 0x259a, 0x11af, 0x30ff, 0xfd1c, 0xc1b2, 0xb384, 0xc924, -0xe414, 0xde8f, 0x0781, 0x295f, 0x51b4, 0x5b09, 0x17c9, 0xf17b, 0xd9cd, 0xb11a, -0x8396, 0xbd98, 0x073f, 0x0598, 0x5258, 0x7bf8, 0x3dd3, 0x096d, 0xe7f8, 0xa966, -0x9271, 0xc3c7, 0xb173, 0xf5d9, 0x6db2, 0x3b89, 0x2231, 0x4aaf, 0x1c3b, 0xc115, -0xcb06, 0xd460, 0xbb98, 0x03f6, 0xf9d7, 0xecaf, 0x4aa5, 0x27cf, 0xcf8c, 0x0764, -0x3489, 0xd9cb, 0xf31b, 0x39b5, 0xebc3, 0xeb7f, 0x1192, 0xceee, 0xbd72, 0x16f9, -0x1b5a, 0xf888, 0x4a44, 0x34a8, 0xedd8, 0x18bb, 0xf8d6, 0xa74c, 0xd19c, 0x139c, -0xeaf7, 0x0d0b, 0x5317, 0x0e81, 0x0c44, 0x35bd, 0xe010, 0xb51d, 0x075b, 0xfc77, -0xc9ae, 0x2b95, 0x35a1, 0xf0e8, 0x2c61, 0x2481, 0xc370, 0xe826, 0x20b5, 0xd95a, -0xf832, 0x43e0, 0xf261, 0xf7ef, 0x414e, 0xf14b, 0xbf9e, 0x1c6c, 0x1380, 0xd3d1, -0x2650, 0x1f52, 0xd592, 0x1ddb, 0x2414, 0xc347, 0xebd1, 0x3e70, 0xf240, 0xeb89, -0x3d66, 0xf738, 0xe57b, 0x2fe8, 0xf22d, 0xbd68, 0x1e7b, 0x2466, 0xd858, 0x2613, -0x3122, 0xdc86, 0x16b9, 0x277b, 0xc324, 0xdb13, 0x2c78, 0xe8ab, 0xed0b, 0x49bb, -0x0342, 0xf02a, 0x3b6c, 0xf7d9, 0xb9c6, 0x0fd8, 0x1192, 0xc763, 0x12e5, 0x2738, -0xe26c, 0x1a89, 0x2a72, 0xcd72, 0xdca7, 0x27a9, 0xe962, 0xd98a, 0x271e, 0xf948, -0xe783, 0x29f0, 0x000e, 0xc137, 0x064c, 0x17e6, 0xcd48, 0x0efb, 0x329b, 0xdc50, -0xf9d6, 0x28fd, 0xd866, 0xc34b, 0x13df, 0xefa3, 0xdcbf, 0x3578, 0x09a7, 0xe33f, -0x2c3f, 0x02a6, 0xaa76, 0xf3eb, 0x1870, 0xc21d, 0x029e, 0x3d07, 0xedbb, 0x0a92, -0x33dc, 0xd94f, 0xc985, 0x15a5, 0xdf1c, 0xd3f5, 0x3f5e, 0x0fca, 0xe50f, 0x3b04, -0x1a3d, 0xb99b, 0xf6d1, 0x1c75, 0xcc21, 0x0987, 0x3e95, 0xed51, 0x0dcf, 0x3b32, -0xd980, 0xc6f7, 0x280e, 0xf587, 0xd3c2, 0x4871, 0x233c, 0xe02f, 0x3039, 0x183d, -0xaecf, 0xf137, 0x2776, 0xcc66, 0x0bf0, 0x5162, 0xeddf, 0x088c, 0x4536, 0xd457, -0xb205, 0x2315, 0xf51a, 0xc60d, 0x4281, 0x2682, 0xe5d9, 0x3aad, 0x1cab, 0xb02d, -0xf294, 0x20af, 0xbecb, 0x0084, 0x4c16, 0xeaf2, 0x054e, 0x449f, 0xdf02, 0xbd48, -0x25bf, 0xfda9, 0xcb15, 0x3a93, 0x1e9b, 0xddd4, 0x3408, 0x1f70, 0xb333, 0xf3df, -0x32ab, 0xd133, 0x014e, 0x52b2, 0xf138, 0xfe00, 0x4260, 0xe1f2, 0xbbac, 0x28bf, -0x0404, 0xcc81, 0x4649, 0x2e56, 0xdee8, 0x3677, 0x23ef, 0xabc0, 0xea62, 0x3159, -0xcf59, 0xfdcf, 0x575a, 0xf403, 0xfe40, 0x4759, 0xe094, 0xb225, 0x1ffa, 0xfefc, -0xc26f, 0x3a61, 0x2be1, 0xdb44, 0x2efe, 0x2504, 0xadcb, 0xe074, 0x2713, 0xc6d3, -0xecc2, 0x48d6, 0xea4d, 0xf2ec, 0x43a0, 0xe1fc, 0xaa5f, 0x1825, 0xffd7, 0xba38, -0x2bdb, 0x24a3, 0xce10, 0x1cd8, 0x1cfc, 0xab2e, 0xdc4d, 0x276d, 0xca22, 0xeb01, -0x4a08, 0xeb0e, 0xe94c, 0x3cd7, 0xe45f, 0xa6c6, 0x0f8c, 0x066f, 0xc2d2, 0x2a01, -0x2aea, 0xd9bf, 0x251a, 0x2933, 0xb64d, 0xd9b5, 0x26a1, 0xd2ba, 0xe822, 0x4af1, -0xfec7, 0xf323, 0x3fdf, 0xf78e, 0xb4c1, 0x0f7f, 0x0e1c, 0xc7ce, 0x25fb, 0x3129, -0xdeb5, 0x2268, 0x3385, 0xc266, 0xd9b6, 0x2efc, 0xddb0, 0xe00f, 0x45ac, 0x0168, -0xea8d, 0x3cd9, 0xfeda, 0xb603, 0x13c5, 0x166b, 0xc192, 0x1f5a, 0x3804, 0xda94, -0x15a3, 0x35b3, 0xc729, 0xd3ae, 0x2e68, 0xe359, 0xde70, 0x4764, 0x0725, 0xe6b1, -0x3882, 0xfed4, 0xad23, 0x0819, 0x16c2, 0xc15b, 0x1c91, 0x4358, 0xe49c, 0x1162, -0x35d1, 0xc8f8, 0xc74f, 0x2676, 0xe0e9, 0xd0d7, 0x4b09, 0x1cea, 0xedea, 0x3f3b, -0x11f4, 0xb09a, 0xfc73, 0x177b, 0xba40, 0x109f, 0x4fcb, 0xf285, 0x1d0f, 0x3dc2, -0xc588, 0xc98d, 0x329a, 0xfd8a, 0xcc33, 0x1573, 0x1810, 0x1532, 0x434e, 0x102d, -0xd555, 0x08d4, 0x0011, 0xb77c, 0xec37, 0x098c, 0xd4fc, 0x2033, 0x7926, 0x32be, -0xfe95, 0x28ab, 0xef9c, 0xa428, 0xcffc, 0xcbdf, 0xd07c, 0x3681, 0x2f87, 0x0680, -0x626e, 0x5e9c, 0xd624, 0xd9e4, 0x080a, 0xadfe, 0xa2f5, 0x12af, 0x142c, 0xffde, -0x3703, 0x3570, 0x269e, 0x24fb, 0xe8a1, 0xb7ec, 0xe753, 0xf82a, 0xce4d, 0x001a, -0x2e98, 0x1f84, 0x0eb8, 0x1beb, 0x2603, 0xfcff, 0xfd98, 0xd8c0, 0xc719, 0xfc52, -0xddd2, 0xe3ec, 0x2ee0, 0x4393, 0x042b, 0x1929, 0x569a, 0xef83, 0xc35d, 0xd9e5, -0xc6ce, 0xd1e0, 0xed86, 0x0b2a, 0x23b2, 0x504c, 0x20ad, 0x029d, 0x3b72, 0xf5a5, -0xad6b, 0xbe54, 0xddfc, 0xd162, 0xddcd, 0x2952, 0x324b, 0x3156, 0x1d3f, 0x12f5, -0x235b, 0xf27f, 0xc001, 0xb250, 0xdfdd, 0xe3f3, 0xd455, 0x14e4, 0x3c06, 0x3326, -0x1a60, 0x30e7, 0x24a0, 0xe2c3, 0xcd08, 0xb21c, 0xc75c, 0xdc66, 0xe088, 0x1e09, -0x54ef, 0x4197, 0x0dca, 0x356f, 0x22ce, 0xcaf2, 0xc0ce, 0xbc3d, 0xcfda, 0xe59b, -0xfe5c, 0x27b1, 0x4caa, 0x45a1, 0x0add, 0x274f, 0x1c6c, 0xcde6, 0xc343, 0xd011, -0xdf48, 0xe021, 0x0b0c, 0x335a, 0x3c8e, 0x345d, 0x0d86, 0x278e, 0x1b8f, 0xdc12, -0xbc28, 0xc6ff, 0xead3, 0xdba0, 0xfdee, 0x39b8, 0x3f03, 0x2143, 0x1858, 0x376b, -0x021b, 0xcaa8, 0xbb59, 0xc6f6, 0xef67, 0xe041, 0xf9ba, 0x3cd7, 0x4cfd, 0x168d, -0x1037, 0x3fec, 0xf71d, 0xbed3, 0xc8d6, 0xcbc6, 0xdd8f, 0xea8c, 0x09b1, 0x2e92, -0x4701, 0x1829, 0x091c, 0x3ad7, 0xfd4b, 0xb999, 0xbe68, 0xdc50, 0xdc39, 0xd663, -0x1009, 0x330e, 0x37f3, 0x1ad3, 0x1cca, 0x3476, 0xf66a, 0xc5e8, 0xbb6f, 0xd5e8, -0xe008, 0xd5ea, 0x10b5, 0x3f63, 0x3725, 0x1102, 0x2911, 0x3855, 0xe9cb, 0xc610, -0xc44b, 0xd547, 0xdd89, 0xe4aa, 0x195d, 0x4084, 0x3d9b, 0x10ab, 0x2a1f, 0x3667, -0xe38c, 0xc1a0, 0xc4c5, 0xdd94, 0xe21b, 0xe99c, 0x1f49, 0x4312, 0x3b69, 0x0f14, -0x2b3d, 0x2eaa, 0xdeab, 0xc00a, 0xc634, 0xe225, 0xe0e3, 0xf311, 0x2b9a, 0x44fd, -0x3881, 0x11ee, 0x2f2a, 0x2428, 0xdc87, 0xc347, 0xc2d0, 0xe6e0, 0xe5b0, 0xf196, -0x2d4e, 0x4a97, 0x3366, 0x1388, 0x3ae0, 0x1bf8, 0xd058, 0xc212, 0xc09b, 0xdbf3, -0xe25c, 0xfa1f, 0x3093, 0x4e94, 0x31f0, 0x12fe, 0x3bde, 0x11ad, 0xc841, 0xb8bd, -0xbe0b, 0xdd25, 0xdd7e, 0x0138, 0x3ac9, 0x4ccb, 0x2ba2, 0x1359, 0x3033, 0xfbc6, -0xc14d, 0xb543, 0xbd7e, 0xdcf8, 0xde2b, 0x0754, 0x388b, 0x409a, 0x1e0d, 0x134a, -0x2ba8, 0xeee4, 0xbe5b, 0xafaf, 0xbb90, 0xe059, 0xde74, 0x0736, 0x3d1c, 0x4310, -0x16f1, 0x1686, 0x2f2a, 0xe9de, 0xbe17, 0xb446, 0xc0ca, 0xdd27, 0xe1a0, 0x0ccd, -0x3c13, 0x4661, 0x1949, 0x196a, 0x2a50, 0xdf8d, 0xb6a7, 0xb25a, 0xc8aa, 0xdf0e, -0xe6dc, 0x19c8, 0x411d, 0x423f, 0x15db, 0x1a0d, 0x2316, 0xdf69, 0xba97, 0xb2a9, -0xd092, 0xe29a, 0xebf2, 0x2556, 0x4a3b, 0x3fb1, 0x16cb, 0x2603, 0x1c1b, 0xd67d, -0xbcf3, 0xb765, 0xdad1, 0xea4b, 0xf176, 0x27e4, 0x4d31, 0x3b4c, 0x0fab, 0x2ad5, -0x1a4d, 0xd10b, 0xbc49, 0xba78, 0xda1f, 0xe903, 0xffda, 0x308c, 0x4a2f, 0x38ce, -0x11cc, 0x299c, 0x105c, 0xcdc3, 0xba31, 0xc03e, 0xe616, 0xe849, 0xfec7, 0x37e1, -0x4e98, 0x3198, 0x12d8, 0x2aeb, 0x03e5, 0xcb29, 0xbccb, 0xc232, 0xe734, 0xeb9c, -0x07ed, 0x3d12, 0x4b48, 0x2515, 0x14e8, 0x330c, 0xfd19, 0xc86d, 0xc241, 0xca17, -0xe64b, 0xe918, 0x09ed, 0x3ba3, 0x4eee, 0x25e2, 0x167a, 0x38a6, 0xffcf, 0xcb0e, -0xc615, 0xd055, 0xe3d3, 0xeafc, 0x1602, 0x3e86, 0x49c2, 0x257e, 0x2166, 0x3bcd, -0xfc55, 0xc4ad, 0xbb87, 0xd6e3, 0xe7a1, 0xe64f, 0x1ddd, 0x4682, 0x4516, 0x1dee, -0x1dc5, 0x2b0d, 0xed86, 0xc590, 0xb97d, 0xd84f, 0xec06, 0xe368, 0x1c7f, 0x4cee, -0x3f07, 0x13c3, 0x289d, 0x28fb, 0xdcf0, 0xc5b7, 0xbdb7, 0xd4a7, 0xec46, 0xecd0, -0x1bda, 0x48cc, 0x4019, 0x0cd7, 0x23a7, 0x2698, 0xd5be, 0xbc3e, 0xb90e, 0xcc6c, -0xddf3, 0xf12b, 0x24f1, 0x4448, 0x3b5c, 0x118e, 0x2441, 0x1c2b, 0xd270, 0xb368, -0xb6c7, 0xdd46, 0xdf51, 0xefbb, 0x3138, 0x49d2, 0x3667, 0x1864, 0x2b86, 0x1073, -0xd35e, 0xbbf4, 0xb47a, 0xdfb4, 0xe8bb, 0xf6b2, 0x353a, 0x4fd4, 0x2e9f, 0x12a8, -0x323d, 0x07c8, 0xcb04, 0xc1fa, 0xbd57, 0xdf17, 0xe6e8, 0xfa82, 0x3052, 0x4c63, -0x2d83, 0x12bf, 0x3366, 0x0318, 0xc6dd, 0xbd85, 0xbc4f, 0xd9e1, 0xe604, 0x0814, -0x34df, 0x4a09, 0x2d9d, 0x148f, 0x2e79, 0xfba1, 0xc12c, 0xb277, 0xbf95, 0xdeb7, -0xe211, 0x10bc, 0x423c, 0x4956, 0x24f7, 0x1571, 0x2434, 0xebbb, 0xc333, 0xb587, -0xc733, 0xedc0, 0xebba, 0x0854, 0x3f2e, 0x6f7e, 0x4a81, 0x0e5e, 0x03fe, 0xcb43, -0xa313, 0xa8c3, 0xd5fe, 0x0fe0, 0x3ce3, 0x6922, 0x5cb3, 0x4541, 0x10b8, 0xc5c1, -0xb7c8, 0xca44, 0xca38, 0xd7f7, 0x38e7, 0x4e53, 0x1cb5, 0x3dfb, 0x3d19, 0x06ab, -0xeda0, 0xe9ef, 0xd83d, 0xf9bf, 0x0ebe, 0xca2a, 0x0a79, 0x510e, 0xf01d, 0xe7eb, -0x3be6, 0x1a2e, 0xf989, 0x353e, 0x099c, 0xe538, 0x16d7, 0xd697, 0xa93c, 0x0407, -0x1bee, 0xf75f, 0x46c6, 0x50e3, 0xf430, 0x1813, 0x169b, 0xaf5d, 0xc57c, 0x0fb5, -0xe638, 0xf7e9, 0x459c, 0x122e, 0x0654, 0x352e, 0xfc93, 0xc262, 0xf99c, 0x072c, -0xc8f1, 0x0bf2, 0x32cd, 0xf85b, 0x1790, 0x25c1, 0xde96, 0xd882, 0x18ea, 0xe834, -0xd77c, 0x3995, 0x1231, 0xeab1, 0x28e3, 0x0cfc, 0xc5ef, 0xf7bd, 0x18f1, 0xd03e, -0x057e, 0x320a, 0xe5e4, 0x07c7, 0x2d5d, 0xd88a, 0xc884, 0x2072, 0xfd6c, 0xd3d0, -0x2a59, 0x1066, 0xe564, 0x22a2, 0x047a, 0xb2ee, 0xeaad, 0x1c28, 0xcc6c, 0xf7e5, -0x3c36, 0xee88, 0xfbfb, 0x3085, 0xddf6, 0xb28a, 0x0aaf, 0xf456, 0xbc45, 0x223a, -0x21ed, 0xe31c, 0x2214, 0x1882, 0xb51a, 0xdbdc, 0x18ba, 0xc1ce, 0xe1c3, 0x397c, -0xed39, 0xf426, 0x3690, 0xe68c, 0xb498, 0x0e83, 0x00c5, 0xc494, 0x1ef7, 0x1b31, -0xdc33, 0x1f84, 0x1b7f, 0xb9c1, 0xdc07, 0x2246, 0xd5fb, 0xeaf7, 0x4a41, 0x042a, -0xed8c, 0x29a9, 0xf4c4, 0xbbf5, 0xffbd, 0x02b3, 0xd089, 0x2a98, 0x349a, 0xe5f4, -0x231d, 0x3682, 0xc042, 0xc757, 0x28ee, 0xe56b, 0xda47, 0x3eac, 0x0d7c, 0xfc17, -0x4512, 0x05d1, 0xbb63, 0x0fc8, 0x0da5, 0xb98f, 0x1ecc, 0x3aea, 0xdbbd, 0x1bd6, -0x4041, 0xd007, 0xd35e, 0x2aaf, 0xea38, 0xe711, 0x3e4a, 0xfa47, 0xef65, 0x3f02, -0xf730, 0xae98, 0x0fae, 0x1e19, 0xc953, 0x1ea3, 0x3db2, 0xe20d, 0x1043, 0x2d87, -0xc8ab, 0xca8a, 0x2270, 0xe769, 0xe021, 0x4751, 0x0a42, 0xed5b, 0x468e, 0x0a0e, -0xa823, 0xfc1e, 0x19ce, 0xc19e, 0x0fd8, 0x3c33, 0xe854, 0x1d91, 0x3f3f, 0xd03c, -0xd290, 0x2d60, 0xe292, 0xd443, 0x4229, 0x0590, 0xe446, 0x3c78, 0x0d2e, 0xbba0, -0x0b10, 0x1fdc, 0xccc6, 0x142f, 0x3174, 0xdc18, 0x1061, 0x371b, 0xd368, 0xd200, -0x2d59, 0xf1a5, 0xdc23, 0x3edd, 0x0fb6, 0xe812, 0x2f6f, 0x0a77, 0xb941, 0xfe00, -0x1d36, 0xcc9c, 0x11f2, 0x404a, 0xe70a, 0x0abc, 0x3965, 0xd3be, 0xbed2, 0x1fe7, -0xee09, 0xd03e, 0x3f07, 0x1799, 0xe33b, 0x342f, 0x1443, 0xb44b, 0xf906, 0x1979, -0xbeaf, 0x0a9f, 0x45b3, 0xe73c, 0x0a6f, 0x3e78, 0xd655, 0xc03d, 0x2087, 0xeb0b, -0xcf7a, 0x4395, 0x1915, 0xe1a6, 0x34d0, 0x134e, 0xac96, 0xeeeb, 0x1aa0, 0xc459, -0x0965, 0x4852, 0xe8bd, 0x0151, 0x3569, 0xd16f, 0xb855, 0x1c6f, 0xed54, 0xcabb, -0x428e, 0x1dfb, 0xdf3c, 0x323b, 0x15f0, 0xab84, 0xe597, 0x18a2, 0xc34f, 0x0120, -0x48dd, 0xedf1, 0x07d1, 0x3f6b, 0xd521, 0xb0ac, 0x1903, 0xf0c7, 0xc122, 0x3959, -0x22ad, 0xe010, 0x2ec7, 0x1837, 0xaaf6, 0xe170, 0x1e2b, 0xc7b9, 0xfdb5, 0x4c53, -0xebef, 0xfb34, 0x3ee4, 0xdef9, 0xb297, 0x1b34, 0xfd76, 0xc42a, 0x391e, 0x2b29, -0xde3f, 0x2dc8, 0x2491, 0xb2a5, 0xe544, 0x2b06, 0xcad8, 0xf507, 0x54ba, 0xf673, -0xfa92, 0x48ca, 0xe9a8, 0xafd3, 0x1ef7, 0x084a, 0xc383, 0x3a6e, 0x3354, 0xdbff, -0x2c3f, 0x2b2a, 0xb115, 0xe1c4, 0x348d, 0xd2d5, 0xf3b1, 0x5801, 0xf84b, 0xf5dc, -0x4897, 0xeba9, 0xac5a, 0x1d11, 0x0bb4, 0xbcd7, 0x32ed, 0x3809, 0xdc0a, 0x2aa8, -0x3035, 0xb63c, 0xddf9, 0x3359, 0xe1e6, 0xdc8c, 0x1666, 0xf007, 0x2428, 0x5dbe, -0x00c2, 0xd781, 0x22f0, 0xf405, 0xa1af, 0xed74, 0xf64b, 0xd808, 0x304e, 0x5c2f, -0x2e87, 0x2aaf, 0x32b5, 0xdc5e, 0xbf90, 0xd852, 0xad5b, 0xdde3, 0x38e2, 0x1923, -0x04d1, 0x62c4, 0x5bb8, 0xe529, 0xeaaf, 0xfd61, 0xc422, 0xc0df, 0xfa6b, 0x0d7b, -0x16ff, 0x3f3e, 0x1d78, 0x1a46, 0x3b1d, 0xf55f, 0xc829, 0xeeb5, 0x0157, 0xdd55, -0xf41a, 0x20b5, 0x1533, 0x2329, 0x1f7c, 0x1523, 0x1e43, 0x1419, 0xdc00, 0xbfc2, -0xfbcf, 0xe187, 0xd1ef, 0x2a18, 0x3c8b, 0x1862, 0x2179, 0x4380, 0x06a1, 0xdc67, -0xe0e4, 0xb854, 0xda4e, 0xf2b5, 0xe744, 0x1f54, 0x57ea, 0x2932, 0xfa55, 0x3961, -0x0fd9, 0xbac6, 0xcdda, 0xd258, 0xcf89, 0xe3ab, 0x0707, 0x1a3e, 0x3967, 0x30cc, -0x0264, 0x2d4f, 0x1e6c, 0xcc01, 0xb70b, 0xd1c4, 0xdac0, 0xca7b, 0xfba8, 0x26c2, -0x371b, 0x359b, 0x23c6, 0x2fb4, 0x0da1, 0xd9bd, 0xae9d, 0xb89c, 0xdb61, 0xcb90, -0x0132, 0x482d, 0x42cd, 0x1e49, 0x2e1f, 0x3691, 0xe7fa, 0xc93c, 0xba66, 0xbb0a, -0xe251, 0xe472, 0x0383, 0x3f08, 0x4cc8, 0x1215, 0x1c80, 0x3b71, 0xe4f3, 0xc431, -0xcbb0, 0xc403, 0xd2f9, 0xf12b, 0x1313, 0x2f50, 0x4774, 0x1313, 0x18c4, 0x4058, -0xed5d, 0xb643, 0xc25a, 0xd92a, 0xcdc3, 0xe267, 0x25f0, 0x37bf, 0x38e3, 0x1db0, -0x2909, 0x2dab, 0xe635, 0xb3fb, 0xb52f, 0xe71a, 0xdad1, 0xdbe5, 0x2dd4, 0x4a45, -0x2f11, 0x15fc, 0x375b, 0x1edc, 0xd95e, 0xc7dd, 0xb91d, 0xdb9a, 0xe8d2, 0xea7d, -0x243c, 0x50aa, 0x347a, 0x0b10, 0x3f2c, 0x24ae, 0xc984, 0xc48d, 0xccec, 0xd726, -0xda77, 0xf845, 0x2782, 0x491b, 0x3bb7, 0x1233, 0x3698, 0x200c, 0xcd29, 0xb25f, -0xc2ef, 0xdd85, 0xd457, 0xfe96, 0x3830, 0x43f0, 0x2e94, 0x1a60, 0x3369, 0x07e9, -0xcb4f, 0xb645, 0xba73, 0xdbb8, 0xdb8f, 0xffae, 0x3a33, 0x48d7, 0x2211, 0x15da, -0x362f, 0xfbd5, 0xc39a, 0xb7fb, 0xc2cf, 0xe1cd, 0xe2d2, 0x0604, 0x36c9, 0x495d, -0x209d, 0x15d2, 0x37e6, 0xf91a, 0xc431, 0xbe00, 0xccf7, 0xe3be, 0xe90a, 0x14f8, -0x3bed, 0x4775, 0x22c5, 0x1a55, 0x2eb8, 0xf7ab, 0xcbee, 0xbb0a, 0xd4b7, 0xea3e, -0xe43a, 0x18fd, 0x455e, 0x429e, 0x1d75, 0x27b4, 0x2e04, 0xeadd, 0xca0c, 0xb834, -0xcf7e, 0xebe8, 0xec12, 0x1d2b, 0x4ce6, 0x4388, 0x1410, 0x2882, 0x2863, 0xdbfa, -0xc201, 0xbc64, 0xd1a8, 0xe40d, 0xf23c, 0x21f6, 0x44ac, 0x3ed5, 0x105b, 0x1f33, -0x1ab9, 0xd393, 0xb879, 0xb8a2, 0xd559, 0xdf87, 0xf31b, 0x2555, 0x3b7c, 0x3066, -0x0f9b, 0x206c, 0x1043, 0xd352, 0xb6fe, 0xb4ae, 0xdacb, 0xdd79, 0xed28, 0x2944, -0x40cc, 0x26e3, 0x1073, 0x2c6d, 0x0a8e, 0xd1fe, 0xbe7c, 0xb79c, 0xdbb0, 0xe28f, -0xf1b8, 0x2782, 0x45d6, 0x288a, 0x146e, 0x3752, 0x08d7, 0xccea, 0xc003, 0xbd89, -0xda98, 0xe4f4, 0x0193, 0x3341, 0x4c65, 0x29eb, 0x1665, 0x385c, 0x0502, 0xcaf3, -0xbfdd, 0xc859, 0xe1b4, 0xe85f, 0x0f40, 0x394b, 0x49b1, 0x2907, 0x1adb, 0x324c, -0xf922, 0xc6e9, 0xb961, 0xcbdd, 0xe7b2, 0xe6bd, 0x11f6, 0x3fef, 0x46fb, 0x1df6, -0x1d0f, 0x2f40, 0xeef5, 0xc4a6, 0xb575, 0xcb9f, 0xe637, 0xea22, 0x1afc, 0x450f, -0x486c, 0x1ca2, 0x2081, 0x27c1, 0xe170, 0xbe81, 0xb610, 0xd4bd, 0xe951, 0xed82, -0x22d8, 0x4920, 0x44fd, 0x1967, 0x24be, 0x230d, 0xdd5b, 0xbd48, 0xb2b2, 0xd74f, -0xebd6, 0xf51e, 0x2d0e, 0x4f15, 0x406d, 0x18ea, 0x2d32, 0x1b37, 0xd68b, 0xc044, -0xb935, 0xdd2b, 0xe8ca, 0xf474, 0x309b, 0x54b6, 0x3c42, 0x14ba, 0x347b, 0x1910, -0xd0a8, 0xbcba, 0xb8c9, 0xdc46, 0xe9fe, 0xfff4, 0x3532, 0x5389, 0x3988, 0x14af, -0x3504, 0x102d, 0xc848, 0xb929, 0xbe6f, 0xdd0e, 0xe430, 0x059c, 0x3ba7, 0x4f58, -0x2e33, 0x10f0, 0x2a2f, 0xfdb9, 0xc2f6, 0xafbb, 0xbb99, 0xe4f6, 0xe5f2, 0x07f0, -0x3ce6, 0x4606, 0x2206, 0x1803, 0x2b80, 0xee52, 0xc24f, 0xb46d, 0xbb4a, 0xe32e, -0xe633, 0x0953, 0x4238, 0x4b00, 0x1a28, 0x1723, 0x280d, 0xe191, 0xbf29, 0xb512, -0xbb95, 0xe083, 0xee77, 0x11e1, 0x3d02, 0x4905, 0x18fb, 0x175c, 0x25d3, 0xdaba, -0xb4bd, 0xb51c, 0xcc2b, 0xe1c5, 0xed1f, 0x1d15, 0x42ae, 0x42e0, 0x12aa, 0x15c7, -0x1ce6, 0xdd86, 0xbb2e, 0xb339, 0xd4c6, 0xe6a3, 0xee5c, 0x246d, 0x4599, 0x378a, -0x117f, 0x247f, 0x1587, 0xd40f, 0xc211, 0xba12, 0xda04, 0xe9d2, 0xf097, 0x2593, -0x4a0d, 0x33f2, 0x0e58, 0x2f92, 0x1796, 0xd23e, 0xc5d1, 0xbd1d, 0xd6a8, 0xea03, -0xfd4b, 0x2b76, 0x4d07, 0x372b, 0x12c1, 0x3610, 0x1455, 0xc9fa, 0xc082, 0xc65f, -0xdbf3, 0xe5fa, 0x0864, 0x3695, 0x4d6d, 0x3441, 0x13be, 0x2f2f, 0x090c, 0xce34, -0xb830, 0xc505, 0xfada, 0xec81, 0xfb68, 0x2eba, 0x319e, 0x3ce2, 0x44fb, 0x1d01, -0xdd9d, 0xd66a, 0xb232, 0xa016, 0xf64b, 0xfff6, 0x09d5, 0x7376, 0x8570, 0x20f6, -0xfe82, 0xf200, 0x9a52, 0xa325, 0xc4f5, 0xcbde, 0x2c79, 0x67bb, 0x4c8f, 0x46a8, -0x523c, 0xf79d, 0xabd0, 0xda12, 0xcc15, 0xb71c, 0xf62d, 0x1e60, 0x327a, 0x4b18, -0x2770, 0xf598, 0x157f, 0x094e, 0xbe89, 0xebf7, 0x2a77, 0xf098, 0xe9ee, 0x28e2, -0xf32a, 0xc056, 0x237c, 0x271d, 0xe4a4, 0x3978, 0x35ba, 0xe536, 0x10f5, 0xfdaa, -0xa68b, 0xd3ea, 0x212f, 0xea07, 0x08a6, 0x5e96, 0x0ae2, 0x07c6, 0x37b6, 0xd70c, -0xb092, 0xfe7d, 0xf21a, 0xcad9, 0x34d2, 0x36e9, 0xf083, 0x37da, 0x22a5, 0xbc01, -0xdc00, 0x1395, 0xd553, 0xe5e3, 0x3bac, 0xfd08, 0xfe92, 0x3f5c, 0xee23, 0xbc45, -0x0d39, 0x0a9c, 0xc61a, 0x146f, 0x2951, 0xdb39, 0x171e, 0x293d, 0xc2e7, 0xdd20, -0x32b9, 0xe330, 0xd7b9, 0x3cc5, 0xf7d9, 0xdcad, 0x311b, 0xf1df, 0xb0b8, 0x105b, -0x1a8c, 0xca51, 0x1be5, 0x2f80, 0xd616, 0x11ca, 0x2652, 0xbc4e, 0xcb96, 0x21a7, -0xe4b4, 0xe253, 0x4487, 0x0196, 0xe674, 0x3516, 0xfb04, 0xb3bc, 0xfeb8, 0x1079, -0xc24e, 0x088e, 0x3298, 0xe80b, 0x14c1, 0x2ed3, 0xd1a8, 0xcf82, 0x2207, 0xee50, -0xcf02, 0x2dec, 0x08af, 0xe42a, 0x344d, 0x0fb2, 0xb971, 0xfa3f, 0x1df7, 0xd32e, -0x11cc, 0x3722, 0xda30, 0x07ac, 0x3d3f, 0xd7ab, 0xc34d, 0x271b, 0xfe76, 0xdbb3, -0x3d61, 0x18ed, 0xed9b, 0x3389, 0x0c14, 0xba7c, 0xf987, 0x187e, 0xcd50, 0x1066, -0x476b, 0xf480, 0x1314, 0x42bb, 0xe5fe, 0xc3be, 0x1389, 0xf39f, 0xd575, 0x3648, -0x1e47, 0xf2fa, 0x3e74, 0x234a, 0xbf4c, 0xf288, 0x221b, 0xc710, 0xfa3c, 0x5035, -0xfb9e, 0x0502, 0x44eb, 0xeee6, 0xc43e, 0x1d61, 0xfb1c, 0xc9de, 0x3c74, 0x2aae, -0xe190, 0x3674, 0x2619, 0xaff2, 0xe8b6, 0x2937, 0xc78c, 0xf5df, 0x540b, 0xfb05, -0x020b, 0x434b, 0xe0e6, 0xacc1, 0x10fc, 0xf91d, 0xbcee, 0x3592, 0x3566, 0xe36e, -0x337d, 0x30bf, 0xb32f, 0xd426, 0x2162, 0xc820, 0xe042, 0x4c56, 0xfe46, 0xf61f, -0x4a89, 0xf803, 0xad4d, 0x11a6, 0x0a6c, 0xb6d1, 0x253a, 0x3311, 0xd5a0, 0x2667, -0x3498, 0xb800, 0xd872, 0x2f83, 0xd41e, 0xe4e1, 0x5463, 0xf738, 0xea30, 0x492c, -0xf007, 0xaab2, 0x16ec, 0x11da, 0xc262, 0x2e37, 0x3e3b, 0xdd03, 0x22dd, 0x30e8, -0xb674, 0xd170, 0x2e2c, 0xdcd6, 0xe399, 0x56b9, 0x04f1, 0xea52, 0x4a44, 0xfd31, -0xa60d, 0x09bf, 0x162d, 0xbcc9, 0x1f55, 0x4352, 0xde68, 0x1cd9, 0x3c0d, 0xbf98, -0xc8d8, 0x2a1f, 0xdc64, 0xd503, 0x4d6c, 0x0785, 0xe7a8, 0x476e, 0xff01, 0xa5cd, -0x064f, 0x17bf, 0xbe55, 0x1800, 0x3b7b, 0xd4d9, 0x10f0, 0x3690, 0xbf8d, 0xc71e, -0x297a, 0xe393, 0xd729, 0x47fe, 0x0285, 0xda51, 0x391b, 0xfeb3, 0xa48e, 0xfbc3, -0x19a9, 0xc3a0, 0x108e, 0x3eae, 0xdebc, 0x0f12, 0x36a2, 0xc4c0, 0xbf91, 0x1e2f, -0xe5de, 0xd395, 0x4354, 0x10d5, 0xe319, 0x39c0, 0x0a87, 0xace4, 0xf7d2, 0x18b7, -0xc582, 0x0b6f, 0x3da5, 0xe290, 0x0dde, 0x3c3e, 0xcfe6, 0xc55b, 0x253d, 0xeca1, -0xcf94, 0x3b7a, 0x0f41, 0xdf74, 0x366b, 0x1370, 0xb644, 0xfb14, 0x1f1e, 0xc7c9, -0x07b4, 0x41e9, 0xe70d, 0x071d, 0x3d77, 0xdb66, 0xc478, 0x265f, 0xf916, 0xd180, -0x3ee6, 0x1beb, 0xdeb9, 0x31d9, 0x191a, 0xb479, 0xf52c, 0x2801, 0xccf7, 0x03d4, -0x4bd7, 0xedac, 0x016a, 0x3a65, 0xd6fc, 0xbe27, 0x2266, 0x07f4, 0xd0c6, 0x0dcf, -0x1281, 0x0eb0, 0x45e8, 0x1d9a, 0xd8e3, 0x05c1, 0x084a, 0xb5be, 0xdaec, 0x09a2, -0xe1b9, 0x11ee, 0x6086, 0x43d5, 0x17db, 0x2892, 0xfb50, 0xb60e, 0xc9f7, 0xc054, -0xc68d, 0x23e0, 0x2ffe, 0x062b, 0x4e99, 0x701f, 0xf80e, 0xd329, 0xff2c, 0xcb90, -0xa2d6, 0xed8c, 0x1910, 0x0699, 0x2f80, 0x3089, 0x1d1c, 0x374f, 0x03b2, 0xc14c, -0xd8b6, 0xfb3d, 0xd617, 0xe1a7, 0x22a2, 0x2021, 0x1a55, 0x1dcb, 0x2025, 0x150c, -0x0753, 0xe11c, 0xb5ad, 0xeb4f, 0xe450, 0xcc01, 0x1b1d, 0x3faa, 0x18aa, 0x0e6d, -0x4970, 0x17e5, 0xcba3, 0xd80b, 0xbb23, 0xc5d9, 0xe755, 0xf01c, 0x158e, 0x5028, -0x3e7d, 0xfcdb, 0x3482, 0x21eb, 0xbae5, 0xbfbe, 0xd2fb, 0xcfc4, 0xd821, 0x0aca, -0x27bb, 0x3924, 0x3d1e, 0x0e86, 0x271a, 0x205b, 0xd16c, 0xaf53, 0xc9df, 0xe377, -0xce6f, 0xfa21, 0x34f1, 0x4083, 0x3410, 0x259d, 0x319d, 0x06b6, 0xd9dc, 0xb750, -0xb2df, 0xde42, 0xd8e4, 0xfe96, 0x4970, 0x54f6, 0x25ba, 0x2520, 0x3c2e, 0xedea, -0xc061, 0xba0d, 0xba48, 0xde9c, 0xeb84, 0x0af2, 0x401e, 0x56b2, 0x1aa5, 0x0f66, -0x36cf, 0xe844, 0xb80e, 0xc1dd, 0xc246, 0xcece, 0xe9fe, 0x177a, 0x32d2, 0x4547, -0x1818, 0x0c52, 0x30b6, 0xec12, 0xaf1a, 0xb2f2, 0xcfa8, 0xd2b1, 0xdf9e, 0x2116, -0x3ab2, 0x35ab, 0x1b48, 0x247c, 0x25e1, 0xdedd, 0xb1e5, 0xaf2b, 0xdb4a, 0xe17b, -0xdfc7, 0x27da, 0x4f71, 0x34be, 0x0ed1, 0x2d58, 0x1ef5, 0xd091, 0xc1bb, 0xbe47, -0xd524, 0xeaee, 0xf469, 0x2112, 0x4cf4, 0x3d3e, 0x0aff, 0x304c, 0x281a, 0xcdc7, -0xbbed, 0xd088, 0xe04a, 0xdeea, 0xfee9, 0x2f94, 0x47fc, 0x3dff, 0x1a27, 0x31e9, -0x1deb, 0xd894, 0xbec6, 0xc6e8, 0xe5fc, 0xe340, 0x014d, 0x3cd1, 0x4e2f, 0x32bd, -0x1d0c, 0x372e, 0x0af1, 0xce88, 0xc502, 0xc97d, 0xe495, 0xeb9c, 0x0798, 0x374a, -0x4e4d, 0x2a7e, 0x16b3, 0x3ddd, 0x08ed, 0xc949, 0xc5ee, 0xce08, 0xdf28, 0xe8b7, -0x0fe9, 0x3689, 0x490a, 0x2817, 0x1714, 0x38c1, 0x0109, 0xc527, 0xc1af, 0xd087, -0xdced, 0xe41e, 0x12dc, 0x3476, 0x3cd2, 0x201b, 0x1cbb, 0x2e40, 0xf269, 0xc5fe, -0xb844, 0xcaac, 0xdf7f, 0xe02f, 0x0f09, 0x373a, 0x383b, 0x13eb, 0x21ef, 0x2cbc, -0xe41a, 0xc568, 0xb82e, 0xc319, 0xdca2, 0xe63b, 0x0f93, 0x3ca2, 0x4202, 0x11af, -0x2392, 0x2ac6, 0xd906, 0xb7d3, 0xb715, 0xce35, 0xdb89, 0xef49, 0x2213, 0x3ff6, -0x3f4b, 0x14d9, 0x2398, 0x1f27, 0xd7ab, 0xb866, 0xb50a, 0xd74b, 0xe11e, 0xf58b, -0x2ece, 0x467f, 0x3963, 0x1933, 0x2caa, 0x1426, 0xd5d8, 0xbe81, 0xb620, 0xdf76, -0xe789, 0xf65e, 0x349f, 0x505c, 0x3366, 0x16ea, 0x3646, 0x0f5a, 0xd18a, 0xc1b2, -0xb7f7, 0xdd2a, 0xeb2f, 0xfd79, 0x30f5, 0x516e, 0x31d6, 0x12af, 0x388a, 0x0be7, -0xca82, 0xbdf0, 0xbc89, 0xda3c, 0xe687, 0x06e8, 0x3695, 0x5204, 0x3323, 0x158c, -0x3447, 0x05f1, 0xca02, 0xb88d, 0xc1cf, 0xe381, 0xea5c, 0x110d, 0x40c6, 0x4e27, -0x2a69, 0x1e8c, 0x30b5, 0xf349, 0xc5eb, 0xb6f0, 0xc440, 0xe7cd, 0xebf2, 0x1281, -0x4308, 0x4ce7, 0x1f25, 0x1bf5, 0x2d5c, 0xe897, 0xc291, 0xb85f, 0xc6be, 0xe556, -0xf257, 0x1cc0, 0x442d, 0x4d14, 0x200a, 0x1f36, 0x26a6, 0xdf96, 0xbef2, 0xb898, -0xd0e1, 0xe9a1, 0xf513, 0x2536, 0x486a, 0x4666, 0x1bd4, 0x216e, 0x1a61, 0xd966, -0xbfd6, 0xb46f, 0xd414, 0xeb0f, 0xf5c1, 0x2917, 0x4b03, 0x3b01, 0x120a, 0x279d, -0x127b, 0xce60, 0xc0b2, 0xbaa1, 0xd93a, 0xea16, 0xf79d, 0x29e1, 0x4e28, 0x3952, -0x0c9b, 0x2918, 0x0da3, 0xc8e3, 0xbf5d, 0xbd5b, 0xd7be, 0xe6b5, 0xff78, 0x2bfe, -0x474e, 0x30ea, 0x0afb, 0x2996, 0x09da, 0xc618, 0xb66b, 0xc002, 0xde8f, 0xe009, -0x0068, 0x34b1, 0x479e, 0x290d, 0x1057, 0x2cf4, 0x0037, 0xc901, 0xba19, 0xc161, -0xe4c1, 0xe626, 0x06b7, 0x3bce, 0x46e6, 0x1fc6, 0x1c43, 0x37dd, 0xf4a3, 0xc6e9, -0xc0da, 0xc57a, 0xe45d, 0xe96e, 0x0b68, 0x3e09, 0x4c53, 0x1c9e, 0x1978, 0x350c, -0xec82, 0xc2fd, 0xc069, 0xc683, 0xdf64, 0xee42, 0x1619, 0x39da, 0x4685, 0x1dc8, -0x1c78, 0x2de4, 0xe52f, 0xbb56, 0xb88c, 0xd10f, 0xe424, 0xed39, 0x226e, 0x45c9, -0x4373, 0x1798, 0x1d16, 0x2213, 0xe050, 0xbf9c, 0xb315, 0xd32f, 0xe7c5, 0xec31, -0x23d2, 0x48dd, 0x38f4, 0x0de0, 0x25fc, 0x1996, 0xcef8, 0xbc82, 0xb55a, 0xd59c, -0xe89f, 0xefe0, 0x23a4, 0x4993, 0x37a4, 0x0c32, 0x2be5, 0x17d1, 0xcddd, 0xc07a, -0xb8e9, 0xd329, 0xe54b, 0xfb4e, 0x2cfc, 0x4d3a, 0x3bad, 0x0fee, 0x2b2b, 0x1047, -0xc712, 0xb4f7, 0xbbf0, 0xdc5f, 0xe2f3, 0xfef6, 0x224e, 0x4c36, 0x6c76, 0x2b5c, -0xfa57, 0xe594, 0xbdb1, 0x8b0a, 0xa7ca, 0x0160, 0x0786, 0x400c, 0x8424, 0x5796, -0x1be9, 0xf540, 0xbdf9, 0x9768, 0xc4df, 0xbd3d, 0xdd3d, 0x63d3, 0x4e65, 0x1fea, -0x4e57, 0x38bb, 0xd91b, 0xcd56, 0xe3ad, 0xbffd, 0xfbd6, 0x0607, 0xdb8e, 0x3ccb, -0x4192, 0xd6b0, 0xf73b, 0x42bf, 0xeff1, 0xe330, 0x3dbd, 0xfa84, 0xdccf, 0x13df, -0xde74, 0xb718, 0x0b45, 0x28d2, 0xfb25, 0x3fa0, 0x4276, 0xedb2, 0x1335, 0x0954, -0xadc9, 0xc6fc, 0x1522, 0xf53c, 0xfe45, 0x4b87, 0x18e7, 0x04a7, 0x3412, 0xee3f, -0xb321, 0xfd48, 0x04c9, 0xc508, 0x15a8, 0x372c, 0xeb2f, 0x165c, 0x2ca0, 0xce55, -0xd34d, 0x1c95, 0xe2fd, 0xdd7e, 0x3294, 0xf905, 0xe452, 0x34a4, 0x04d0, 0xbdd7, -0x0a01, 0x1d14, 0xce45, 0x0c02, 0x2888, 0xd716, 0x03dc, 0x2b4a, 0xd5cd, 0xd68c, -0x2ef2, 0xfc10, 0xda6f, 0x316f, 0x0773, 0xda8e, 0x2215, 0x0507, 0xb6f3, 0xfe70, -0x2a5d, 0xd6c8, 0x0b84, 0x4188, 0xe8c4, 0xfdd0, 0x2ec5, 0xd794, 0xc3a2, 0x2279, -0xf779, 0xd09d, 0x423d, 0x27fc, 0xe6f8, 0x2f40, 0x1b5a, 0xbc8b, 0xf157, 0x1fb4, -0xcaa0, 0xfa3a, 0x4232, 0xf922, 0x0ecb, 0x3f09, 0xe3ee, 0xc318, 0x1cef, 0xfdc1, -0xca4c, 0x2a22, 0x20f1, 0xe87d, 0x279c, 0x1afc, 0xbe72, 0xe720, 0x2220, 0xd1d4, -0xf6a7, 0x4e3e, 0xf5f4, 0xecca, 0x3b54, 0xf567, 0xb06f, 0x0a45, 0x0b98, 0xc973, -0x28f0, 0x2f21, 0xdfeb, 0x24a0, 0x2810, 0xaef4, 0xd207, 0x2ab3, 0xcd51, 0xdc82, -0x4ca3, 0xfdde, 0xef82, 0x40ab, 0xf143, 0xac33, 0x082b, 0xfdac, 0xb9b7, 0x28f3, -0x2b71, 0xd054, 0x2723, 0x3651, 0xb6cf, 0xd176, 0x2ba8, 0xd75d, 0xdb92, 0x450f, -0xfd8f, 0xec9c, 0x3e23, 0xf598, 0xaf02, 0x111a, 0x135a, 0xbd2d, 0x2334, 0x3d0a, -0xd3d8, 0x1768, 0x3bb0, 0xbab9, 0xc676, 0x311a, 0xe06f, 0xd889, 0x5018, 0x070b, -0xe756, 0x4942, 0xfd09, 0x9d71, 0x0bcc, 0x1bed, 0xb4ce, 0x1c66, 0x47ec, 0xdc5f, -0x1b74, 0x4238, 0xc481, 0xcf1b, 0x32f5, 0xe1ce, 0xda85, 0x4e4d, 0x0437, 0xe474, -0x4777, 0x07ed, 0xaff9, 0x127d, 0x24cd, 0xc370, 0x199d, 0x3e29, 0xd8bf, 0x14fd, -0x3d8b, 0xc8ea, 0xd1b6, 0x3766, 0xecd3, 0xda6f, 0x4fa7, 0x0ce7, 0xdddd, 0x4019, -0x0c05, 0xaedb, 0x0dbc, 0x2b4c, 0xcdd3, 0x1ddc, 0x470d, 0xe283, 0x1764, 0x40d4, -0xcabb, 0xcd66, 0x3585, 0xf1c2, 0xdda9, 0x4ffb, 0x11e4, 0xe28a, 0x415d, 0x12d2, -0xb486, 0x055e, 0x1fd4, 0xc66e, 0x12ca, 0x417e, 0xe4e2, 0x1229, 0x3e2a, 0xd10c, -0xc800, 0x29e0, 0xed4e, 0xd10c, 0x3fcc, 0x11a3, 0xe1f9, 0x3ad7, 0x16ca, 0xb727, -0x0155, 0x2400, 0xc8c6, 0x0c22, 0x40bf, 0xe1dc, 0x06e9, 0x3e89, 0xd924, 0xc59a, -0x27a2, 0xf22c, 0xcfad, 0x3f51, 0x14cd, 0xda51, 0x2f15, 0x1235, 0xae68, 0xedef, -0x1983, 0xc602, 0x06d9, 0x46e6, 0xe9de, 0x01cd, 0x3928, 0xd470, 0xb512, 0x15ff, -0xec31, 0xc9cd, 0x3f78, 0x243a, 0xe15e, 0x29ed, 0x1245, 0xaba1, 0xe5e9, 0x199e, -0xc296, 0xfe39, 0x4ac5, 0xeb5f, 0xfa56, 0x3c11, 0xd94f, 0xae50, 0x1511, 0xf001, -0xbde3, 0x36c2, 0x230d, 0xd7fe, 0x2c17, 0x1e3a, 0xaa06, 0xe31f, 0x226e, 0xc144, -0xf626, 0x4f4e, 0xeb1e, 0xf4dd, 0x3e8c, 0xdc0a, 0xaf47, 0x1e56, 0xfed8, 0xc48e, -0x3d56, 0x2b0f, 0xd682, 0x2918, 0x1dec, 0xa955, 0xe5bb, 0x2b6b, 0xc9b8, 0xfa77, -0x56f8, 0xf481, 0xfb61, 0x4479, 0xdf2e, 0xabca, 0x1c70, 0xffe7, 0xbc88, 0x3a59, -0x3826, 0xe054, 0x2f4b, 0x2c11, 0xb1dd, 0xe03e, 0x2b29, 0xc998, 0xf18d, 0x59ee, -0xf7ac, 0xf73d, 0x4d8e, 0xed67, 0xb1d8, 0x21a9, 0x1848, 0xccda, 0x07a8, 0xffcd, -0xfa23, 0x5aea, 0x3797, 0xd62f, 0x0ab0, 0x245e, 0xb225, 0xc27c, 0x09d2, 0xd863, -0xfe56, 0x5bb6, 0x4ddb, 0x2aa0, 0x3bda, 0x0b4a, 0xc172, 0xdcc2, 0xc178, 0xb04a, -0x211f, 0x3a69, 0xf99a, 0x3316, 0x7dfa, 0x1afe, 0xd954, 0x046d, 0xdf49, 0xb051, -0xdb3e, 0x09de, 0x0f9f, 0x347e, 0x2f84, 0x0af2, 0x3895, 0x1ade, 0xc5b6, 0xd468, -0xfe63, 0xe6d0, 0xddae, 0x14b5, 0x175e, 0x16c3, 0x1fae, 0x122e, 0x1fcb, 0x16f5, -0xef7f, 0xbe99, 0xdd8a, 0xf61b, 0xc6ce, 0x019a, 0x43c3, 0x2909, 0x1168, 0x39ad, -0x2f76, 0xde14, 0xddd2, 0xc963, 0xbd2a, 0xea45, 0xea62, 0x0266, 0x4616, 0x4e1c, -0x015e, 0x18de, 0x36f6, 0xcf20, 0xb656, 0xd210, 0xd017, 0xd8b3, 0xfb2f, 0x1950, -0x2b27, 0x3c69, 0x095e, 0x0ec5, 0x2d9c, 0xe85e, 0xb75b, 0xc4ac, 0xe1a3, 0xcfe0, -0xdf2a, 0x1a10, 0x3183, 0x376b, 0x1d4c, 0x237d, 0x185c, 0xe4f3, 0xbf7b, 0xab18, -0xcfc2, 0xd346, 0xe11c, 0x2903, 0x45ea, 0x24aa, 0x102b, 0x353d, 0x080f, 0xc677, -0xbdeb, 0xb32e, 0xd477, 0xe331, 0xef66, 0x2325, 0x4c5c, 0x27b3, 0xfaf5, 0x3106, -0x1252, 0xc709, 0xca81, 0xcbe5, 0xd04d, 0xdd4e, 0xfd3c, 0x1a5f, 0x3c21, 0x2935, -0x0252, 0x3750, 0x1e8e, 0xc8e4, 0xb83e, 0xd48f, 0xdc6e, 0xce04, 0x0585, 0x31bc, -0x360a, 0x2009, 0x14c2, 0x394e, 0x10a3, 0xc93d, 0xafa5, 0xd6b1, 0xef04, 0xcd94, -0x0467, 0x48be, 0x4051, 0x1437, 0x1fb5, 0x3cd3, 0xf79e, 0xcc8f, 0xc153, 0xcd1f, -0xeeb7, 0xe37c, 0x03cf, 0x4174, 0x4958, 0x1000, 0x1d3b, 0x4853, 0xf289, 0xbea9, -0xc9e2, 0xd4e7, 0xde7b, 0xe633, 0x1461, 0x3e75, 0x4911, 0x1b5a, 0x2106, 0x4059, -0xf467, 0xbf92, 0xc1f8, 0xd995, 0xdcf6, 0xe0ba, 0x1eb5, 0x4600, 0x4167, 0x1d25, -0x2ef9, 0x3639, 0xe56b, 0xc105, 0xc027, 0xd70a, 0xe1eb, 0xea01, 0x247e, 0x4c66, -0x3f10, 0x14c8, 0x3245, 0x3094, 0xdd32, 0xc132, 0xc147, 0xd9a1, 0xe18c, 0xeeb8, -0x2824, 0x4a69, 0x395c, 0x1015, 0x3040, 0x238d, 0xd48a, 0xc21b, 0xc53b, 0xdc6d, -0xe1ce, 0xf6ef, 0x2ad6, 0x46b7, 0x34da, 0x1114, 0x34eb, 0x1e8c, 0xd361, 0xbe8c, -0xc1c6, 0xe012, 0xdf71, 0xf81f, 0x3359, 0x4a06, 0x2d71, 0x1454, 0x38da, 0x0f58, -0xccf4, 0xbea1, 0xbbb5, 0xdb25, 0xe2c4, 0xfd77, 0x3627, 0x5121, 0x2c35, 0x15a0, -0x3989, 0x0018, 0xc03f, 0xbaf5, 0xc3cd, 0xe02b, 0xe6c9, 0x0e3d, 0x3eb5, 0x4d58, -0x26cb, 0x1318, 0x2fe0, 0xf891, 0xc27d, 0xb828, 0xc71e, 0xe21d, 0xe699, 0x15c5, -0x4058, 0x45ef, 0x2262, 0x1985, 0x28bc, 0xeca7, 0xc32c, 0xb7e4, 0xcfc1, 0xea07, -0xe4ac, 0x173a, 0x461c, 0x3eba, 0x15fa, 0x2087, 0x2703, 0xe29a, 0xc4b3, 0xb600, -0xc8f7, 0xe687, 0xe9ba, 0x15c4, 0x426b, 0x3e74, 0x0edd, 0x1f75, 0x23b8, 0xd925, -0xc0ab, 0xba1f, 0xcb2d, 0xe095, 0xeca2, 0x1990, 0x40e5, 0x3c5d, 0x1023, 0x24d2, -0x1ebd, 0xd280, 0xbab7, 0xba30, 0xd226, 0xe0d0, 0xf39a, 0x22d6, 0x4063, 0x34fc, -0x0f3b, 0x2825, 0x190f, 0xd310, 0xbcd9, 0xbd2e, 0xd80b, 0xdd5a, 0xf3ce, 0x2b22, -0x4783, 0x32fb, 0x13a4, 0x3361, 0x1484, 0xcf84, 0xc067, 0xc184, 0xdb6a, 0xe3e8, -0x0230, 0x3122, 0x4768, 0x320a, 0x16c1, 0x3427, 0x0aed, 0xc9bb, 0xba81, 0xc328, -0xe12c, 0xe1e3, 0x07e1, 0x3ec1, 0x4f67, 0x2ddc, 0x15f0, 0x2f32, 0xfe24, 0xc964, -0xbbfd, 0xc754, 0xe848, 0xe9ef, 0x1196, 0x41b3, 0x4b5f, 0x2760, 0x1f91, 0x35ce, -0xf3d5, 0xc722, 0xbfad, 0xcade, 0xe85a, 0xeaa5, 0x1660, 0x4809, 0x4c60, 0x1f11, -0x212f, 0x36b0, 0xefd1, 0xc764, 0xbc28, 0xc837, 0xe455, 0xec50, 0x1c88, 0x4b3c, -0x4de3, 0x2041, 0x28c6, 0x32d7, 0xe1ea, 0xbea7, 0xbd29, 0xd1a9, 0xe60c, 0xf02d, -0x2433, 0x4c51, 0x4811, 0x1816, 0x23ce, 0x292b, 0xdf35, 0xbcec, 0xb6b3, 0xd50d, -0xe49a, 0xee6a, 0x2b0c, 0x4b27, 0x39f9, 0x15f5, 0x29d8, 0x1859, 0xd36e, 0xc04c, -0xb5ab, 0xd4b0, 0xe461, 0xec0c, 0x2803, 0x4fb2, 0x371f, 0x0f49, 0x2de8, 0x1063, -0xc7dd, 0xbbcc, 0xb195, 0xcdad, 0xe2b5, 0xf6ed, 0x29d0, 0x4ac5, 0x31e4, 0x0aa9, -0x2acb, 0x0735, 0xbe72, 0xb39c, 0xb620, 0xd1d9, 0xdc5c, 0xfcdd, 0x307a, 0x46be, -0x2c7c, 0x0a2b, 0x22ab, 0xfca8, 0xc0ac, 0xafc4, 0xb815, 0xde74, 0xe13f, 0x0269, -0x370e, 0x4107, 0x21c4, 0x13c8, 0x26a3, 0xf061, 0xc3ac, 0xb5ad, 0xbbea, 0xe2b6, -0xe314, 0x03cd, 0x3a8a, 0x453e, 0x1c0b, 0x167a, 0x2afd, 0xe8ae, 0xc330, 0xbb1d, -0xbd71, 0xdff6, 0xea8c, 0x0cd6, 0x3cc1, 0x4b59, 0x1e08, 0x1762, 0x288a, 0xe327, -0xbcf5, 0xbc04, 0xcc41, 0xe3b8, 0xf214, 0x1f63, 0x40b7, 0x4556, 0x1932, 0x14e9, -0x22d4, 0xe5af, 0xc176, 0xbaf1, 0xd516, 0xeaa3, 0xed1a, 0x10a5, 0x4490, 0x6d0e, -0x387d, 0x06ad, 0xf979, 0xc011, 0x9e3c, 0xad92, 0xe582, 0x1234, 0x4531, 0x6a71, -0x4d3b, 0x3443, 0xfe14, 0xbf35, 0xb76f, 0xcbe7, 0xc8b4, 0xe9aa, 0x501f, 0x43d8, -0x1d18, 0x4560, 0x2e0d, 0xf4de, 0xea75, 0xe7b7, 0xd73f, 0x0fdc, 0x11cd, 0xd145, -0x29c3, 0x44d4, 0xdad6, 0xf4f0, 0x3e51, 0x092e, 0x07b6, 0x43ce, 0xfd5a, 0xf591, -0x1f19, 0xc2ea, 0xb2d7, 0x198e, 0x140f, 0xfa53, 0x5e12, 0x4564, 0xf1ae, 0x2e33, -0x0b49, 0xa4bb, 0xdaed, 0x1485, 0xdc90, 0x0cbf, 0x4f0d, 0x0854, 0x15b5, 0x3b75, -0xe6b5, 0xbe4e, 0x0981, 0xfc23, 0xc6c6, 0x22a2, 0x2984, 0xf1db, 0x27f9, 0x1ab0, -0xc60e, 0xe49e, 0x21a8, 0xd681, 0xe953, 0x41f4, 0xf337, 0xeb9d, 0x3560, 0xf514, -0xbd12, 0x135a, 0x1144, 0xc99f, 0x21e0, 0x2271, 0xd155, 0x1a35, 0x25a3, 0xbec7, -0xdc7d, 0x31ed, 0xe802, 0xe46d, 0x3ef6, 0xfc72, 0xeb7d, 0x2fc5, 0xecf1, 0xb132, -0x0d6d, 0x171d, 0xcaf3, 0x20be, 0x36cd, 0xdeff, 0x1430, 0x2c97, 0xc49c, 0xcab8, -0x247c, 0xe2b5, 0xd3f1, 0x3d93, 0x0c22, 0xee37, 0x3907, 0x01ea, 0xb32c, 0x07e5, -0x153f, 0xb7d0, 0x0a61, 0x333d, 0xdb8a, 0x1138, 0x3892, 0xcfca, 0xccd1, 0x2831, -0xeb12, 0xd73f, 0x357e, 0xfe1d, 0xe21b, 0x300a, 0xfded, 0xaf91, 0xffaf, 0x1ba1, -0xc5b1, 0x0c52, 0x3c73, 0xe33f, 0xfac8, 0x2338, 0xd7bc, 0xc7f4, 0x0f99, 0xe739, -0xd9e0, 0x3980, 0x0e6d, 0xe382, 0x335e, 0x15f9, 0xae82, 0xe698, 0x1c86, 0xca1d, -0xf6f1, 0x34d2, 0xf14d, 0x0fec, 0x3c9a, 0xdef1, 0xc8df, 0x20f3, 0xea68, 0xc50a, -0x33ef, 0x1185, 0xd7d4, 0x2ebc, 0x2178, 0xc1c1, 0xf72b, 0x2354, 0xd2eb, 0x0168, -0x30db, 0xe0a6, 0x05a6, 0x3bad, 0xdc09, 0xc45f, 0x276d, 0xfd11, 0xcf0a, 0x363e, -0x204b, 0xe4d0, 0x2851, 0x199c, 0xbc3c, 0xe9df, 0x1c38, 0xcf05, 0x03b3, 0x4c0e, -0xf6ee, 0x068d, 0x44a0, 0xe3e0, 0xb163, 0x17fa, 0xfdee, 0xc709, 0x37cd, 0x2e63, -0xea1c, 0x3531, 0x2ca0, 0xbf9e, 0xeeec, 0x2b06, 0xc5f7, 0xf1c8, 0x5385, 0xfc84, -0x0002, 0x49cb, 0xf52b, 0xbfbf, 0x204d, 0x04c6, 0xc649, 0x35e9, 0x30b9, 0xe658, -0x31b2, 0x2cb7, 0xb96b, 0xe5a8, 0x2f24, 0xd410, 0xf6ab, 0x5771, 0x04b9, 0xfc36, -0x3ec2, 0xecd7, 0xb28a, 0x1904, 0x0954, 0xc24d, 0x3764, 0x3ade, 0xddf2, 0x29ce, -0x3445, 0xb4d5, 0xd352, 0x2d79, 0xd037, 0xe09a, 0x535a, 0x01a5, 0xf095, 0x48ef, -0xf767, 0xa8aa, 0x11b3, 0x097b, 0xb15c, 0x2a36, 0x3db4, 0xd6cd, 0x24b8, 0x3712, -0xb430, 0xcdb4, 0x2b60, 0xd3aa, 0xe09b, 0x53eb, 0xfc4a, 0xea6b, 0x4b2a, 0xf90e, -0xa8c8, 0x1124, 0x129b, 0xbaac, 0x269d, 0x4030, 0xda96, 0x20e1, 0x3a2e, 0xbd1c, -0xd0d8, 0x3158, 0xdd88, 0xdee5, 0x57dc, 0x0a22, 0xeb70, 0x4be8, 0x027e, 0xa602, -0x0680, 0x1714, 0xbfed, 0x242e, 0x47b1, 0xe011, 0x1d8e, 0x3eca, 0xc029, 0xc616, -0x2e06, 0xe28f, 0xd4aa, 0x4f9e, 0x0f06, 0xe649, 0x438d, 0x0564, 0xa6f1, 0x02f6, -0x1bd3, 0xc020, 0x18b7, 0x4204, 0xd71b, 0x0fc8, 0x3e52, 0xc2b8, 0xbf78, 0x2bd7, -0xe6d3, 0xce7b, 0x47d7, 0x0a5d, 0xd7d4, 0x39c7, 0x074e, 0xa273, 0xfc77, 0x1fef, -0xbcf9, 0x0cec, 0x426a, 0xd4f2, 0x044f, 0x3d93, 0xc6ea, 0xbcdf, 0x2bd1, 0xed0b, -0xce10, 0x43d0, 0x0ca2, 0xd5d2, 0x35c4, 0x0c15, 0xa646, 0xfa2d, 0x2097, 0xc0dd, -0x0c2b, 0x439b, 0xdc9c, 0x059f, 0x3b30, 0xca4a, 0xbbcb, 0x2337, 0xe7a3, 0xc717, -0x3dae, 0x1491, 0xdc09, 0x3229, 0x0f3e, 0xac38, 0xf62a, 0x25d7, 0xcda9, 0xe86a, -0x0ccd, 0xe87d, 0x2a51, 0x46ee, 0xea42, 0xe40d, 0x22b1, 0xd631, 0xa84b, 0xf868, -0xe4f9, 0xdc35, 0x395d, 0x50be, 0x1fd6, 0x25ac, 0x1e57, 0xc641, 0xc358, 0xd0f2, -0xac56, 0xf1aa, 0x385d, 0x08e0, 0x0c0c, 0x6c90, 0x3d9a, 0xd534, 0xf575, 0xf32f, -0xb660, 0xc715, 0x00c3, 0x060a, 0x1cc0, 0x3bee, 0x0fef, 0x2648, 0x390e, 0xe520, -0xcaa4, 0xfc07, 0xfa8c, 0xcfc3, 0xff3b, 0x2146, 0x0eaa, 0x22f7, 0x21eb, 0x22a5, -0x2b3f, 0x140f, 0xd035, 0xce0d, 0x018b, 0xcdfd, 0xe402, 0x4024, 0x35a0, 0x11b4, -0x30f7, 0x48c6, 0xfc36, 0xe429, 0xd9e4, 0xb819, 0xe9ae, 0xecf6, 0xeaac, 0x339b, -0x5af6, 0x18c2, 0x0a7d, 0x49f9, 0xfaf8, 0xba8d, 0xd5ae, 0xd0dc, 0xd43b, 0xee6d, -0x117b, 0x23d1, 0x46ee, 0x29ad, 0x0339, 0x3a96, 0x1314, 0xc110, 0xbad0, 0xd9cf, -0xd706, 0xcd74, 0x0d91, 0x315e, 0x3f9c, 0x359b, 0x2190, 0x2aef, 0x0433, 0xcdb2, -0xa574, 0xc711, 0xe12e, 0xcf40, 0x197f, 0x5324, 0x3d77, 0x20a5, 0x375c, 0x2d03, -0xe262, 0xca54, 0xb0b0, 0xc640, 0xed6e, 0xe3a9, 0x14a7, 0x559a, 0x4789, 0x0af5, -0x2c0d, 0x3078, 0xd574, 0xc8f1, 0xc60c, 0xc4a3, 0xddbb, 0xf434, 0x12bf, 0x3a4f, -0x46b5, 0x0de9, 0x28f8, 0x3a3e, 0xd862, 0xb36e, 0xc4b9, 0xd290, 0xcc9e, 0xf150, -0x28e5, 0x36b7, 0x3727, 0x1942, 0x296a, 0x1f3b, 0xd304, 0xaa50, 0xb7e0, 0xe064, -0xcd60, 0xe774, 0x36d2, 0x4354, 0x256f, 0x16b9, 0x32a9, 0x04e9, 0xc94b, 0xbd89, -0xb4e8, 0xde63, 0xe6b3, 0xefc2, 0x2c99, 0x4d30, 0x244c, 0x0dd9, 0x4013, 0x0a25, -0xbf81, 0xc647, 0xc8d3, 0xd098, 0xddd7, 0x0729, 0x2f46, 0x49fe, 0x3186, 0x11dd, -0x33c9, 0x0a1a, 0xc455, 0xb4fe, 0xcbf9, 0xe01f, 0xd9c3, 0x0d63, 0x3ca9, 0x40e4, -0x2597, 0x1f85, 0x2fee, 0xf6c7, 0xc763, 0xb733, 0xcbd2, 0xe734, 0xe337, 0x11ef, -0x4379, 0x4377, 0x1865, 0x216c, 0x32c8, 0xee0c, 0xca47, 0xc04a, 0xd076, 0xe70b, -0xebed, 0x16a9, 0x3f81, 0x44ae, 0x16f6, 0x2266, 0x31f0, 0xe6d6, 0xc30d, 0xc333, -0xd87e, 0xe495, 0xf14b, 0x1f91, 0x3ab4, 0x3a37, 0x1822, 0x291b, 0x2bcf, 0xea2c, -0xc762, 0xbf57, 0xdd08, 0xe181, 0xec7b, 0x28f2, 0x456d, 0x366d, 0x1d72, 0x3891, -0x2266, 0xe229, 0xcd55, 0xbe26, 0xdad8, 0xe6b2, 0xf1ad, 0x2a59, 0x4ce7, 0x346f, -0x18da, 0x3eb3, 0x1a5c, 0xd0e6, 0xc45c, 0xbfe1, 0xd4d3, 0xe11f, 0x014a, 0x2f81, -0x4863, 0x3408, 0x1268, 0x31f4, 0x0ec8, 0xca3c, 0xb9fc, 0xc1dc, 0xdd52, 0xe093, -0x05ff, 0x33c3, 0x43d7, 0x2da7, 0x1671, 0x2bf0, 0x0034, 0xc97d, 0xb0c3, 0xbe5e, -0xe43b, 0xdb68, 0x0387, 0x3ae6, 0x3ee7, 0x1b43, 0x174b, 0x2d76, 0xf26e, 0xc68d, -0xb289, 0xbb76, 0xe0fe, 0xde57, 0x0305, 0x3b0e, 0x44f7, 0x17e8, 0x1ae0, 0x2e9e, -0xe6b9, 0xc23e, 0xb5b3, 0xbe35, 0xde8a, 0xe55c, 0x0e04, 0x3e2d, 0x4749, 0x1891, -0x1cce, 0x2c46, 0xe31a, 0xbc10, 0xb2be, 0xc7d4, 0xe1b0, 0xec58, 0x1b5f, 0x42a4, -0x4577, 0x18af, 0x206d, 0x2610, 0xe0cf, 0xbee7, 0xb63b, 0xd3ec, 0xe4ab, 0xed6c, -0x253f, 0x4a99, 0x42a8, 0x18da, 0x2a90, 0x2130, 0xd967, 0xc013, 0xb623, 0xd3d0, -0xe7d4, 0xf7c7, 0x2d1b, 0x4df4, 0x3dfe, 0x18d3, 0x2e94, 0x12e1, 0xceb7, 0xbc8b, -0xb721, 0xd9e6, 0xe7b6, 0xfe42, 0x37c7, 0x5256, 0x3916, 0x14fa, 0x2b19, 0x0829, -0xcbbc, 0xbc11, 0xba4e, 0xe0c8, 0xec3c, 0x04ba, 0x39d7, 0x4f48, 0x314f, 0x1b2a, -0x320f, 0xfbd4, 0xc7ad, 0xbe46, 0xbcb0, 0xdfec, 0xea9d, 0x0a76, 0x3eae, 0x51ce, -0x28b8, 0x15c7, 0x32b1, 0xf736, 0xc436, 0xbbe2, 0xc2bd, 0xe357, 0xecf4, 0x10c4, -0x40f6, 0x524e, 0x25cd, 0x19ce, 0x301e, 0xec1e, 0xbc6a, 0xbace, 0xd1d8, 0xe825, -0xec23, 0x1b1b, 0x4400, 0x482a, 0x1bce, 0x1896, 0x260d, 0xe7f6, 0xc325, 0xb5e4, -0xd101, 0xeabf, 0xea0d, 0x1d52, 0x47b3, 0x3cf0, 0x1282, 0x235d, 0x2050, 0xd9ae, -0xc4d9, 0xb970, 0xd086, 0xe87d, 0xea86, 0x1b23, 0x48b1, 0x3c02, 0x0e92, 0x2cb2, -0x2472, 0xd2cb, 0xbff8, 0xbc43, 0xce22, 0xe2d3, 0xf745, 0x2364, 0x47bb, 0x3e11, -0x1209, 0x2f53, 0x1fb0, 0xcfac, 0xba0d, 0xbeea, 0xd8e3, 0xde1c, 0xf854, 0x2f24, -0x49ea, 0x387f, 0x14be, 0x2de7, 0x1304, 0xd01b, 0xb948, 0xbc56, 0xe0d1, 0xe484, -0xff93, 0x3655, 0x488a, 0x2bf5, 0x16ba, 0x3637, 0x07aa, 0xcf32, 0xc24d, 0xc063, -0xe449, 0xe713, 0x01e8, 0x3b25, 0x4f83, 0x26c5, 0x1640, 0x3ab8, 0xff8c, 0xc83c, -0xbf7c, 0xc052, 0xdf32, 0xe807, 0x09ef, 0x390f, 0x4d01, 0x25f5, 0x19b1, 0x3685, -0xf201, 0xbb2e, 0xb561, 0xc59a, 0xdeac, 0xe81d, 0x110d, 0x2f0d, 0x5faf, 0x5bc4, -0x12fe, 0xf615, 0xda50, 0xa9ae, 0x886f, 0xcd18, 0x0718, 0x09f1, 0x5bb7, 0x7af7, -0x3e15, 0x0a73, 0xe799, 0xa715, 0x9c71, 0xc971, 0xb0a1, 0x07a7, 0x6f82, 0x3163, -0x2a82, 0x5398, 0x14c9, 0xbfc2, 0xd8b3, 0xd540, 0xc5ce, 0x0fda, 0xf099, 0xf1a7, -0x518e, 0x1c60, 0xcd84, 0x19da, 0x35b8, 0xd346, 0x03a1, 0x377b, 0xdf14, 0xefc5, -0x0c59, 0xc206, 0xc448, 0x2196, 0x11ee, 0xfe70, 0x4f48, 0x1fa8, 0xeb2d, 0x1d5b, -0xeaf2, 0x9f51, 0xdaef, 0x10e0, 0xe2b8, 0x140e, 0x4a7c, 0x0548, 0x11c6, 0x2b5c, -0xd063, 0xb9e9, 0x0c49, 0xee9f, 0xcd6b, 0x32da, 0x2204, 0xe702, 0x2c36, 0x195d, -0xbc4d, 0xef56, 0x1ea6, 0xd1a8, 0xfb24, 0x33f8, 0xe2fa, 0xffb5, 0x3ccd, 0xe4cb, -0xca25, 0x2563, 0x0355, 0xd2da, 0x2bb0, 0x137a, 0xd6ec, 0x2218, 0x1b95, 0xc3b7, -0xf854, 0x3722, 0xe6e8, 0xf671, 0x3596, 0xec18, 0xf030, 0x2d9c, 0xe6e7, 0xc2e9, -0x26d5, 0x1a49, 0xd733, 0x2bbd, 0x26a1, 0xdab2, 0x1ba0, 0x23f0, 0xc355, 0xe703, -0x2bce, 0xdc49, 0xf190, 0x496d, 0xfc6e, 0xf386, 0x3dfc, 0xf7e9, 0xbcff, 0x1444, -0x0996, 0xc579, 0x1fc6, 0x2a9c, 0xe722, 0x261f, 0x2af6, 0xc74c, 0xe3b8, 0x2a8b, -0xe105, 0xe8c4, 0x3bef, 0xfd9f, 0xefc2, 0x313f, 0xf888, 0xbeed, 0x0f0d, 0x10ba, -0xcf13, 0x2814, 0x3a0d, 0xdf51, 0x0dca, 0x2f8f, 0xccfa, 0xc831, 0x1d1f, 0xe940, -0xe8cf, 0x4a93, 0x0d52, 0xef83, 0x3c76, 0xfaba, 0xa5dd, 0x0126, 0x112f, 0xbdc7, -0x1a61, 0x422a, 0xe9fb, 0x1b1a, 0x3694, 0xc9c9, 0xce2b, 0x1ef2, 0xd732, 0xdc7b, -0x46da, 0xff7f, 0xe9fc, 0x46e1, 0x05ac, 0xaeb6, 0x0663, 0x159d, 0xc012, 0x1316, -0x34ba, 0xdd94, 0x12fd, 0x31f6, 0xc596, 0xcbff, 0x2ae2, 0xe4d5, 0xd6c1, 0x4927, -0x0b3d, 0xdd77, 0x3926, 0x07d1, 0xa2d2, 0xfb2e, 0x211d, 0xc317, 0x1769, 0x4b14, -0xe280, 0x1326, 0x411c, 0xc1e2, 0xbac5, 0x2cf6, 0xe603, 0xcb4b, 0x4ed8, 0x191b, -0xe401, 0x458d, 0x1545, 0xac87, 0x005f, 0x1e4a, 0xba9b, 0x0cc0, 0x467e, 0xe1cf, -0x11da, 0x478e, 0xd350, 0xc70d, 0x31c1, 0xf201, 0xce1e, 0x434c, 0x12d6, 0xdd0b, -0x3986, 0x1337, 0xaf6d, 0x006c, 0x2ca7, 0xc8ff, 0x0c23, 0x4bde, 0xe11d, 0x0318, -0x41ef, 0xd0e5, 0xbd29, 0x3042, 0xf8a0, 0xd014, 0x4ad2, 0x1b53, 0xda3d, 0x3aa3, -0x14be, 0xa4bb, 0xf7a1, 0x2b27, 0xc43a, 0x0948, 0x507e, 0xe296, 0x0297, 0x45db, -0xd510, 0xb990, 0x273a, 0xf430, 0xc750, 0x3f4c, 0x1a85, 0xdb6a, 0x3993, 0x1ab4, -0xadf3, 0xf5f6, 0x280d, 0xc5fc, 0xfe8a, 0x4668, 0xe58d, 0x011c, 0x457a, 0xdfc7, -0xbebe, 0x255e, 0xfae1, 0xcb02, 0x3a3a, 0x1b49, 0xd635, 0x2c90, 0x1a6d, 0xb0f1, -0xf0b7, 0x2851, 0xcb11, 0xfd3b, 0x4815, 0xe902, 0xf73a, 0x3af3, 0xde3a, 0xb633, -0x1a27, 0xfae7, 0xc8e7, 0x3373, 0x1efa, 0xdab3, 0x2af8, 0x1f54, 0xb3a4, 0xe5bd, -0x2010, 0xc657, 0xef33, 0x4332, 0xf32a, 0xf8bc, 0x3a43, 0xe716, 0xb380, 0x1025, -0xf94b, 0xbff8, 0x2cbe, 0x2722, 0xd958, 0x2238, 0x24de, 0xb4e0, 0xd8e0, 0x219f, -0xcbf4, 0xec3e, 0x4cc4, 0xf5db, 0xee65, 0x3cb1, 0xe88f, 0xad6b, 0x15fb, 0x0312, -0xbe65, 0x3207, 0x3441, 0xd731, 0x1ee6, 0x2923, 0xb321, 0xd7af, 0x26e1, 0xce3a, -0xeb73, 0x52d6, 0xfb3a, 0xed11, 0x3fa2, 0xeb93, 0xa537, 0x0fde, 0x078b, 0xbcc7, -0x2dd2, 0x39bf, 0xde73, 0x2216, 0x2ba5, 0xb204, 0xd113, 0x2873, 0xd059, 0xe433, -0x5331, 0x00f5, 0xede6, 0x41c9, 0xf3f8, 0xaa8c, 0x0f24, 0x06be, 0xb5e6, 0x29e9, -0x39f4, 0xda5d, 0x25b6, 0x2b84, 0xb07f, 0xd942, 0x36fe, 0xe8ec, 0xcdd1, 0x1733, -0x0849, 0x1aa1, 0x4558, 0xfb48, 0xd3ef, 0x11bd, 0xef48, 0xb142, 0xf697, 0xfdea, -0xd64a, 0x32a6, 0x6fed, 0x25f8, 0x0b17, 0x2a5c, 0xdff3, 0xaef4, 0xd4ec, 0xbcba, -0xe25d, 0x44cb, 0x234b, 0x1321, 0x73ba, 0x4da9, 0xced4, 0xe817, 0x007f, 0xa908, -0xb5df, 0x1df6, 0x1875, 0x0e2c, 0x3ec1, 0x2fcd, 0x272d, 0x2290, 0xe25c, 0xc07e, -0xf134, 0xfb4d, 0xd6f2, 0x0ad3, 0x3447, 0x223c, 0x13f1, 0x2272, 0x269f, 0x025a, -0x0161, 0xd722, 0xd224, 0x0308, 0xde38, 0xef78, 0x3c18, 0x42ed, 0x0555, 0x290a, -0x5400, 0xe6be, 0xca2d, 0xdae9, 0xc68f, 0xd7d0, 0xf47c, 0x10ed, 0x3184, 0x5941, -0x1cf6, 0x0891, 0x3a35, 0xeb6f, 0xae39, 0xc817, 0xe6f7, 0xd6ef, 0xebb0, 0x3372, -0x35f1, 0x34dd, 0x1e90, 0x16f7, 0x21a1, 0xf074, 0xc287, 0xb8c4, 0xe82d, 0xe6eb, -0xdb90, 0x1e19, 0x40af, 0x3208, 0x1a9a, 0x320f, 0x1b9a, 0xdeff, 0xcf46, 0xb4a3, -0xcbd7, 0xdf68, 0xe59a, 0x22fe, 0x55b5, 0x3892, 0x0be2, 0x396b, 0x1a98, 0xc635, -0xc146, 0xc092, 0xd361, 0xe7eb, 0x0383, 0x2879, 0x4c6a, 0x3c0d, 0x05ca, 0x2976, -0x14a9, 0xc798, 0xc116, 0xd2d8, 0xdd50, 0xdcd3, 0x0b09, 0x2bce, 0x3653, 0x2ab8, -0x0a52, 0x2aa9, 0x14b7, 0xd401, 0xb890, 0xcc60, 0xea88, 0xd5e4, 0xfe7d, 0x383b, -0x3ade, 0x1b88, 0x1ad7, 0x3ed1, 0x075f, 0xcd9c, 0xbbc4, 0xcbe8, 0xeb85, 0xd822, -0xfbc3, 0x3e44, 0x459b, 0x10ea, 0x17ee, 0x43df, 0xf7f9, 0xc0a0, 0xc419, 0xcf1f, -0xdea4, 0xe05f, 0x0678, 0x330f, 0x42d4, 0x111c, 0x0fba, 0x3f57, 0xf9a8, 0xb669, -0xbe1b, 0xe146, 0xdd9a, 0xd4be, 0x11a3, 0x389d, 0x37d6, 0x13b2, 0x1c70, 0x3370, -0xf10c, 0xc063, 0xb6e6, 0xd48a, 0xdc66, 0xd809, 0x1789, 0x3f92, 0x3566, 0x0e0f, -0x231e, 0x2ea3, 0xde28, 0xbb31, 0xba09, 0xcf05, 0xd866, 0xdfc8, 0x18f1, 0x3ecd, -0x369b, 0x0a8f, 0x2386, 0x2af2, 0xda2a, 0xb79e, 0xb7d0, 0xd687, 0xde35, 0xe721, -0x22ba, 0x4574, 0x3572, 0x0dd3, 0x2e06, 0x263b, 0xd7ab, 0xbfc7, 0xc2f6, 0xdf1b, -0xe1c3, 0xf1d5, 0x2cb4, 0x4e1c, 0x37cd, 0x115d, 0x35cd, 0x1fe6, 0xd779, 0xc301, -0xc58e, 0xe72d, 0xe5ce, 0xf960, 0x335a, 0x4fb4, 0x357b, 0x1898, 0x426e, 0x1cc2, -0xd473, 0xc727, 0xc831, 0xe1fb, 0xe68f, 0x072e, 0x3da1, 0x58d4, 0x39ac, 0x1d25, -0x4338, 0x1401, 0xcf2b, 0xbee6, 0xca41, 0xe875, 0xe64f, 0x1316, 0x4982, 0x5236, -0x2f67, 0x2085, 0x38b8, 0xfd01, 0xc7c8, 0xba93, 0xc979, 0xebc7, 0xea26, 0x168d, -0x477d, 0x4817, 0x212b, 0x1fdd, 0x335a, 0xef3e, 0xc484, 0xb895, 0xc9b8, 0xec42, -0xe805, 0x136b, 0x4a61, 0x4971, 0x17b6, 0x1f6a, 0x2f97, 0xe4b1, 0xc086, 0xb999, -0xcac9, 0xe5fd, 0xee0c, 0x1807, 0x412f, 0x460f, 0x166d, 0x1f4c, 0x29b3, 0xdc64, -0xb965, 0xb861, 0xd211, 0xe147, 0xeb99, 0x2193, 0x450f, 0x3ce4, 0x113e, 0x1f63, -0x1c84, 0xd769, 0xb874, 0xb487, 0xd682, 0xe1c9, 0xed74, 0x24b0, 0x43a4, 0x33ba, -0x0e1c, 0x2413, 0x10f7, 0xcdd3, 0xb977, 0xb8d7, 0xdbc4, 0xe37c, 0xf175, 0x272e, -0x457d, 0x2d0a, 0x07d0, 0x297e, 0x0f41, 0xca51, 0xb9f7, 0xbc9a, 0xdaa7, 0xddfe, -0xf766, 0x2c64, 0x4532, 0x2d7b, 0x0dd9, 0x2cf4, 0x0c17, 0xc90f, 0xb4bc, 0xc07f, -0xe270, 0xdce1, 0xfc63, 0x371a, 0x4749, 0x2579, 0x1187, 0x2dc1, 0xff1f, 0xc7eb, -0xb7f0, 0xc150, 0xe209, 0xe0bb, 0x0562, 0x39a6, 0x419b, 0x18df, 0x11d0, 0x32b0, -0xf640, 0xc162, 0xbaa8, 0xc92a, 0xe355, 0xdf42, 0x07ce, 0x3de6, 0x485f, 0x1a8d, -0x15e0, 0x345d, 0xf2a7, 0xc2b4, 0xbc48, 0xcb43, 0xe331, 0xe771, 0x171d, 0x41c7, -0x46e8, 0x1e6a, 0x1d99, 0x3169, 0xea47, 0xbb34, 0xb612, 0xcfe5, 0xe372, 0xe4f4, -0x1dce, 0x4978, 0x4538, 0x1abb, 0x1ff9, 0x2726, 0xe2ee, 0xbf5a, 0xb642, 0xd6a7, -0xeb65, 0xe981, 0x26c2, 0x5246, 0x3d0f, 0x142e, 0x2f6c, 0x28fc, 0xda88, 0xc3aa, -0xbdbe, 0xd749, 0xeb54, 0xf1e0, 0x25cd, 0x526c, 0x41ba, 0x10bf, 0x2fe6, 0x2449, -0xd374, 0xbfc4, 0xbbe2, 0xd283, 0xe25c, 0xf99f, 0x2cef, 0x4ac7, 0x3ddf, 0x17d9, -0x3082, 0x1925, 0xce89, 0xb6a1, 0xbc50, 0xe283, 0xe474, 0xfd8d, 0x3bcc, 0x4e96, -0x33c3, 0x167a, 0x2e47, 0x0a43, 0xcef1, 0xbc29, 0xba3d, 0xe1a3, 0xe5f7, 0xfe7c, -0x39db, 0x4bea, 0x270e, 0x128b, 0x30de, 0xfb59, 0xc505, 0xbdab, 0xbd60, 0xe0c5, -0xe64a, 0x0171, 0x35c3, 0x47fc, 0x21b5, 0x11b5, 0x30fa, 0xf4fc, 0xc06e, 0xbaa3, -0xbc7a, 0xd83f, 0xe4e1, 0x0b66, 0x373d, 0x497a, 0x2421, 0x109c, 0x26f1, 0xecb6, -0xbadf, 0xb11f, 0xc45d, 0xdf59, 0xe4b2, 0x1641, 0x3f59, 0x42bc, 0x1c1c, 0x129c, -0x1bee, 0xe133, 0xbe64, 0xb1ad, 0xcc9f, 0xec32, 0xe986, 0x19ae, 0x44e5, 0x3a19, -0x0e88, 0x1ade, 0x1f2c, 0xdec4, 0xc3cc, 0xb2ef, 0xdaab, 0xfd08, 0xe6a7, 0x1123, -0x3704, 0x3898, 0x3ad4, 0x346f, 0xfe92, 0xd1d3, 0xcc04, 0x9cbd, 0xc135, 0x02eb, -0xf63b, 0x365b, 0x8b58, 0x58ad, 0x0415, 0x0488, 0xcf1c, 0x8e64, 0xb611, 0xc2c4, -0xeda1, 0x4ea8, 0x63da, 0x49bb, 0x50ec, 0x3618, 0xc859, 0xb818, 0xe010, 0xbc83, -0xd14a, 0x1ad8, 0x32a9, 0x4030, 0x4434, 0x0f51, 0xf80f, 0x1895, 0xeaf8, 0xc48d, -0x175a, 0x2702, 0xe895, 0x0fd2, 0x2756, 0xcf30, 0xdfad, 0x3a11, 0xfb80, 0xfa5f, -0x565f, 0x121d, 0xf290, 0x2352, 0xddf3, 0xa5f4, 0xfe06, 0x187b, 0xde96, 0x3a13, -0x5105, 0xf698, 0x255a, 0x212e, 0xb7a3, 0xc5a7, 0x10d7, 0xdeff, 0xe90b, 0x551a, -0x1708, 0xfd81, 0x42e8, 0xf9ae, 0xb328, 0xf8c8, 0x0cd4, 0xc910, 0x1398, 0x3cf8, -0xe89b, 0x1c9d, 0x36c1, 0xcb0b, 0xcca6, 0x2759, 0xf1e2, 0xd264, 0x3d80, 0x146d, -0xe372, 0x36c2, 0x0ab3, 0xb2ef, 0x0a0e, 0x2e96, 0xc7be, 0x0dcf, 0x4ba5, 0xdcd8, -0xff1f, 0x391f, 0xcf51, 0xc4c3, 0x314b, 0xfef3, 0xd680, 0x41c6, 0x156e, 0xdd3b, -0x2eeb, 0x0886, 0xa9c9, 0xf3a6, 0x247e, 0xc927, 0x069f, 0x4a37, 0xe895, 0x0061, -0x378e, 0xd6a1, 0xb348, 0x1688, 0xf810, 0xc498, 0x323f, 0x1fc5, 0xe066, 0x29b3, -0x139d, 0xadbb, 0xe71a, 0x2c44, 0xcae3, 0xe8e1, 0x3f89, 0xe822, 0xf034, 0x367a, -0xdf30, 0xb32c, 0x1aba, 0x0281, 0xcc1b, 0x36c4, 0x14ca, 0xc6d5, 0x2590, 0x1c08, -0xa351, 0xd7ce, 0x2a74, 0xd59f, 0xf3fa, 0x477e, 0xf4e0, 0xf8ff, 0x3121, 0xdc01, -0xb775, 0x150c, 0xfcd8, 0xcc0d, 0x38c8, 0x2ece, 0xe423, 0x2849, 0x29b5, 0xc302, -0xda74, 0x1f47, 0xdd2f, 0xf247, 0x3ab2, 0xf2d7, 0x006a, 0x4787, 0xf2b8, 0xbc9f, -0x1f1c, 0x0ac7, 0xbde9, 0x23df, 0x2b4d, 0xd869, 0x1cf2, 0x2e59, 0xc8ac, 0xe6ca, -0x2cef, 0xd907, 0xf197, 0x4b23, 0xf20c, 0xef15, 0x4469, 0xf156, 0xb2a9, 0x1bfd, -0x127b, 0xca28, 0x306d, 0x3438, 0xdf08, 0x20cf, 0x27a1, 0xb9c7, 0xd8d7, 0x2741, -0xd309, 0xe588, 0x5056, 0x03d5, 0xef96, 0x4207, 0xfd8d, 0xae5f, 0x03b5, 0x0589, -0xbd38, 0x23af, 0x3a76, 0xe095, 0x2023, 0x3839, 0xc165, 0xd4ca, 0x2de1, 0xd781, -0xe09b, 0x50bd, 0x0340, 0xea0c, 0x4022, 0xf840, 0xad2c, 0x1331, 0x12e3, 0xc260, -0x2f2a, 0x3b6c, 0xd12c, 0x18a4, 0x3197, 0xb77c, 0xd13d, 0x2bd2, 0xd972, 0xe616, -0x52eb, 0x041e, 0xeb26, 0x3bdc, 0xf119, 0xa2b2, 0x06d5, 0x1049, 0xbd97, 0x289f, -0x44ee, 0xdf14, 0x1d7f, 0x34ac, 0xb7bb, 0xc690, 0x278a, 0xda25, 0xdff9, 0x579a, -0x080c, 0xe6ec, 0x4374, 0xfcbd, 0xa5be, 0x078c, 0x146b, 0xbb5b, 0x2164, 0x4262, -0xdc47, 0x1e50, 0x39f8, 0xbce7, 0xcbb4, 0x2afe, 0xdc45, 0xdb5e, 0x5409, 0x0b3a, -0xe53c, 0x4358, 0x02ae, 0xa896, 0x0857, 0x1af8, 0xc23a, 0x2352, 0x496b, 0xdfb5, -0x197e, 0x3ce3, 0xbe54, 0xc506, 0x3012, 0xe677, 0xd704, 0x5217, 0x11ee, 0xe2e7, -0x3fe4, 0x06fa, 0xa5a3, 0xfdcd, 0x181f, 0xbb44, 0x1747, 0x4a1f, 0xdca9, 0x10d6, -0x4291, 0xc4a6, 0xbd25, 0x2a87, 0xe605, 0xcbf6, 0x4b38, 0x15fb, 0xe0ad, 0x3f35, -0x0d36, 0xa652, 0x0068, 0x234a, 0xbde1, 0x11e2, 0x4aeb, 0xdbe0, 0x0a9d, 0x4344, -0xcb41, 0xbfb5, 0x3005, 0xef5d, 0xcc1b, 0x497b, 0x1516, 0xd825, 0x3b8a, 0x1677, -0xabc7, 0xfeb9, 0x2dcb, 0xc484, 0x08d9, 0x4f69, 0xe275, 0x041d, 0x4846, 0xd81a, -0xc055, 0x3307, 0xfcca, 0xce29, 0x4a68, 0x20cb, 0xd8f3, 0x379a, 0x1b24, 0xaf02, -0xfaf5, 0x36a2, 0xdc0c, 0xf2c4, 0x1be5, 0xefab, 0x3121, 0x54a4, 0xf198, 0xe7b1, -0x2ba4, 0xe144, 0xad6e, 0x0105, 0xf0a8, 0xe331, 0x4379, 0x5911, 0x2529, 0x2cc9, -0x279d, 0xcce3, 0xc4aa, 0xd6d0, 0xaebe, 0xee7a, 0x3b83, 0x0e08, 0x0c23, 0x6c6b, -0x4249, 0xd2d0, 0xee99, 0xf0dc, 0xb1d9, 0xc315, 0x0097, 0x05b2, 0x198e, 0x3a21, -0x0a87, 0x19af, 0x3001, 0xda73, 0xbf3b, 0xf320, 0xf1e8, 0xce48, 0xfde9, 0x1c97, -0x092a, 0x1c03, 0x1996, 0x174f, 0x1e06, 0x0784, 0xc7d4, 0xc266, 0xf6c0, 0xcc3e, -0xdd41, 0x35c9, 0x3070, 0x0e9b, 0x2df6, 0x46cc, 0xf777, 0xdb3b, 0xd86d, 0xb2ad, -0xdad5, 0xea97, 0xeb13, 0x2dfd, 0x5bf5, 0x1d45, 0x0c80, 0x4c6b, 0xf9d2, 0xb332, -0xd3b1, 0xcca7, 0xc90a, 0xeeb9, 0x1637, 0x2142, 0x4501, 0x2b65, 0x034c, 0x35ee, -0x0df0, 0xbcaf, 0xb962, 0xda48, 0xd509, 0xd169, 0x125a, 0x2f7e, 0x3ce9, 0x31e9, -0x1f9f, 0x25c3, 0xf9ce, 0xc7fa, 0xa4bc, 0xc4e4, 0xde14, 0xd114, 0x1820, 0x4ec1, -0x3955, 0x1716, 0x2ffb, 0x23e1, 0xd401, 0xc118, 0xaea8, 0xc267, 0xe761, 0xe650, -0x166b, 0x5317, 0x4508, 0x02ef, 0x256f, 0x2a58, 0xc9bf, 0xbfb1, 0xc690, 0xc4c0, -0xdb5a, 0xfc3e, 0x1d56, 0x406f, 0x4672, 0x07f9, 0x258b, 0x3563, 0xd0af, 0xad4b, -0xc62c, 0xd7ec, 0xd118, 0xfacb, 0x32c8, 0x3cf8, 0x3893, 0x1827, 0x2a88, 0x2056, -0xd2e0, 0xa9a8, 0xbc95, 0xe82d, 0xd209, 0xed42, 0x4049, 0x4bca, 0x27a4, 0x17b5, -0x33c7, 0x048c, 0xc896, 0xbbc4, 0xb5e8, 0xde55, 0xe670, 0xf26e, 0x319d, 0x5284, -0x20bb, 0x0a37, 0x3f54, 0x0425, 0xb7b6, 0xbeac, 0xc4a7, 0xd10a, 0xdf90, 0x067a, -0x32d8, 0x4def, 0x29e9, 0x0de0, 0x33b8, 0x00f9, 0xbb53, 0xb44c, 0xca99, 0xdcb3, -0xdf0d, 0x140a, 0x4211, 0x47c0, 0x2741, 0x1ffe, 0x3026, 0xf2ee, 0xc44d, 0xb8c2, -0xcbde, 0xe86b, 0xed77, 0x1b4d, 0x47ee, 0x4766, 0x1ae9, 0x2386, 0x33e9, 0xea57, -0xc4ae, 0xc182, 0xd3ec, 0xeae1, 0xf2fa, 0x1c99, 0x46ce, 0x4849, 0x16e5, 0x2357, -0x2d0d, 0xe171, 0xc2b4, 0xc619, 0xdb43, 0xe861, 0xf96a, 0x2580, 0x41a1, 0x3fcc, -0x14f6, 0x245e, 0x2692, 0xe3d9, 0xc4b2, 0xc084, 0xdcfb, 0xe481, 0xf277, 0x26e8, -0x4453, 0x3677, 0x1401, 0x2eb5, 0x1c61, 0xda3a, 0xc536, 0xbbc2, 0xd95f, 0xe409, -0xf088, 0x24e3, 0x4a50, 0x36ef, 0x1343, 0x3794, 0x1bd7, 0xd0b1, 0xbf04, 0xc0c0, -0xd999, 0xe102, 0xfe57, 0x307b, 0x4c60, 0x3446, 0x110b, 0x34af, 0x1719, 0xd246, -0xbe92, 0xc661, 0xdf01, 0xde95, 0x04b9, 0x34ae, 0x44b2, 0x2c3e, 0x140f, 0x311d, -0x09e5, 0xcd57, 0xb673, 0xc4ea, 0xe8e6, 0xdfae, 0x02f2, 0x3c51, 0x460d, 0x2037, -0x160e, 0x33a4, 0xffd5, 0xd044, 0xbb77, 0xbff6, 0xe547, 0xe2d3, 0x02a5, 0x3927, -0x483e, 0x1e84, 0x18a8, 0x35a4, 0xf428, 0xc4b4, 0xb810, 0xbe31, 0xdf3f, 0xe3b9, -0x070a, 0x3a6e, 0x4a01, 0x1d3d, 0x19b7, 0x312f, 0xe9ed, 0xb9d2, 0xb185, 0xc27d, -0xdf74, 0xeaac, 0x163c, 0x40b2, 0x480f, 0x196b, 0x1a68, 0x2946, 0xe387, 0xbd44, -0xb687, 0xcdd3, 0xe1c0, 0xe73f, 0x19c6, 0x454e, 0x4471, 0x1517, 0x1f37, 0x2415, -0xde12, 0xbc28, 0xb193, 0xce92, 0xe199, 0xeb2a, 0x208d, 0x45c9, 0x3dfd, 0x140f, -0x2554, 0x1b8d, 0xd3e4, 0xb631, 0xaeee, 0xd2a0, 0xe032, 0xeefc, 0x2bad, 0x4d46, -0x3a27, 0x0f3b, 0x24c9, 0x111e, 0xcdbf, 0xb6b5, 0xb194, 0xd7c9, 0xe60a, 0xf93b, -0x2f30, 0x4968, 0x3246, 0x1306, 0x2d46, 0x06e7, 0xc785, 0xba49, 0xb9ab, 0xde3a, -0xe629, 0xff26, 0x3a02, 0x5456, 0x30f0, 0x10ea, 0x3149, 0x0578, 0xc92c, 0xbe07, -0xc00c, 0xe26f, 0xec57, 0x0aaf, 0x3ec0, 0x560f, 0x2fbd, 0x16da, 0x3543, 0xfe12, -0xc441, 0xbe1e, 0xce80, 0xec2a, 0xeeda, 0x1761, 0x467e, 0x531c, 0x2945, 0x16f3, -0x30b3, 0xfa85, 0xc82a, 0xb95b, 0xd0b9, 0xf0bf, 0xecf0, 0x1a23, 0x4acc, 0x48e2, -0x1c65, 0x1e4d, 0x2902, 0xe8e0, 0xc9c2, 0xbc2f, 0xd2c0, 0xf22e, 0xed1c, 0x1844, -0x4b01, 0x4776, 0x146e, 0x2125, 0x2832, 0xde1c, 0xc52b, 0xbd52, 0xcf76, 0xec76, -0xf496, 0x1c9f, 0x4726, 0x432c, 0x116f, 0x25e7, 0x2766, 0xd731, 0xbc99, 0xc162, -0xd7c6, 0xe12d, 0xf3c2, 0x27ac, 0x489f, 0x3fb7, 0x11f9, 0x23e3, 0x1dd1, 0xd9f9, -0xbc81, 0xbc41, 0xe051, 0xe6f9, 0xf6af, 0x2df2, 0x4613, 0x2fe6, 0x12b9, 0x303c, -0x120f, 0xd130, 0xc1da, 0xbc62, 0xdb80, 0xe279, 0xf4be, 0x2c52, 0x4660, 0x275e, -0x0d1c, 0x3149, 0x075f, 0xc931, 0xbf24, 0xbaf3, 0xd888, 0xe2cc, 0xfb8b, 0x2cb6, -0x4a52, 0x29ea, 0x0caa, 0x3097, 0xff78, 0xbdf9, 0xb45b, 0xc1cb, 0xdd4e, 0xe310, -0x068a, 0x24f2, 0x54ac, 0x60d2, 0x1650, 0xf4c6, 0xe059, 0xb014, 0x83b1, 0xbc91, -0x02f9, 0x0157, 0x4fca, 0x7cb3, 0x41bb, 0x0df7, 0xea9f, 0xace6, 0x957f, 0xc6c1, -0xb25c, 0xf25c, 0x6d4a, 0x3c23, 0x2274, 0x5314, 0x2754, 0xc59a, 0xd13d, 0xdf25, -0xc110, 0x097d, 0xfee9, 0xe7e9, 0x4a58, 0x3073, 0xd0cf, 0x0834, 0x3f47, 0xde37, -0xefad, 0x3de0, 0xef24, 0xe80f, 0x1519, 0xd4ff, 0xbae7, 0x160b, 0x1ed5, 0xf6f3, -0x4a32, 0x391f, 0xed02, 0x1a95, 0x0194, 0xa798, 0xcdb4, 0x1870, 0xed7d, 0x0805, -0x5609, 0x1649, 0x0c10, 0x37fb, 0xe698, 0xb602, 0x0ab1, 0x02bc, 0xc982, 0x29a0, -0x3592, 0xeb4c, 0x26a0, 0x2b39, 0xc51e, 0xe028, 0x25df, 0xdd3d, 0xeb89, 0x3c7d, -0xf25b, 0xf2a8, 0x40cd, 0xf67a, 0xbd91, 0x18e1, 0x1367, 0xcbf7, 0x1e2b, 0x2469, -0xd608, 0x16f8, 0x2a0b, 0xc927, 0xe2cc, 0x37b1, 0xf346, 0xe694, 0x382d, 0xf93e, -0xe10a, 0x2a89, 0xf6d1, 0xba9a, 0x1681, 0x2591, 0xd24e, 0x187c, 0x2fad, 0xd8f6, -0x0b46, 0x2ae5, 0xc8c2, 0xd38a, 0x2a7a, 0xe7b1, 0xdcb3, 0x3e95, 0x03d8, 0xe5e1, -0x3718, 0x0451, 0xb6fc, 0x05d7, 0x1636, 0xc589, 0x0c0d, 0x2d31, 0xe2a5, 0x145e, -0x2ff3, 0xd16c, 0xd748, 0x2961, 0xeee4, 0xd89a, 0x2f77, 0x0413, 0xe432, 0x2a18, -0x07e4, 0xbf90, 0xffac, 0x1a5c, 0xce43, 0x0eae, 0x3e2a, 0xe525, 0xfcdf, 0x3412, -0xe19f, 0xc0d0, 0x160c, 0xf679, 0xdaf7, 0x3d0f, 0x184f, 0xe643, 0x3314, 0x0d97, -0xa9d1, 0xf210, 0x1e09, 0xc374, 0x0591, 0x4995, 0xef24, 0x05bf, 0x3aa6, 0xdaf6, -0xc1a1, 0x198b, 0xe5e1, 0xce1f, 0x40dc, 0x134c, 0xddc5, 0x3b68, 0x1d73, 0xb2c0, -0xf385, 0x2228, 0xc6a9, 0xff0d, 0x4056, 0xea16, 0x071a, 0x3ca3, 0xd74b, 0xbcb1, -0x210f, 0xf2c1, 0xcb2c, 0x414d, 0x1f07, 0xd958, 0x2f24, 0x195a, 0xa747, 0xe84d, -0x2605, 0xc501, 0xff0e, 0x4e0a, 0xea48, 0x02a4, 0x46c5, 0xd723, 0xafda, 0x23af, -0xf6a4, 0xbc7c, 0x3c45, 0x26f8, 0xdc74, 0x367d, 0x2543, 0xb0bd, 0xed3e, 0x27e5, -0xc333, 0xf8e2, 0x4d51, 0xeb5f, 0xfefc, 0x4865, 0xe34b, 0xb937, 0x2748, 0x0266, -0xc562, 0x3a6b, 0x25b1, 0xd774, 0x2daa, 0x2313, 0xb311, 0xef4a, 0x32e8, 0xcf60, -0xf8b2, 0x51fb, 0xee07, 0xf368, 0x44ab, 0xe548, 0xb2da, 0x24f2, 0x07b3, 0xc446, -0x3bc4, 0x2db3, 0xd779, 0x2ee4, 0x281e, 0xace1, 0xe4c5, 0x335a, 0xceab, 0xf17b, -0x55c7, 0xf4d4, 0xf24d, 0x4603, 0xeb3d, 0xaf65, 0x1bbf, 0x08f3, 0xbf31, 0x300f, -0x30d1, 0xd9cf, 0x29ba, 0x2ead, 0xb5b6, 0xdd53, 0x2dff, 0xcf0d, 0xe2d2, 0x4b14, -0xf7cf, 0xf104, 0x48fe, 0xf5f9, 0xb102, 0x175c, 0x0bd9, 0xbcb2, 0x27b4, 0x336a, -0xd7be, 0x2088, 0x3408, 0xbf37, 0xdc20, 0x3264, 0xdb9f, 0xe60e, 0x5013, 0xffb1, -0xe856, 0x4281, 0xff01, 0xb10f, 0x1094, 0x176a, 0xc863, 0x243b, 0x390b, 0xdf6d, -0x1d95, 0x3900, 0xc65f, 0xd25c, 0x2c3a, 0xe187, 0xdbeb, 0x465f, 0x0986, 0xeb79, -0x4044, 0x0711, 0xb0da, 0x0496, 0x1513, 0xc287, 0x187b, 0x3c78, 0xe032, 0x1521, -0x3c5f, 0xccc3, 0xcce1, 0x2b96, 0xe4ca, 0xd2ba, 0x430a, 0x0aa2, 0xdffc, 0x3ad9, -0x0be5, 0xaea0, 0x03b6, 0x1ba6, 0xbbed, 0x0db5, 0x4113, 0xdc05, 0x05bb, 0x3a65, -0xcc55, 0xc14b, 0x25ec, 0xe783, 0xd1d2, 0x4965, 0x1616, 0xdbb4, 0x3263, 0x0a8b, -0xa569, 0xf7cf, 0x1f57, 0xc4d2, 0x1559, 0x517f, 0xea47, 0x0848, 0x3a1f, 0xc9f1, -0xbb1e, 0x266f, 0xea60, 0xcd1c, 0x4d59, 0x23a6, 0xe328, 0x3b46, 0x19d2, 0xabe2, -0xf47d, 0x1e1a, 0xbcd3, 0x09ed, 0x4fb3, 0xe964, 0x0dbc, 0x485b, 0xd3bb, 0xbbbe, -0x2722, 0xffc5, 0xce39, 0x0d09, 0xfe2c, 0x112d, 0x596c, 0x16f5, 0xd170, 0x12a7, -0x07ab, 0xa521, 0xd945, 0x0290, 0xcf7b, 0x0c60, 0x5af6, 0x3c64, 0x1c78, 0x2c17, -0xf082, 0xb62e, 0xcf0c, 0xac7d, 0xba75, 0x27ac, 0x23af, 0xf129, 0x473d, 0x6f3b, -0xefd4, 0xd0fc, 0xfcb5, 0xc5fa, 0xa532, 0xe2aa, 0x08ff, 0x0704, 0x304a, 0x21e6, -0x0f85, 0x38c1, 0x010f, 0xbcdf, 0xd894, 0xf8ab, 0xd8c7, 0xe36a, 0x1b7a, 0x178e, -0x1a72, 0x1741, 0x157f, 0x1dd4, 0x0dfe, 0xe47e, 0xbdd3, 0xf139, 0xe9ae, 0xc861, -0x198e, 0x432e, 0x1c93, 0x1246, 0x4ca3, 0x23d4, 0xd5a3, 0xdda1, 0xc097, 0xcd1a, -0xeddd, 0xed1a, 0x1738, 0x5658, 0x41b1, 0xfba0, 0x3319, 0x2a1d, 0xc048, 0xc0cf, -0xd583, 0xd4c9, 0xdb7a, 0x070a, 0x2588, 0x3798, 0x3aa9, 0x0add, 0x25af, 0x2337, -0xd4c2, 0xb504, 0xca8a, 0xe4dd, 0xd039, 0xf1df, 0x2bb9, 0x3c1d, 0x3426, 0x22ac, -0x331f, 0x0f10, 0xdc7d, 0xbbe1, 0xb47c, 0xdb63, 0xd984, 0xfcbd, 0x450f, 0x5431, -0x271a, 0x210b, 0x3c62, 0xf43d, 0xc565, 0xc201, 0xc114, 0xe4f6, 0xee9b, 0x085b, -0x3cf5, 0x5653, 0x1e82, 0x0d52, 0x38a8, 0xf608, 0xc456, 0xce8b, 0xcfd1, 0xda08, -0xf0f6, 0x16f4, 0x3014, 0x4610, 0x1a46, 0x0c35, 0x36d1, 0xfb60, 0xbc9c, 0xbcea, -0xdb0c, 0xda74, 0xdb6c, 0x17ce, 0x3715, 0x33b3, 0x13c8, 0x1e7d, 0x290c, 0xe55b, -0xb756, 0xb135, 0xdad6, 0xde70, 0xd336, 0x1782, 0x43a0, 0x29e5, 0x03f8, 0x2c8f, -0x2768, 0xd396, 0xc0e1, 0xbc23, 0xccce, 0xdf32, 0xe829, 0x16bf, 0x4610, 0x3567, -0x012e, 0x312c, 0x3059, 0xcf4c, 0xbc48, 0xcf7d, 0xdb0c, 0xd4f7, 0xefe3, 0x2327, -0x3f5e, 0x34bf, 0x11d9, 0x3885, 0x2a09, 0xd700, 0xbd59, 0xc753, 0xde26, 0xd765, -0xf612, 0x31cc, 0x45d3, 0x2d56, 0x17c3, 0x3e94, 0x16b7, 0xce82, 0xc389, 0xc8ab, -0xdd9d, 0xdd82, 0xfe36, 0x3550, 0x4d5b, 0x2a45, 0x1384, 0x3ff2, 0x0ef3, 0xc81d, -0xbe61, 0xcce9, 0xe4aa, 0xe0ab, 0x0699, 0x385e, 0x486b, 0x2294, 0x15f0, 0x3d85, -0x01db, 0xc686, 0xc116, 0xcff5, 0xe34d, 0xe14b, 0x10c9, 0x3f07, 0x466b, 0x1f0d, -0x1d05, 0x3ab5, 0xf8ef, 0xc8ce, 0xc05e, 0xd357, 0xe7d4, 0xe29f, 0x13df, 0x471a, -0x4a47, 0x1c4c, 0x297f, 0x3e3f, 0xed74, 0xc6f8, 0xbfdc, 0xccde, 0xe3d5, 0xeaa0, -0x19ee, 0x4b1b, 0x4d49, 0x1865, 0x2976, 0x37a0, 0xe1a2, 0xb9e8, 0xba35, 0xd299, -0xde2b, 0xecfc, 0x27c6, 0x4ae2, 0x4564, 0x19a4, 0x28b5, 0x2426, 0xd885, 0xbc0a, -0xb90e, 0xd777, 0xe284, 0xf34a, 0x2d9a, 0x4813, 0x38b9, 0x14ee, 0x2c67, 0x1894, -0xd324, 0xbc7e, 0xb3bc, 0xda55, 0xe3e2, 0xef0a, 0x2fd7, 0x4da0, 0x2e6a, 0x0ee2, -0x326e, 0x0fc1, 0xcb23, 0xbee7, 0xb82c, 0xdaac, 0xe70d, 0xf676, 0x2c96, 0x4efe, -0x3180, 0x0f59, 0x3430, 0x0900, 0xc464, 0xbdf3, 0xbfd4, 0xdc18, 0xe54f, 0x02ac, -0x346a, 0x4ca2, 0x2bdb, 0x0ba0, 0x2ca5, 0x00e4, 0xc0c2, 0xb4ae, 0xbea2, 0xdd3b, -0xe1b5, 0x05e5, 0x36cd, 0x455d, 0x228c, 0x0f39, 0x2721, 0xf06a, 0xbe3c, 0xb625, -0xc315, 0xe0db, 0xe130, 0x0824, 0x3af9, 0x4450, 0x1a04, 0x1254, 0x2a20, 0xeab7, -0xbec0, 0xb857, 0xc3f1, 0xdf5a, 0xe831, 0x122e, 0x3daa, 0x44fa, 0x18f6, 0x1739, -0x28bb, 0xe41d, 0xbbf3, 0xb89c, 0xcff6, 0xe5c1, 0xea00, 0x1c60, 0x45d5, 0x43ef, -0x1c06, 0x2434, 0x292a, 0xe39e, 0xc2cb, 0xbd71, 0xd5c4, 0xe95c, 0xf5d3, 0x2a36, -0x4b75, 0x403d, 0x1c75, 0x32bf, 0x27ef, 0xdda2, 0xcb76, 0xc7c5, 0xdb1a, 0xe84b, -0xf612, 0x2b74, 0x5112, 0x4137, 0x18a7, 0x3889, 0x2791, 0xd8ed, 0xc90a, 0xc6c8, -0xd83c, 0xe5cf, 0x01d1, 0x32e1, 0x4e08, 0x3e77, 0x1bcc, 0x39cc, 0x1bcb, 0xcf7c, -0xbfad, 0xc693, 0xe1af, 0xe44a, 0x0379, 0x3b80, 0x5122, 0x34da, 0x12cd, 0x2ff7, -0x0b3f, 0xcb7c, 0xb95e, 0xc0f4, 0xe555, 0xe1ce, 0x01a1, 0x3cc9, 0x475e, 0x2196, -0x156a, 0x33d5, 0xf97d, 0xc4c1, 0xb9ed, 0xbd90, 0xe1db, 0xe24b, 0x021d, 0x3cfa, -0x4dca, 0x1ea3, 0x12f7, 0x33e9, 0xf159, 0xbf15, 0xb984, 0xbdf4, 0xd9dc, 0xe483, -0x0d28, 0x3bb5, 0x4aee, 0x1fcf, 0x1792, 0x2e75, 0xe773, 0xb55d, 0xaeed, 0xc525, -0xdc07, 0xe07a, 0x162d, 0x403a, 0x41df, 0x18e6, 0x1816, 0x22ca, 0xe0d7, 0xba3f, -0xab7d, 0xc6d9, 0xe296, 0xe464, 0x1a6b, 0x45ec, 0x3cb4, 0x1203, 0x21d7, 0x1e19, -0xd553, 0xc042, 0xb44a, 0xca17, 0xe29f, 0xe893, 0x1a35, 0x457e, 0x3ca4, 0x0eee, -0x274f, 0x1dc0, 0xd010, 0xbe2a, 0xb43a, 0xca4c, 0xe2e4, 0xf448, 0x246d, 0x4965, -0x3fdb, 0x10f7, 0x25b8, 0x162e, 0xcca7, 0xb882, 0xb947, 0xd7a5, 0xe438, 0xfea1, -0x33b7, 0x4cfe, 0x3b14, 0x1175, 0x24b7, 0x0bf7, 0xcecd, 0xbc06, 0xbdfc, 0xe81e, -0xee24, 0x01b0, 0x3af8, 0x476e, 0x28b0, 0x20d9, 0x3412, 0xf8f1, 0xc8c9, 0xbcf1, -0xbe75, 0xed66, 0xf061, 0x0477, 0x2e04, 0x4a13, 0x58d9, 0x3473, 0x0a86, 0xde23, -0xc927, 0xa391, 0xa85a, 0xfd01, 0x01c0, 0x26a1, 0x8016, 0x6b60, 0x1f2e, 0x05f0, -0xdab1, 0x948b, 0xb8c2, 0xc47d, 0xcb07, 0x3e6c, 0x61b3, 0x38d1, 0x421e, 0x423b, -0xe147, 0xbac6, 0xe0e9, 0xbe22, 0xd9dd, 0x0b3a, 0x03de, 0x31f4, 0x40dc, 0xf8e0, -0xf398, 0x2cdd, 0xec3a, 0xc582, 0x25ea, 0x1c58, 0xd8cd, 0x0700, 0x13d5, 0xbffb, -0xdfbf, 0x2eeb, 0xf5a6, 0x0a90, 0x5506, 0x0dfc, 0xf5d4, 0x194f, 0xd0b1, 0xa469, -0xf944, 0x0659, 0xe225, 0x3e8d, 0x4455, 0xf804, 0x285d, 0x16e8, 0xac54, 0xcbe6, -0x0dc1, 0xcfa2, 0xef3a, 0x51ba, 0x0cea, 0x02d9, 0x4169, 0xec8e, 0xb42c, 0x04b3, -0xfdcb, 0xc5c1, 0x233b, 0x2fa0, 0xe530, 0x27ec, 0x2937, 0xc096, 0xdeaa, 0x2435, -0xe010, 0xe6f7, 0x3e0a, 0xfd91, 0xeb2e, 0x333b, 0xf30c, 0xb2be, 0x1250, 0x1fd9, -0xc7fa, 0x1877, 0x3b5d, 0xdbdb, 0x0644, 0x23bb, 0xbdc0, 0xcc54, 0x2f0c, 0xf1f3, -0xe57b, 0x48e5, 0x082c, 0xe598, 0x360e, 0xf80f, 0xa876, 0x0659, 0x1b42, 0xc57b, -0x1fb9, 0x48cb, 0xe657, 0x1591, 0x3682, 0xcb26, 0xccd4, 0x2692, 0xe8fa, 0xda38, -0x445f, 0x1220, 0xf0e0, 0x3d8f, 0x09d0, 0xb6bc, 0x00e1, 0x260a, 0xd188, 0x0576, -0x3935, 0xef31, 0x1179, 0x33e9, 0xd401, 0xcde0, 0x2d56, 0xfa21, 0xdbb0, 0x4a3c, -0x1c17, 0xdc92, 0x334e, 0x1ceb, 0xb17d, 0xebbf, 0x27e0, 0xd919, 0x0f7e, 0x4a4b, -0xf20c, 0x107b, 0x3a20, 0xcab0, 0xba0a, 0x24cb, 0xf1fa, 0xcaf2, 0x3f6a, 0x2005, -0xe495, 0x30ba, 0x16ef, 0xb7a7, 0xec01, 0x1401, 0xc9b7, 0x05b6, 0x393b, 0xe12d, -0x07bf, 0x44cb, 0xdee8, 0xbd85, 0x23f1, 0xfc20, 0xc5f2, 0x2bd6, 0x194b, 0xdfc0, -0x26e3, 0x160b, 0xba3e, 0xf25d, 0x2457, 0xcdc4, 0x004b, 0x4634, 0xe4dc, 0xf4a7, -0x3d04, 0xdcc3, 0xb397, 0x1fb0, 0x0021, 0xcbc7, 0x37a0, 0x223d, 0xdd2e, 0x2e4a, -0x1e22, 0xae9d, 0xea89, 0x28db, 0xc94f, 0xf4c9, 0x4a0c, 0xf565, 0xfefa, 0x4525, -0xee89, 0xbfb1, 0x1b8b, 0xfa5c, 0xc436, 0x2f51, 0x24ca, 0xdfcd, 0x2cf4, 0x2a31, -0xbd55, 0xe94e, 0x2b96, 0xd147, 0xee2f, 0x4235, 0xf2bf, 0xf601, 0x3b75, 0xe821, -0xb876, 0x1f6c, 0x056a, 0xc597, 0x378b, 0x2e60, 0xd377, 0x1e9d, 0x283c, 0xb892, -0xdee1, 0x2781, 0xd1f0, 0xf4ba, 0x554c, 0x0123, 0xf9be, 0x420b, 0xea52, 0xaa76, -0x121d, 0x04f0, 0xbdca, 0x333f, 0x4315, 0xe753, 0x27d8, 0x325a, 0xb873, 0xd272, -0x2388, 0xcf65, 0xe913, 0x57fe, 0x07f4, 0xf542, 0x4773, 0xf6c7, 0xab18, 0x11a2, -0x0c42, 0xb926, 0x2816, 0x3c96, 0xde7c, 0x28fb, 0x3ab1, 0xb843, 0xd1f6, 0x2e08, -0xd30e, 0xdd67, 0x552d, 0x04b1, 0xe6f1, 0x4408, 0xfac7, 0xa436, 0x0956, 0x1138, -0xb9e0, 0x267a, 0x42e3, 0xd676, 0x199d, 0x3443, 0xb086, 0xc200, 0x28e5, 0xda7a, -0xd827, 0x53d9, 0x0bab, 0xe3c1, 0x40a6, 0xfc98, 0xa29e, 0x018f, 0x1105, 0xb6e7, -0x1b43, 0x498c, 0xdd00, 0x1541, 0x4138, 0xc0de, 0xbf2e, 0x283d, 0xe09c, 0xce50, -0x4aa8, 0x1053, 0xdfdb, 0x3f0a, 0x0676, 0xa0c4, 0xfe21, 0x1b86, 0xb753, 0x10a7, -0x4958, 0xd934, 0x0821, 0x3f0a, 0xc34f, 0xba57, 0x2b9e, 0xe817, 0xc7b8, 0x46a9, -0x0faa, 0xd39d, 0x38e6, 0x0b67, 0x9f2e, 0xfa87, 0x2298, 0xbafd, 0x0a31, 0x466b, -0xd41d, 0xfd51, 0x3a2b, 0xc101, 0xb40d, 0x2b20, 0xef1f, 0xc9c0, 0x46b9, 0x10a8, -0xce9c, 0x3371, 0x097d, 0x9c55, 0xf5c4, 0x3090, 0xd2df, 0xecd4, 0x147e, 0xe806, -0x2e2d, 0x4ebd, 0xe525, 0xe179, 0x26a7, 0xd61d, 0xaa44, 0x0053, 0xe8dc, 0xe136, -0x46b6, 0x57fc, 0x24b5, 0x2da9, 0x235d, 0xcd31, 0xca81, 0xd1c7, 0xaf26, 0xfdb0, -0x3bad, 0x09ac, 0x1da0, 0x74cf, 0x3a9c, 0xd9e6, 0xf662, 0xf1b3, 0xb74e, 0xc78b, -0x079d, 0x1303, 0x25f7, 0x3f88, 0x14f4, 0x2459, 0x2c9b, 0xdd69, 0xc8da, 0xfb0c, -0xfc56, 0xd94a, 0x0b6d, 0x29c0, 0x150a, 0x22e5, 0x1d92, 0x18ef, 0x1cfc, 0x0c23, -0xcdc3, 0xcd7b, 0x0116, 0xd225, 0xea05, 0x3ecf, 0x321c, 0x114b, 0x3429, 0x4818, -0xf80f, 0xe0f6, 0xd86d, 0xb88f, 0xe34c, 0xec85, 0xf335, 0x37f0, 0x5dfb, 0x1c42, -0x11db, 0x4f23, 0xfc26, 0xba44, 0xd5aa, 0xd324, 0xd1fd, 0xf17c, 0x1856, 0x276a, -0x4692, 0x286a, 0x0ba3, 0x3d7b, 0x1150, 0xc1ac, 0xbdf0, 0xe136, 0xd542, 0xcf26, -0x1384, 0x2faf, 0x3870, 0x2e2c, 0x2318, 0x28d4, 0xfdee, 0xcb16, 0xa730, 0xc88d, -0xdb00, 0xceb6, 0x1995, 0x4d9d, 0x384a, 0x1a5c, 0x3253, 0x2473, 0xd8f1, 0xc53c, -0xafa7, 0xc440, 0xe9ed, 0xe807, 0x176e, 0x51f8, 0x4372, 0x074b, 0x2912, 0x2b6d, -0xcf1e, 0xc37a, 0xc518, 0xc3a3, 0xdc9c, 0xfa3e, 0x1a98, 0x40fa, 0x4783, 0x0983, -0x2539, 0x3545, 0xd15a, 0xad3e, 0xc7ca, 0xd8f8, 0xd06d, 0xf96a, 0x3340, 0x3d0f, -0x375f, 0x19a1, 0x2cbe, 0x2480, 0xd619, 0xaa0f, 0xbfde, 0xe9c8, 0xd157, 0xf05e, -0x42af, 0x4a8c, 0x2ada, 0x1e55, 0x372a, 0x09e1, 0xcd35, 0xbbbe, 0xb8fb, 0xe193, -0xe480, 0xf329, 0x3476, 0x51aa, 0x233d, 0x1102, 0x44ce, 0x08c4, 0xbbca, 0xc158, -0xc692, 0xd1e1, 0xdde2, 0x0656, 0x33cd, 0x4f74, 0x2ddc, 0x0f4d, 0x394e, 0x09e6, -0xbe5c, 0xb5ad, 0xcc4d, 0xdc27, 0xdab2, 0x1043, 0x3ea4, 0x479b, 0x2b41, 0x1d61, -0x308b, 0xf8bb, 0xc464, 0xb735, 0xcd64, 0xe75e, 0xe437, 0x161f, 0x4870, 0x48ad, -0x1c9b, 0x1ea2, 0x33d1, 0xf0d7, 0xc828, 0xc0cf, 0xd2aa, 0xe99c, 0xeb83, 0x1a23, -0x47fe, 0x4a7b, 0x1905, 0x1e38, 0x324a, 0xe958, 0xc1e9, 0xc325, 0xd7e4, 0xe635, -0xf2dc, 0x2267, 0x3faa, 0x415f, 0x1a59, 0x202f, 0x2852, 0xe889, 0xc4bd, 0xbe6c, -0xdab6, 0xe56a, 0xef10, 0x26eb, 0x4556, 0x3a6e, 0x180a, 0x2a09, 0x1fb9, 0xde8b, -0xc5cf, 0xb9e8, 0xd7a7, 0xe7b9, 0xedd9, 0x2300, 0x49df, 0x37d1, 0x12a8, 0x2f9a, -0x18aa, 0xce1b, 0xbdb0, 0xbc6b, 0xd29e, 0xdeee, 0xf71a, 0x2781, 0x45f4, 0x339d, -0x0ccf, 0x2b8f, 0x12d6, 0xc8e8, 0xb6b6, 0xbda1, 0xd3af, 0xd912, 0xfe42, 0x2d6d, -0x3ef8, 0x304b, 0x1689, 0x29cf, 0x067c, 0xcba1, 0xb57d, 0xbede, 0xdf90, 0xdc36, -0xfffb, 0x3465, 0x3c40, 0x2128, 0x1a44, 0x31e1, 0xfd23, 0xcc9c, 0xbac3, 0xbc48, -0xdd17, 0xde23, 0xfd31, 0x3137, 0x4362, 0x1eee, 0x18fc, 0x32b8, 0xf20e, 0xc5cc, -0xbb44, 0xbdcc, 0xd9bd, 0xe2e3, 0x076b, 0x353c, 0x4892, 0x1e7b, 0x18f6, 0x30cf, -0xea76, 0xbae7, 0xb387, 0xc4fa, 0xdcc0, 0xe7b8, 0x15fe, 0x3d5b, 0x45ed, 0x199f, -0x192d, 0x2670, 0xe39c, 0xbd23, 0xb49a, 0xce79, 0xe000, 0xe7b3, 0x1c0b, 0x42fb, -0x4231, 0x1500, 0x214f, 0x24b7, 0xddd0, 0xbdaf, 0xb443, 0xd1d6, 0xe488, 0xf035, -0x248c, 0x481a, 0x412e, 0x17e3, 0x2a09, 0x1e55, 0xd730, 0xbb6d, 0xb458, 0xd6fe, -0xe4f4, 0xf65d, 0x30dd, 0x50e1, 0x3ee0, 0x15ff, 0x2a77, 0x133f, 0xd208, 0xbb41, -0xb5d0, 0xdc9b, 0xea0f, 0xfe2e, 0x33e8, 0x4ec6, 0x3729, 0x17b0, 0x327e, 0x0ae7, -0xcbb1, 0xbd40, 0xbbb0, 0xe021, 0xe92f, 0x0274, 0x3a54, 0x53fa, 0x3201, 0x1417, -0x340a, 0x073d, 0xcaf8, 0xbd21, 0xbddc, 0xdfe9, 0xebda, 0x0cee, 0x3df0, 0x5410, -0x3025, 0x16c7, 0x32e0, 0xfb90, 0xc1f6, 0xbac8, 0xcb01, 0xe852, 0xeb00, 0x14bb, -0x4362, 0x4e09, 0x267e, 0x175e, 0x2ea6, 0xf673, 0xc679, 0xb7ca, 0xce42, 0xef38, -0xea88, 0x19da, 0x4c19, 0x4976, 0x1f9b, 0x2155, 0x2984, 0xe747, 0xc939, 0xbc60, -0xced0, 0xf05b, 0xef4a, 0x1a1b, 0x4d06, 0x49db, 0x1707, 0x21a4, 0x261f, 0xdb58, -0xc42f, 0xbd07, 0xcd03, 0xeb57, 0xf6f1, 0x1fe4, 0x4885, 0x45cb, 0x1391, 0x2121, -0x232c, 0xd7ec, 0xbd29, 0xbf59, 0xd799, 0xe575, 0xf6b7, 0x28c6, 0x4774, 0x3e39, -0x1137, 0x1ef7, 0x1744, 0xd610, 0xbc35, 0xb9e4, 0xdd70, 0xe860, 0xf41e, 0x2929, -0x4423, 0x2e1c, 0x110a, 0x2d77, 0x0e41, 0xced2, 0xc1cd, 0xbb1d, 0xd9b8, 0xe41e, -0xf4ed, 0x2b8f, 0x4959, 0x2b92, 0x0f36, 0x32ac, 0x0962, 0xc983, 0xc063, 0xbadd, -0xd65d, 0xe557, 0xfe42, 0x2c4a, 0x49b0, 0x2cc4, 0x11af, 0x335e, 0x0099, 0xbfb6, -0xb950, 0xc282, 0xda13, 0xe2c3, 0x0c6b, 0x2301, 0x3d0d, 0x5b32, 0x2bf1, 0xf816, -0xd811, 0xc694, 0x94e5, 0xa76b, 0x02b3, 0xf90f, 0x23f0, 0x863b, 0x6a8b, 0x1164, -0xf826, 0xd412, 0x8d7c, 0xade7, 0xbb44, 0xce06, 0x446e, 0x60cd, 0x37d3, 0x464b, -0x4603, 0xdb1a, 0xadc9, 0xd748, 0xbb79, 0xd2d1, 0x0130, 0x0928, 0x3b0a, 0x4390, -0x0002, 0xef18, 0x25c7, 0xf662, 0xbcac, 0x1201, 0x1f14, 0xda32, 0xff28, 0x1fbe, -0xd1ed, 0xd6db, 0x389e, 0x0dbf, 0xf3fb, 0x488e, 0x1c7e, 0xe70e, 0x1374, 0xed9e, -0xa6a3, 0xedff, 0x24fa, 0xe3d7, 0x2454, 0x55ff, 0xfbf6, 0x19ad, 0x27e2, 0xbe48, -0xc206, 0x1296, 0xe45b, 0xdd35, 0x4db6, 0x1c33, 0xef46, 0x4348, 0x0b24, 0xb5eb, -0xf732, 0x126a, 0xcd2b, 0x063c, 0x37c5, 0xec01, 0x18d9, 0x3cc8, 0xd616, 0xd2bc, -0x273a, 0xf84b, 0xd381, 0x30e7, 0x1385, 0xdff9, 0x30f4, 0x18b0, 0xc10e, 0x0696, -0x364d, 0xd762, 0xfbf8, 0x3d8c, 0xe397, 0xf63d, 0x3932, 0xe0e0, 0xc7fb, 0x31c6, -0x0e47, 0xd472, 0x33f1, 0x19cb, 0xd9b6, 0x297e, 0x174d, 0xb770, 0xf385, 0x2b80, -0xd844, 0x020b, 0x44ae, 0xecdc, 0xffc6, 0x3c84, 0xe59b, 0xc239, 0x1b9c, 0x03d8, -0xcdfc, 0x284d, 0x2050, 0xe856, 0x29ba, 0x1da6, 0xc79c, 0xf13e, 0x2a8b, 0xdb89, -0xe7cf, 0x315a, 0xf321, 0xf5f9, 0x332c, 0xf230, 0xc508, 0x1566, 0x093d, 0xce9a, -0x23e7, 0x1dac, 0xd1bc, 0x1aa1, 0x2c3b, 0xbf9a, 0xd5cb, 0x286f, 0xe374, 0xeccb, -0x3bb8, 0xfa4b, 0xf5e8, 0x2f2d, 0xe9fe, 0xbb23, 0x0ce2, 0xff3a, 0xc469, 0x2484, -0x2c01, 0xe186, 0x1b3d, 0x26db, 0xc966, 0xd43c, 0x0cf6, 0xd3cc, 0xeaf1, 0x349d, -0xf49e, 0xfa1b, 0x3da3, 0xf2ae, 0xb5a2, 0x09f1, 0x0303, 0xbee6, 0x1c15, 0x2f77, -0xe137, 0x187e, 0x2a96, 0xc3d0, 0xd414, 0x1e88, 0xd685, 0xe774, 0x4a59, 0xfa19, -0xe6e2, 0x3f71, 0xf365, 0xa657, 0x0aac, 0x0b27, 0xbbf9, 0x22a4, 0x3896, 0xdde2, -0x1ea6, 0x2ddb, 0xb6a3, 0xcf13, 0x234d, 0xd209, 0xdfcd, 0x4de4, 0x0530, 0xf235, -0x458e, 0xfe1c, 0xaf9c, 0x04ff, 0x0ba8, 0xc04a, 0x1dfe, 0x3a8f, 0xe44e, 0x1ebd, -0x3786, 0xc838, 0xd340, 0x2d49, 0xe462, 0xdc75, 0x4606, 0x05e5, 0xe8d6, 0x3e07, -0x0367, 0xb349, 0x0d09, 0x1be3, 0xc56a, 0x2148, 0x4004, 0xd8d7, 0x1595, 0x3a7d, -0xc56d, 0xd126, 0x3261, 0xe7bb, 0xe124, 0x5308, 0x0eb8, 0xeb26, 0x437a, 0x042c, -0xaf26, 0x0eb2, 0x218d, 0xc6ad, 0x22c2, 0x4ab9, 0xe2d2, 0x1874, 0x40cd, 0xca8d, -0xc838, 0x2e68, 0xedb9, 0xd7fa, 0x4c7a, 0x1526, 0xe7cb, 0x4150, 0x0c9e, 0xae99, -0x0430, 0x20bb, 0xc1b5, 0x1187, 0x47d4, 0xe4da, 0x1440, 0x42ec, 0xcdff, 0xc6df, -0x2c13, 0xec08, 0xd2d5, 0x46ed, 0x0ef1, 0xdd36, 0x3d9e, 0x0f6f, 0xadf9, 0x0331, -0x2434, 0xc516, 0x0f43, 0x4485, 0xde32, 0x0700, 0x3d61, 0xce91, 0xbeff, 0x2926, -0xf4fa, 0xd134, 0x41a9, 0x1664, 0xdc49, 0x34e2, 0x1333, 0xace3, 0xf361, 0x1f70, -0xc693, 0x0699, 0x47e9, 0xe934, 0x0537, 0x4024, 0xd7ae, 0xba09, 0x1da8, 0xf1cd, -0xcbb4, 0x3981, 0x188e, 0xddeb, 0x326b, 0x1892, 0xb095, 0xf130, 0x20bc, 0xc29d, -0xf9a7, 0x3f9b, 0xe3d4, 0xfb7a, 0x3e64, 0xdf3d, 0xbe59, 0x2395, 0xf6d6, 0xc4b9, -0x3386, 0x1936, 0xd767, 0x2b7a, 0x1dd1, 0xb547, 0xeefe, 0x2814, 0xcaa5, 0xf817, -0x487c, 0xeb89, 0xf26b, 0x397c, 0xdf7f, 0xb39b, 0x1c04, 0xfe68, 0xc54d, 0x349f, -0x2a68, 0xdbdd, 0x22d6, 0x1ddd, 0xb0a0, 0xe0fa, 0x21cc, 0xc993, 0xf898, 0x52ec, -0xf689, 0xf70a, 0x426a, 0xe741, 0xaf61, 0x151d, 0xf921, 0xc0cc, 0x3886, 0x3002, -0xdf54, 0x2dc5, 0x25fe, 0xb022, 0xe019, 0x2b01, 0xdc46, 0xdf34, 0x113b, 0xf3ef, -0x2b9e, 0x5380, 0xf6a9, 0xdedb, 0x2143, 0xe2d9, 0xa1ed, 0xf09b, 0xf0dc, 0xdb67, -0x3407, 0x5e55, 0x32ca, 0x27e1, 0x25c5, 0xd709, 0xc36a, 0xcfc9, 0xa7f8, 0xea51, -0x3bd7, 0x0e16, 0x0801, 0x6978, 0x4f5b, 0xdcee, 0xefc1, 0xfe47, 0xbe36, 0xbc03, -0xf6fb, 0x0a54, 0x1b49, 0x3bd0, 0x15ec, 0x2382, 0x3cb2, 0xeb19, 0xc5aa, 0xedb7, -0xf957, 0xd7a6, 0xf768, 0x22e7, 0x17a3, 0x2061, 0x1ba7, 0x1fd9, 0x23d8, 0x0c01, -0xd667, 0xc4b5, 0xfb1e, 0xd994, 0xd7e9, 0x3398, 0x3e92, 0x166b, 0x27af, 0x4ec2, -0x0515, 0xd7d3, 0xde1a, 0xb994, 0xdcbe, 0xf079, 0xeced, 0x2d31, 0x5f61, 0x253a, -0x040a, 0x4aa6, 0x0dcf, 0xb7f1, 0xcf3e, 0xd1b1, 0xcf61, 0xe63f, 0x105c, 0x26cc, -0x431a, 0x31ef, 0x089d, 0x3279, 0x1505, 0xc9c8, 0xbc77, 0xd253, 0xdbe3, 0xd185, -0x028c, 0x2ef7, 0x3d5b, 0x2ff3, 0x2436, 0x2ecc, 0xffae, 0xd27d, 0xae72, 0xb7bb, -0xda8b, 0xd2ea, 0x0b5f, 0x4d03, 0x43e6, 0x19f3, 0x2a63, 0x2e3d, 0xdc83, 0xc2d3, -0xb8a6, 0xbf1e, 0xe681, 0xe9fe, 0x0c02, 0x47ed, 0x4b9b, 0x0b96, 0x1c09, 0x3193, -0xdad3, 0xc2e7, 0xcba3, 0xc7a2, 0xd979, 0xf765, 0x1768, 0x34eb, 0x4272, 0x0e6d, -0x1cba, 0x35bf, 0xe2f7, 0xb4ec, 0xc2dc, 0xdbc9, 0xd4b0, 0xeaf5, 0x2a04, 0x3d45, -0x3135, 0x163c, 0x29bf, 0x1de9, 0xd862, 0xb512, 0xbc25, 0xe60f, 0xdb2d, 0xe4e1, -0x2e4c, 0x46ed, 0x2280, 0x0c95, 0x3390, 0x0dba, 0xc912, 0xc227, 0xbe99, 0xd9c1, -0xe290, 0xed33, 0x2239, 0x44f4, 0x1ed4, 0x0166, 0x3720, 0x122c, 0xc1e5, 0xbe0b, -0xcc00, 0xd5f1, 0xd17f, 0xf3b3, 0x23a2, 0x3db4, 0x2624, 0x0cc4, 0x33e1, 0x1023, -0xc9ae, 0xb631, 0xc8cd, 0xdd8a, 0xceea, 0xfaf7, 0x3621, 0x3ded, 0x1ca5, 0x1b0d, -0x3ba6, 0x0216, 0xce92, 0xc394, 0xc90e, 0xdd1a, 0xd9c7, 0x0438, 0x3a3b, 0x43f5, -0x193b, 0x1b93, 0x427f, 0xfe8c, 0xc66d, 0xc591, 0xd58c, 0xe393, 0xe1bc, 0x0fe2, -0x3df6, 0x448e, 0x1c8a, 0x21c0, 0x3e71, 0xf9bd, 0xc96f, 0xc456, 0xd5d9, 0xe4b5, -0xe922, 0x1ce5, 0x418b, 0x417b, 0x1d09, 0x2518, 0x341d, 0xf03a, 0xcbea, 0xc1e1, -0xd6e8, 0xe7f3, 0xe55f, 0x1d2e, 0x4954, 0x3f9e, 0x1b23, 0x31e7, 0x3163, 0xe580, -0xcb7b, 0xbe90, 0xd27b, 0xe98e, 0xede5, 0x22f3, 0x51cc, 0x440f, 0x174a, 0x3744, -0x308b, 0xdab4, 0xc2e8, 0xc03f, 0xd50d, 0xe3f9, 0xf766, 0x2e0e, 0x4fbc, 0x4269, -0x17f2, 0x2ec2, 0x1f55, 0xd524, 0xbca2, 0xbb93, 0xda21, 0xe34e, 0xfad1, 0x31b6, -0x47da, 0x36e7, 0x19ff, 0x2ef6, 0x0ef3, 0xd0a0, 0xb94c, 0xb54a, 0xdd97, 0xe171, -0xf86b, 0x35bc, 0x47ba, 0x2983, 0x1694, 0x3324, 0x023b, 0xc961, 0xbc03, 0xb2d4, -0xd7d9, 0xe2e7, 0xf9f7, 0x3391, 0x4b1a, 0x23f3, 0x1255, 0x2f9f, 0xf2dd, 0xbf1b, -0xb8c8, 0xb595, 0xd67b, 0xe49e, 0x04eb, 0x3553, 0x491e, 0x20bc, 0x101c, 0x2d09, -0xefb9, 0xbc9b, 0xb456, 0xbc74, 0xdcb8, 0xe7e3, 0x1085, 0x3e2d, 0x4862, 0x20a4, -0x178e, 0x24e7, 0xe5be, 0xbfc5, 0xb5ac, 0xc6ce, 0xe4c1, 0xe9df, 0x156b, 0x41e6, -0x4347, 0x169b, 0x1dcf, 0x2537, 0xdbdb, 0xbd60, 0xb4ac, 0xc74f, 0xe58f, 0xf1d1, -0x1bdb, 0x438d, 0x41e3, 0x1215, 0x1c5f, 0x197b, 0xd34e, 0xbb05, 0xb7f2, 0xd3b2, -0xe362, 0xf0f9, 0x2581, 0x46a8, 0x3c22, 0x115f, 0x2009, 0x122f, 0xd304, 0xbd34, -0xb54b, 0xd890, 0xe911, 0xf7d8, 0x2be6, 0x4644, 0x3124, 0x11c4, 0x2d7b, 0x0ee0, -0xcfc2, 0xc445, 0xbf59, 0xdf01, 0xe6a3, 0xf6fe, 0x2fa7, 0x4f24, 0x32c6, 0x10fe, -0x360f, 0x14d0, 0xd1fe, 0xc5f6, 0xc3cd, 0xdf16, 0xe84d, 0x00f9, 0x3302, 0x4f34, -0x330f, 0x190f, 0x3c38, 0x0f70, 0xcd2f, 0xc1c9, 0xccf8, 0xe5e1, 0xe528, 0x0d0a, -0x3fdc, 0x4c89, 0x2965, 0x1572, 0x3558, 0x0773, 0xcf30, 0xbe56, 0xcfa1, 0xeeb4, -0xe390, 0x0d40, 0x446b, 0x47d6, 0x22c9, 0x1f4d, 0x33f7, 0xf611, 0xcd9c, 0xbd57, -0xc66d, 0xec7d, 0xe7bf, 0x0df9, 0x474d, 0x4b43, 0x1b2f, 0x1f2d, 0x35f1, 0xea3c, -0xc455, 0xbc6b, 0xc16f, 0xe41e, 0xef9d, 0x16e5, 0x457b, 0x4d9b, 0x1df1, 0x2052, -0x310e, 0xe35e, 0xbd3b, 0xbe4a, 0xd17b, 0xe63a, 0xf0dd, 0x2445, 0x4bde, 0x4c1f, -0x20d2, 0x25a1, 0x2da1, 0xe9a8, 0xc288, 0xb909, 0xd9c6, 0xee3e, 0xf6a6, 0x2ef9, -0x50a3, 0x43ee, 0x1dd3, 0x2eed, 0x2309, 0xe027, 0xcabc, 0xbd16, 0xdbde, 0xee25, -0xf339, 0x2c27, 0x5390, 0x40a0, 0x1b96, 0x375c, 0x1d2b, 0xd734, 0xc78b, 0xb807, -0xd541, 0xecee, 0xfe54, 0x2fa0, 0x5361, 0x3f28, 0x1522, 0x3393, 0x1266, 0xc88d, -0xbc85, 0xb8d2, 0xd4a8, 0xe5ab, 0x0438, 0x3396, 0x4c0c, 0x33cd, 0x0a79, 0x2169, -0xfe45, 0xc2f1, 0xb2f9, 0xb954, 0xe0e9, 0xe165, 0x029e, 0x2aa5, 0x318e, 0x465f, -0x33d0, 0x0444, 0xd0f3, 0xc734, 0xa46c, 0x9309, 0xec1b, 0xfbdf, 0x010e, 0x6440, -0x7e13, 0x1ed8, 0xeedd, 0xe7dd, 0x9c36, 0x94bd, 0xaeea, 0xb4e6, 0x08d9, 0x4ccc, -0x44d3, 0x3995, 0x43b3, 0xfbc8, 0xa9f8, 0xbc47, 0xb7e9, 0xb05c, 0xd4b9, 0x0205, -0x2fad, 0x38f1, 0x145d, 0xf759, 0x0aec, 0xf6cf, 0xb93a, 0xdb0a, 0x14b9, 0xe52f, -0xdb64, 0x2275, 0xfc04, 0xc169, 0x1482, 0x2708, 0xda58, 0x13ad, 0x34f3, 0xe68c, -0xf5c6, 0x0b27, 0xbfca, 0xc11f, 0x1a39, 0xf895, 0xf15a, 0x535e, 0x1fbe, 0xfd1c, -0x2fab, 0xeb46, 0xb207, 0xf114, 0xfe7d, 0xd0f4, 0x2a5f, 0x4be3, 0xf98f, 0x2db3, -0x365b, 0xcbe5, 0xd12f, 0x160e, 0xe93e, 0xe29c, 0x4478, 0x1d6f, 0x0413, 0x49f1, -0x0cdb, 0xc582, 0x08ff, 0x1ce3, 0xd295, 0x10d4, 0x44fb, 0xeff9, 0x15bb, 0x4310, -0xddfd, 0xd8c9, 0x34f7, 0xfaaf, 0xd716, 0x4457, 0x1cff, 0xe28a, 0x3514, 0x1237, -0xb5a9, 0x01cc, 0x3075, 0xd90a, 0x1130, 0x4b8f, 0xf1cd, 0x0fee, 0x37f4, 0xcf77, -0xc32c, 0x262d, 0xfb1f, 0xde11, 0x4cfa, 0x220f, 0xe7b6, 0x379f, 0x1630, 0xb473, -0xeb2b, 0x1f40, 0xcdde, 0xfb49, 0x44ba, 0xf7a8, 0x0b44, 0x3ad4, 0xdf56, 0xbc11, -0x192e, 0xfdae, 0xc1e6, 0x2be2, 0x260a, 0xe13b, 0x285b, 0x1e09, 0xb912, 0xe471, -0x2223, 0xd36d, 0xfe2f, 0x46c5, 0xe36b, 0xf7a8, 0x4755, 0xdf55, 0xa7b3, 0x1a23, -0x0b84, 0xc893, 0x2faf, 0x2991, 0xe2bb, 0x2481, 0x13cb, 0xb383, 0xe0a3, 0x1702, -0xc7e4, 0xf45d, 0x4b67, 0xf6a6, 0xf97a, 0x3e6f, 0xecd9, 0xadf3, 0xfeb4, 0xfd06, -0xc788, 0x1f7a, 0x200a, 0xe5ba, 0x31ba, 0x2511, 0xb3da, 0xe100, 0x2776, 0xc990, -0xe1ba, 0x44f0, 0xef0a, 0xe6e3, 0x3c2c, 0xf024, 0xb397, 0x1376, 0x054e, 0xc2a5, -0x2c7f, 0x2067, 0xc5d1, 0x227f, 0x2507, 0xa6ee, 0xd8b4, 0x2da2, 0xd0d2, 0xe5d2, -0x4ae3, 0xf3c8, 0xec01, 0x3d1d, 0xe4c8, 0xa76d, 0x0cd7, 0x02fe, 0xc051, 0x2a3f, -0x2ff4, 0xd96d, 0x206e, 0x2cdb, 0xbcc2, 0xcfce, 0x1d9e, 0xd936, 0xe286, 0x3c62, -0xf899, 0xf161, 0x4188, 0xfacc, 0xb89c, 0x13b2, 0x0f18, 0xbfc2, 0x1908, 0x2b9b, -0xdd40, 0x1c58, 0x31c8, 0xcc18, 0xe1de, 0x3029, 0xe1a2, 0xeab8, 0x4814, 0xf8bb, -0xec3b, 0x41cd, 0xfd3d, 0xbb42, 0x1ab1, 0x1bc5, 0xd03f, 0x2b94, 0x39e6, 0xe56b, -0x20f6, 0x302f, 0xc7e3, 0xe07e, 0x304b, 0xe35d, 0xef37, 0x556b, 0x0a77, 0xf3f3, -0x471e, 0x0715, 0xb6bf, 0x0a15, 0x15a7, 0xc8f0, 0x27ce, 0x49e1, 0xef3f, 0x24d8, -0x3fc8, 0xcf85, 0xd3a9, 0x2981, 0xe391, 0xe1e2, 0x5568, 0x1a44, 0xf6f8, 0x47a5, -0x0bb1, 0xb4f6, 0x0758, 0x180f, 0xc264, 0x1c7f, 0x4997, 0xe7b6, 0x1790, 0x3eec, -0xcc71, 0xc989, 0x25c5, 0xe0ff, 0xd5eb, 0x4cb5, 0x1585, 0xe9fb, 0x3f4e, 0x079f, -0xa4e8, 0xf8b6, 0x18b7, 0xbe0e, 0x140f, 0x4dcd, 0xe6f8, 0x106e, 0x3e48, 0xc659, -0xb8d1, 0x1fc6, 0xe2f7, 0xcafc, 0x4b21, 0x1aac, 0xddf9, 0x38ef, 0x0d86, 0xa172, -0xefb8, 0x1ae3, 0xb8b9, 0x022b, 0x4952, 0xe3b7, 0x0763, 0x3fb6, 0xcaa1, 0xb546, -0x2034, 0xe7b3, 0xc26e, 0x4341, 0x1b44, 0xd872, 0x37f9, 0x158c, 0xa302, 0xed16, -0x212e, 0xbda6, 0x0112, 0x4eb5, 0xe481, 0x0180, 0x448c, 0xd2be, 0xb34a, 0x2659, -0xf969, 0xc3e7, 0x4253, 0x2498, 0xd3ac, 0x2f84, 0x1c93, 0xa86a, 0xeae4, 0x2be9, -0xc824, 0xfb1e, 0x5254, 0xeb1f, 0xf85a, 0x40c6, 0xd602, 0xacc6, 0x20d2, 0xfcc1, -0xbfa1, 0x3d9b, 0x2b83, 0xd669, 0x2fcc, 0x202d, 0xa65f, 0xe170, 0x2e20, 0xdd4c, -0xe4e8, 0x1128, 0xe9c6, 0x2b2e, 0x54f5, 0xec68, 0xd881, 0x25b3, 0xe44f, 0x9e7f, -0xf130, 0xeb8e, 0xd492, 0x3413, 0x5451, 0x26e6, 0x289d, 0x2623, 0xcf51, 0xbd9d, -0xcfb5, 0xa20d, 0xdfe7, 0x36b7, 0x0cc4, 0x056d, 0x65fe, 0x4dbe, 0xdc03, 0xeb0b, -0xf3c5, 0xb95e, 0xbfab, 0xf463, 0x0575, 0x1cd4, 0x3d59, 0x1332, 0x1c9e, 0x36b2, -0xe6ff, 0xc1d2, 0xebab, 0xf875, 0xd9ed, 0xf751, 0x1ec8, 0x177a, 0x1fdf, 0x14b0, -0x158a, 0x1d8b, 0x09a1, 0xd3cc, 0xc301, 0xfa03, 0xda41, 0xd913, 0x31d2, 0x3bcd, -0x1398, 0x2276, 0x47f0, 0x04ad, 0xdb13, 0xde05, 0xbc40, 0xe0b4, 0xf233, 0xedf8, -0x2b4d, 0x5c62, 0x258d, 0x04db, 0x4756, 0x0b9d, 0xbaa9, 0xd2dc, 0xd3e6, 0xd2e5, -0xeb10, 0x1044, 0x260b, 0x4492, 0x307c, 0x0989, 0x37e3, 0x1952, 0xc9b1, 0xbe5d, -0xd980, 0xdd51, 0xcfb1, 0x0665, 0x3440, 0x3f66, 0x32e5, 0x2caa, 0x3828, 0x04a2, -0xd6f3, 0xb98c, 0xc152, 0xdc75, 0xd7fc, 0x131d, 0x50a8, 0x4584, 0x218f, 0x39e9, -0x3a8c, 0xe3c0, 0xca56, 0xc147, 0xc3c0, 0xe3a0, 0xec33, 0x13df, 0x4aaa, 0x4c83, -0x117c, 0x2896, 0x399a, 0xdc86, 0xc6f6, 0xcd47, 0xc6ce, 0xd9cc, 0xf86e, 0x19ad, -0x3a91, 0x491f, 0x123e, 0x24d9, 0x3cde, 0xe11b, 0xb37c, 0xc2df, 0xd7a4, 0xd130, -0xef29, 0x2c49, 0x3d20, 0x37d9, 0x16ea, 0x291c, 0x26ac, 0xda1f, 0xaaed, 0xb607, -0xe686, 0xd568, 0xe17d, 0x337f, 0x4b43, 0x2881, 0x1389, 0x38c7, 0x12c8, 0xcd26, -0xbcc3, 0xb56e, 0xdd1b, 0xe483, 0xea42, 0x2a90, 0x51ed, 0x261e, 0x0805, 0x4126, -0x1494, 0xbddc, 0xbaf3, 0xc450, 0xd324, 0xd5cd, 0xf712, 0x2ef6, 0x4d20, 0x2af3, -0x0e0c, 0x3762, 0x0ac6, 0xbe79, 0xae49, 0xc076, 0xd8d1, 0xd3ec, 0x0029, 0x3a43, -0x4756, 0x22ca, 0x1958, 0x35d2, 0xf721, 0xbeb2, 0xb3fe, 0xbef0, 0xda1f, 0xdc92, -0x07d3, 0x4050, 0x4bd0, 0x19d3, 0x154a, 0x3522, 0xefeb, 0xbbc4, 0xb715, 0xc994, -0xdf48, 0xe0b3, 0x1046, 0x40b6, 0x462c, 0x16f6, 0x1aab, 0x3230, 0xe981, 0xbd2a, -0xbab3, 0xd2a2, 0xe5c9, 0xe97f, 0x1c0b, 0x466f, 0x4272, 0x167d, 0x2642, 0x3042, -0xe930, 0xc684, 0xbe13, 0xdccc, 0xed63, 0xea9d, 0x2413, 0x5441, 0x43f9, 0x180c, -0x36e2, 0x2e3f, 0xdd7b, 0xc957, 0xc358, 0xdaa2, 0xeec1, 0xf6ba, 0x27c0, 0x5560, -0x448c, 0x1635, 0x38ef, 0x285a, 0xd524, 0xc336, 0xc6ef, 0xdd82, 0xe86a, 0x024a, -0x3514, 0x5243, 0x4024, 0x1679, 0x30ad, 0x19cb, 0xd2e3, 0xbea4, 0xc57f, 0xe4e0, -0xe5d6, 0xfff4, 0x3873, 0x4af7, 0x2f8e, 0x188b, 0x3243, 0x0881, 0xcd5e, 0xbb93, -0xbf31, 0xe52f, 0xe3f9, 0xfc94, 0x39c7, 0x4714, 0x1d41, 0x169d, 0x38c9, 0xfecd, -0xcb1a, 0xc0fc, 0xbdbe, 0xdc75, 0xe1b5, 0xfaa7, 0x3213, 0x48a1, 0x1b68, 0x15dc, -0x3a99, 0xf55f, 0xc01b, 0xbf71, 0xc2a6, 0xd4e4, 0xdf4d, 0x066a, 0x3170, 0x40f1, -0x1a26, 0x18e0, 0x342c, 0xedf9, 0xba41, 0xb3b4, 0xc38e, 0xd795, 0xe02b, 0x10cf, -0x387d, 0x3bf9, 0x151b, 0x1afe, 0x254b, 0xde99, 0xbc43, 0xb3bd, 0xc921, 0xde4a, -0xe189, 0x1332, 0x3ec6, 0x3af6, 0x0d82, 0x1fcf, 0x23ba, 0xd6bf, 0xb847, 0xb180, -0xcc73, 0xe0cc, 0xeb2b, 0x1cb5, 0x417e, 0x395b, 0x0e37, 0x260e, 0x1c37, 0xceea, -0xb693, 0xb466, 0xd44d, 0xde55, 0xeccf, 0x28e9, 0x4c64, 0x3a26, 0x1085, 0x2a16, -0x12d5, 0xcc04, 0xb7f6, 0xb4e4, 0xda25, 0xe424, 0xf801, 0x3387, 0x4c82, 0x2ff0, -0x133d, 0x33da, 0x0a6a, 0xc653, 0xba9f, 0xb9a4, 0xdb0d, 0xe2d3, 0xfd70, 0x3932, -0x535b, 0x2ec6, 0x131d, 0x36f2, 0x041c, 0xc4de, 0xbc13, 0xbb88, 0xdb49, 0xe632, -0x0792, 0x3bf9, 0x5215, 0x2df5, 0x197b, 0x38ba, 0xfc1c, 0xc000, 0xb9c1, 0xc559, -0xe3b9, 0xea55, 0x13bb, 0x45e0, 0x506f, 0x2598, 0x1a8a, 0x31d4, 0xf25d, 0xc2c1, -0xb752, 0xc926, 0xeaa3, 0xebad, 0x18ac, 0x4b3a, 0x4b14, 0x1dc9, 0x246e, 0x2f24, -0xe2ff, 0xc4be, 0xbca8, 0xccb7, 0xee7a, 0xf1d7, 0x1fad, 0x5446, 0x4e8c, 0x1a55, -0x2bfd, 0x2de3, 0xdbcc, 0xc7c4, 0xc19e, 0xcdf0, 0xeebf, 0x0347, 0x2b46, 0x50aa, -0x4efc, 0x1ae7, 0x2bc2, 0x29fc, 0xd661, 0xbcdd, 0xc496, 0xddc3, 0xe917, 0xff6c, -0x336f, 0x4f83, 0x433f, 0x1820, 0x2877, 0x176c, 0xd63d, 0xbf37, 0xbd41, 0xe3e4, -0xec70, 0xfccc, 0x35e8, 0x4cad, 0x2f68, 0x12a7, 0x2fac, 0x08d7, 0xcd76, 0xc4f5, -0xbf59, 0xdfbb, 0xe9f3, 0xfd34, 0x3194, 0x4aa3, 0x2767, 0x0f36, 0x33c2, 0x0048, -0xc54f, 0xc2fd, 0xbd10, 0xd605, 0xe41c, 0xff51, 0x2a83, 0x4801, 0x28e6, 0x0aff, -0x2f62, 0xfdb7, 0xbd39, 0xb775, 0xc18e, 0xd7dc, 0xe021, 0x0a7d, 0x33af, 0x423a, -0x2081, 0x0d26, 0x28f2, 0xf6aa, 0xc604, 0xb20f, 0xca22, 0xf929, 0xe0c9, 0xfe41, -0x2c91, 0x285c, 0x3479, 0x40e1, 0x11dd, 0xd23c, 0xd207, 0xa861, 0xa807, 0xfe96, -0xf904, 0x0d6b, 0x7934, 0x78f8, 0x0f08, 0xfce1, 0xe859, 0x8e1b, 0xa714, 0xc4a3, -0xd05d, 0x3229, 0x6379, 0x4af3, 0x48ea, 0x4a2d, 0xe67d, 0xa875, 0xd782, 0xc120, -0xb7cb, 0xfb9f, 0x1f2e, 0x34e8, 0x4bf0, 0x20cd, 0xf0b2, 0x1643, 0x0563, 0xbcd0, -0xf27f, 0x2741, 0xe7cd, 0xef58, 0x2d5f, 0xebc6, 0xc657, 0x2de0, 0x1cb6, 0xe47e, -0x4360, 0x2d6e, 0xe23e, 0x16ee, 0xf98d, 0xa445, 0xddbb, 0x207f, 0xe2c0, 0x123e, -0x5bce, 0x016d, 0x1099, 0x3582, 0xc93d, 0xb242, 0x0573, 0xeaa3, 0xcd42, 0x3ec8, -0x2edf, 0xedf6, 0x39f9, 0x19b8, 0xb85b, 0xe3bc, 0x1369, 0xd1a8, 0xf1db, 0x3e71, -0xf679, 0x05fa, 0x4211, 0xe5a8, 0xbfbb, 0x17fb, 0x0686, 0xc767, 0x1e4b, 0x24e5, -0xdea8, 0x2460, 0x26bf, 0xbfbf, 0xebef, 0x3662, 0xdb5b, 0xe735, 0x478f, 0xf36f, -0xe6aa, 0x3a12, 0xedab, 0xb885, 0x2168, 0x1a11, 0xd137, 0x2b7c, 0x29a9, 0xd6be, -0x2004, 0x24d0, 0xb744, 0xddad, 0x2f52, 0xe201, 0xeeef, 0x4b3b, 0xfc02, 0xef7b, -0x3d92, 0xf6b9, 0xb6be, 0x0b17, 0x0d77, 0xc534, 0x191c, 0x30b3, 0xe945, 0x23b0, -0x2f3b, 0xc9bb, 0xd7dc, 0x269c, 0xe46c, 0xd902, 0x3766, 0x0212, 0xeb25, 0x39af, -0x03e0, 0xb8e7, 0x077c, 0x169d, 0xcc6f, 0x1b10, 0x2e0e, 0xd19a, 0x0fd8, 0x38f6, -0xca91, 0xc8f9, 0x29d6, 0xf078, 0xdd5c, 0x3b96, 0x09ef, 0xf001, 0x3507, 0xfbb1, -0xb660, 0x03d5, 0x0e0a, 0xc387, 0x17f0, 0x3e5b, 0xe93c, 0x15a4, 0x38a5, 0xd46a, -0xc5fe, 0x1360, 0xe4bc, 0xda9b, 0x39bc, 0x0c8d, 0xef2a, 0x3f9f, 0x0fdb, 0xb193, -0xf8d2, 0x1694, 0xb855, 0xffea, 0x4558, 0xed39, 0x083b, 0x3cd1, 0xda45, 0xc36d, -0x1e6e, 0xe476, 0xc765, 0x430f, 0x19a5, 0xde45, 0x3c55, 0x16d3, 0xa619, 0xf171, -0x1f0a, 0xbe1b, 0x04f9, 0x4d9d, 0xeec4, 0x0d1f, 0x3e78, 0xccbe, 0xb49f, 0x1d9b, -0xeb29, 0xc2b6, 0x448d, 0x28e1, 0xe2a4, 0x3aab, 0x21b9, 0xad2c, 0xe5d9, 0x1d14, -0xbe21, 0xf5ec, 0x4db1, 0xef99, 0x030b, 0x4e45, 0xe3cc, 0xb105, 0x20c5, 0xfc50, -0xb9ac, 0x35e2, 0x2898, 0xd9cf, 0x3397, 0x236d, 0xace6, 0xea90, 0x2c38, 0xc65d, -0xf7eb, 0x5313, 0xe39f, 0xf211, 0x477a, 0xdbe9, 0xad7a, 0x1ffa, 0x0017, 0xc201, -0x3906, 0x27ff, 0xd56a, 0x2d70, 0x20b7, 0xa843, 0xe22b, 0x2d3a, 0xc8d1, 0xeff9, -0x5809, 0xf171, 0xf1ca, 0x4aa1, 0xe752, 0xa7f6, 0x1531, 0x03a8, 0xbd38, 0x3515, -0x35e4, 0xd873, 0x2cec, 0x2fa3, 0xaedb, 0xd92e, 0x2cc6, 0xccea, 0xe531, 0x5116, -0xf947, 0xf6dd, 0x4ce8, 0xef45, 0xaeac, 0x190f, 0x0bfc, 0xbc68, 0x2afb, 0x3612, -0xd723, 0x2687, 0x3899, 0xbdfb, 0xda26, 0x3069, 0xdbaf, 0xe6bf, 0x5016, 0x0057, -0xf1d5, 0x480e, 0xf64b, 0xad29, 0x13a0, 0x16f4, 0xc33e, 0x2614, 0x4041, 0xe193, -0x201a, 0x3648, 0xc1be, 0xd35e, 0x2aee, 0xe031, 0xe3b6, 0x52de, 0x0b18, 0xedc3, -0x4b67, 0x084a, 0xaf95, 0x0ccc, 0x1af7, 0xc089, 0x19da, 0x4369, 0xe6d2, 0x1d97, -0x422d, 0xcfe2, 0xd1f7, 0x2e93, 0xe6d4, 0xd904, 0x4acc, 0x0d46, 0xe47d, 0x425b, -0x0eb4, 0xb1bb, 0x06af, 0x2009, 0xc469, 0x12a4, 0x3fee, 0xdd98, 0x0e0f, 0x3d40, -0xcdb1, 0xc9b2, 0x2f38, 0xedc8, 0xcf7d, 0x42ef, 0x1026, 0xd9af, 0x3710, 0x1033, -0xad12, 0xfd62, 0x2285, 0xc358, 0x0c72, 0x48be, 0xe51c, 0x0a9a, 0x2ff8, 0xbdc8, -0xc28d, 0x2cc9, 0xfec9, 0xc7ca, 0x13e5, 0x17e1, 0x02ca, 0x35c6, 0x0fac, 0xcd93, -0xf8c0, 0xfbac, 0xbba6, 0xe67f, 0x0735, 0xd6a6, 0x1bde, 0x7350, 0x2eb9, 0xfad9, -0x2334, 0xeee4, 0xa104, 0xc9e5, 0xcf89, 0xd376, 0x2f8b, 0x2f58, 0x13f9, 0x6199, -0x5172, 0xd760, 0xdae6, 0x0109, 0xaaab, 0x9f01, 0x11f6, 0x1c74, 0xff8b, 0x303b, -0x3d89, 0x2a49, 0x1900, 0xe5d6, 0xbb94, 0xe00a, 0xeeb6, 0xce23, 0x0253, 0x3291, -0x20e8, 0x0d77, 0x2650, 0x2ac1, 0xf46d, 0xf971, 0xdbe5, 0xc599, 0xf13a, 0xdd86, -0xebfb, 0x30a4, 0x422d, 0x0726, 0x20e1, 0x53cf, 0xe990, 0xbf8d, 0xd717, 0xc738, -0xcc12, 0xed69, 0x1207, 0x2679, 0x4b59, 0x2072, 0x0f6e, 0x3b3c, 0xef93, 0xaf82, -0xc07e, 0xdcf5, 0xccea, 0xe17b, 0x31b5, 0x3ad1, 0x3673, 0x24ae, 0x1fad, 0x2260, -0xf0dd, 0xc29e, 0xb494, 0xe43c, 0xe377, 0xdb2b, 0x2318, 0x45fc, 0x34e0, 0x20c4, -0x3ea5, 0x232d, 0xdb31, 0xcc1d, 0xb320, 0xc65a, 0xdf18, 0xea47, 0x25f0, 0x5cd4, -0x4386, 0x10e7, 0x3d78, 0x214b, 0xc538, 0xbca9, 0xbe8b, 0xcd16, 0xe049, 0x05f5, -0x2fe8, 0x5197, 0x45f2, 0x0fc2, 0x2f2f, 0x1971, 0xc5a7, 0xb7fc, 0xca80, 0xd975, -0xd7df, 0x0c99, 0x3a4e, 0x3fc6, 0x30e6, 0x13a0, 0x2b87, 0x0f79, 0xcd82, 0xaf27, -0xc107, 0xe381, 0xd5f3, 0x035c, 0x44e6, 0x44fc, 0x1db9, 0x1e95, 0x3a4e, 0xf713, -0xc17f, 0xb545, 0xc44b, 0xea43, 0xe0b2, 0x034f, 0x458e, 0x4e96, 0x1557, 0x1819, -0x3caa, 0xeb53, 0xb81f, 0xc29d, 0xcb34, 0xd827, 0xea2e, 0x153f, 0x3683, 0x4599, -0x17a8, 0x1025, 0x3238, 0xefae, 0xb3df, 0xb7d0, 0xd811, 0xd6f7, 0xd85a, 0x1792, -0x3a94, 0x35ea, 0x1657, 0x21c8, 0x243d, 0xdfb1, 0xbb30, 0xb0e9, 0xceed, 0xddbf, -0xdff2, 0x1be1, 0x44e0, 0x3265, 0x0879, 0x260d, 0x1f6a, 0xcd4c, 0xba1b, 0xbafd, -0xcb3d, 0xd899, 0xeb07, 0x19d6, 0x3c13, 0x3544, 0x08ed, 0x2407, 0x1daa, 0xcd68, -0xb792, 0xbf01, 0xd1c2, 0xd611, 0xf34b, 0x259b, 0x3b2f, 0x2f39, 0x1053, 0x2d78, -0x1a05, 0xd265, 0xba97, 0xc02a, 0xdab7, 0xd903, 0xf86d, 0x2ff9, 0x403d, 0x28d3, -0x14a1, 0x34af, 0x0a0e, 0xcda4, 0xc3ac, 0xc13a, 0xdd8d, 0xe2ea, 0xfd27, 0x2eb2, -0x45ac, 0x26be, 0x14ec, 0x3dd9, 0x06bc, 0xc81d, 0xc6e2, 0xc719, 0xd8c3, 0xe5c7, -0x0b66, 0x32c9, 0x4b92, 0x2cb4, 0x19ce, 0x3c10, 0x0272, 0xc5eb, 0xbb96, 0xccd7, -0xe07a, 0xdfbb, 0x160a, 0x4341, 0x4749, 0x2676, 0x21c7, 0x2fd3, 0xf0d0, 0xc6ee, -0xb624, 0xcad8, 0xe85f, 0xe7c5, 0x1d81, 0x4b84, 0x45af, 0x1eba, 0x2c0d, 0x30c1, -0xe4ba, 0xca8d, 0xbdbe, 0xcc89, 0xed50, 0xf3dd, 0x2203, 0x5159, 0x4cd1, 0x1b22, -0x2e08, 0x3255, 0xe28f, 0xc7e6, 0xc2e3, 0xd510, 0xe8de, 0xfa14, 0x2846, 0x4a33, -0x4c04, 0x1fed, 0x2f64, 0x2cdb, 0xe195, 0xc51d, 0xc13f, 0xdb62, 0xe696, 0xfa3e, -0x31bd, 0x4d3c, 0x4355, 0x2150, 0x33b3, 0x204a, 0xe0d7, 0xc877, 0xbdd9, 0xdeff, -0xeb46, 0x0024, 0x3a0c, 0x5485, 0x3c57, 0x1fe4, 0x3a57, 0x110e, 0xd1f0, 0xc4f8, -0xbfae, 0xe303, 0xf0e8, 0x0757, 0x3795, 0x525c, 0x364d, 0x163e, 0x3411, 0x078d, -0xc95e, 0xbed3, 0xc146, 0xdd79, 0xe8cb, 0x0fb0, 0x3b8b, 0x4b40, 0x2f52, 0x1708, -0x2c9b, 0xf6f7, 0xc33e, 0xb8f8, 0xc0a0, 0xe27b, 0xe6d8, 0x0c22, 0x3d9b, 0x4878, -0x24a6, 0x186c, 0x2467, 0xe600, 0xc014, 0xb323, 0xbc0b, 0xe0eb, 0xe8da, 0x0f11, -0x3cef, 0x40c5, 0x12df, 0x1430, 0x227a, 0xdb2e, 0xb8c4, 0xb5de, 0xc528, 0xdd7a, -0xe824, 0x13d4, 0x3e15, 0x4384, 0x124e, 0x1469, 0x1e10, 0xd6df, 0xb7fb, 0xb8e3, -0xcdaf, 0xdd42, 0xecbb, 0x1e7d, 0x3c6f, 0x378a, 0x1149, 0x1e8a, 0x1b08, 0xd447, -0xb3c7, 0xb0b3, 0xd341, 0xe16d, 0xebda, 0x22db, 0x4450, 0x345d, 0x0fcd, 0x27aa, -0x137c, 0xcfd2, 0xc241, 0xbb1b, 0xd538, 0xe771, 0xf62c, 0x297f, 0x4c37, 0x3271, -0x103e, 0x360c, 0x143c, 0xc8cb, 0xc2e2, 0xc46f, 0xd919, 0xe83f, 0x0316, 0x2e44, -0x49f7, 0x320d, 0x1102, 0x34eb, 0x0f20, 0xc998, 0xc1e5, 0xc68b, 0xd8b7, 0xe3aa, -0x0e51, 0x3839, 0x473d, 0x306d, 0x1936, 0x313b, 0x023a, 0xca3b, 0xb9cb, 0xc4e8, -0xe9a8, 0xe8e8, 0x0e4f, 0x4390, 0x4a72, 0x23d2, 0x1d52, 0x31ca, 0xf0b0, 0xc813, -0xbd25, 0xc202, 0xe6ea, 0xee18, 0x12c8, 0x4476, 0x4b2e, 0x198b, 0x19a1, 0x2fcc, -0xe518, 0xbf8d, 0xbe9e, 0xcb6d, 0xe622, 0xee0e, 0x167e, 0x3fbf, 0x46fb, 0x1a7d, -0x1f4e, 0x2d08, 0xe17f, 0xbedd, 0xbaa3, 0xcc16, 0xe196, 0xeedc, 0x21cc, 0x44ed, -0x4348, 0x1a18, 0x2363, 0x242e, 0xdc65, 0xbd61, 0xb300, 0xcfa0, 0xe712, 0xe5c7, -0x169f, 0x5f17, 0x7299, 0x2bb1, 0x0b65, 0xe2fd, 0x9b1c, 0xa3b5, 0xaeb5, 0xe147, -0x3787, 0x593a, 0x5290, 0x4a3c, 0x2e69, 0xcbb4, 0xa91f, 0xc5a3, 0xbd85, 0xd07b, -0x1055, 0x3d01, 0x2411, 0x222c, 0x2b1a, 0x0b05, 0xffdc, 0xe3f2, 0xcf8c, 0xf390, -0x15ad, 0xd9f5, 0xc944, 0x2d37, 0x09de, 0xc93f, 0x0d00, 0x229d, 0x0eb5, 0x31fb, -0x193d, 0xdefd, 0x0b6e, 0xe2b8, 0x80c1, 0xd2cb, 0x1719, 0xe9c2, 0x3008, 0x6a8a, -0xfee8, 0x01bc, 0x299a, 0xb39d, 0xa48f, 0x0798, 0xe5e9, 0xddfa, 0x41e1, 0x17dc, -0xf43b, 0x3829, 0x05c3, 0xbd89, 0xf1bc, 0x0dab, 0xcd37, 0xfad8, 0x2e14, 0xf080, -0x107b, 0x24ea, 0xd852, 0xd4a0, 0x17fd, 0xfd24, 0xd818, 0x2b57, 0x168d, 0xdb81, -0x1759, 0x0cdb, 0xc867, 0xf2e6, 0x257d, 0xe349, 0xfff1, 0x39a1, 0xe33d, 0xee45, -0x2b9d, 0xe388, 0xc702, 0x21a8, 0x103e, 0xda0f, 0x2f4b, 0x1b70, 0xd6c1, 0x1b55, -0x104d, 0xb577, 0xe811, 0x28c6, 0xe0aa, 0xfee3, 0x4403, 0xf56f, 0xf711, 0x2c32, -0xe60a, 0xbd96, 0x0d61, 0x048f, 0xce3d, 0x272f, 0x325d, 0xf1c7, 0x2094, 0x1ff0, -0xc860, 0xe30a, 0x2515, 0xdac5, 0xe92d, 0x441f, 0x0548, 0xfb76, 0x3d79, 0xfd5c, -0xbecb, 0x1149, 0x13f9, 0xd214, 0x2a6f, 0x3388, 0xe6f8, 0x2190, 0x2948, 0xc4a5, -0xdd42, 0x2f43, 0xe6af, 0xf03f, 0x54db, 0x13d9, 0xfca6, 0x3215, 0xf506, 0xc57a, -0x12c0, 0x0f9f, 0xd3cc, 0x322e, 0x4301, 0xf273, 0x23ab, 0x356e, 0xd3b2, 0xd051, -0x21b7, 0xf281, 0xecda, 0x4489, 0x12f0, 0x0242, 0x4407, 0x06d1, 0xbafe, 0x0b6c, -0x1d26, 0xc6ee, 0x19bc, 0x44e9, 0xe86d, 0x13b7, 0x39ca, 0xd446, 0xd119, 0x2941, -0xee93, 0xe7c5, 0x4bc2, 0x0643, 0xe908, 0x4535, 0x0444, 0xaaed, 0x0a10, 0x21a1, -0xc807, 0x1964, 0x41d6, 0xe77b, 0x16b3, 0x34e4, 0xc869, 0xc8dc, 0x2196, 0xe4ba, -0xdb97, 0x4973, 0x106a, 0xe7d9, 0x3cc5, 0x0a39, 0xa769, 0xf083, 0x1925, 0xc4c2, -0x075d, 0x3c4a, 0xe3ca, 0x0d28, 0x38d3, 0xc8d1, 0xbd34, 0x29a4, 0xea91, 0xbfc6, -0x3ad0, 0x0fbe, 0xd348, 0x3125, 0x0f03, 0xa6a3, 0xf4e3, 0x222b, 0xbe73, 0x00e2, -0x3cfa, 0xd671, 0xff6d, 0x3893, 0xc753, 0xb6c9, 0x236e, 0xec95, 0xc571, 0x3925, -0x1324, 0xdbd6, 0x2c09, 0x07a8, 0xaa2d, 0xf242, 0x1fa6, 0xc716, 0x042c, 0x441c, -0xe490, 0xffd3, 0x3dc4, 0xdc2a, 0xb9f6, 0x1f30, 0xfbb0, 0xca2e, 0x371c, 0x2003, -0xddf1, 0x31fa, 0x2307, 0xb4d8, 0xf004, 0x2cbc, 0xc878, 0xf7c6, 0x4caf, 0xed6b, -0xfe78, 0x48e2, 0xe5f3, 0xb861, 0x209c, 0xffdd, 0xc990, 0x3a8f, 0x2235, 0xd8df, -0x3307, 0x26fb, 0xb85c, 0xf04e, 0x2cb8, 0xd049, 0xfa7c, 0x49ae, 0xedac, 0xfa25, -0x3f7d, 0xe587, 0xbae6, 0x1de2, 0x03f4, 0xc964, 0x35b0, 0x2bf3, 0xdcc1, 0x287a, -0x2509, 0xb53e, 0xdd34, 0x21f5, 0xd014, 0xf4e6, 0x5327, 0xfbed, 0xf63d, 0x422a, -0xebde, 0xadff, 0x109b, 0x03c0, 0xc66e, 0x34a1, 0x371f, 0xe1e5, 0x266b, 0x298a, -0xb6ec, 0xdb6e, 0x2815, 0xd421, 0xf019, 0x5352, 0xff26, 0xf3f2, 0x43a2, 0xf389, -0xb155, 0x11bc, 0x093e, 0xc3cf, 0x2cd7, 0x37d3, 0xe04d, 0x22e3, 0x2ecd, 0xb8a8, -0xd4f6, 0x2b5c, 0xd879, 0xe9c1, 0x5345, 0x0074, 0xeb1b, 0x3d0d, 0xf0a8, 0xa78e, -0x0f83, 0x1369, 0xc523, 0x2e80, 0x3d4c, 0xdb13, 0x1e41, 0x2ecc, 0xb34d, 0xd013, -0x2f65, 0xd81e, 0xe458, 0x57d2, 0x0406, 0xebe0, 0x457a, 0xf7e1, 0xa66e, 0x0b6f, -0x0e7a, 0xb8d1, 0x251b, 0x3fe1, 0xdd7d, 0x20b9, 0x3687, 0xbb17, 0xd0b9, 0x2e06, -0xd6de, 0xd8c9, 0x50ce, 0x0665, 0xe605, 0x3fce, 0xfc63, 0xac6f, 0x0f4a, 0x249e, -0xcfcc, 0xfc68, 0x0488, 0xeb03, 0x4794, 0x43ec, 0xda83, 0xf3bd, 0x2644, 0xc019, -0xac19, 0xfdb7, 0xdae0, 0xebc8, 0x4a7e, 0x4842, 0x204b, 0x2f4a, 0x0bd7, 0xb466, -0xc7b6, 0xc25a, 0x9ff5, 0xfeaa, 0x3147, 0xf5d6, 0x1606, 0x7098, 0x2536, 0xcb1f, -0xef7c, 0xd8bb, 0xa610, 0xc6a6, 0xf94d, 0x01cf, 0x2326, 0x2e92, 0xfa36, 0x1b23, -0x1fee, 0xc9e0, 0xbf64, 0xf0f7, 0xebf9, 0xcd60, 0xfed7, 0x1727, 0x0a34, 0x1723, -0x0a36, 0x0e7b, 0x18aa, 0xfc64, 0xb854, 0xc985, 0xfdd8, 0xc68b, 0xea58, 0x3e80, -0x2a34, 0x0ba2, 0x2d28, 0x367f, 0xec1b, 0xe1de, 0xce20, 0xb654, 0xf084, 0xed7d, -0xf556, 0x44c2, 0x5de8, 0x118c, 0x103a, 0x43c2, 0xea97, 0xbbb3, 0xd781, 0xd5a7, -0xe105, 0xfd04, 0x1bc6, 0x2dbd, 0x4a4e, 0x2262, 0x090c, 0x37f3, 0x0697, 0xc337, -0xc445, 0xe70d, 0xe217, 0xdb53, 0x1953, 0x38c9, 0x40c1, 0x2ede, 0x22c3, 0x260c, -0xfc8c, 0xcfd4, 0xb0e1, 0xd39e, 0xe34c, 0xdae1, 0x253f, 0x508f, 0x342f, 0x1aa5, -0x3628, 0x1e6e, 0xd753, 0xc9cd, 0xb7c4, 0xd23c, 0xef41, 0xeb30, 0x1cc2, 0x523c, -0x3c38, 0x02b5, 0x29f9, 0x28a2, 0xd759, 0xd1f8, 0xd1bf, 0xd245, 0xe508, 0xfe91, -0x19fc, 0x3b30, 0x3d9b, 0x0a4c, 0x2fd0, 0x37f4, 0xdc8c, 0xbe49, 0xd7b4, 0xe591, -0xd3af, 0xfd0f, 0x3070, 0x36f1, 0x325e, 0x18a4, 0x3386, 0x27ca, 0xdc73, 0xb513, -0xcdc8, 0xf809, 0xd5d9, 0xf013, 0x4334, 0x4964, 0x2395, 0x1b7b, 0x3c5a, 0x0fe4, -0xd7a3, 0xc6c6, 0xc6b6, 0xf0cf, 0xeb7d, 0xf80b, 0x3914, 0x525a, 0x2445, 0x1417, -0x4928, 0x0ff5, 0xc35d, 0xc78a, 0xd244, 0xdff9, 0xe35e, 0x07df, 0x35ae, 0x4ba7, -0x2974, 0x1106, 0x3cf3, 0x0fec, 0xc4b4, 0xb9c7, 0xd562, 0xe489, 0xd970, 0x0e52, -0x3f61, 0x43d6, 0x27aa, 0x2131, 0x364b, 0xfd16, 0xc955, 0xb961, 0xcde0, 0xe8ce, -0xe024, 0x0e4f, 0x4544, 0x4582, 0x18cd, 0x22be, 0x3a77, 0xf22e, 0xc4f3, 0xbabf, -0xcce0, 0xe32e, 0xe352, 0x12f7, 0x43a1, 0x4666, 0x1401, 0x1c0c, 0x30e6, 0xe619, -0xbb56, 0xbb95, 0xd491, 0xe00b, 0xe61a, 0x17a4, 0x3b47, 0x3b00, 0x10e7, 0x1c7e, -0x2707, 0xe361, 0xb750, 0xb2d2, 0xd7be, 0xdb41, 0xe145, 0x2096, 0x40ed, 0x319a, -0x118b, 0x2924, 0x1a2e, 0xd6af, 0xba5f, 0xad6c, 0xd2c9, 0xe064, 0xe67c, 0x25df, -0x4ea4, 0x3357, 0x0fc7, 0x312c, 0x1020, 0xc418, 0xb5ff, 0xb68a, 0xd4f7, 0xe2ef, -0xfb14, 0x2e11, 0x4c3e, 0x3280, 0x08fc, 0x2966, 0x0e36, 0xc69d, 0xb3ba, 0xbf26, -0xde72, 0xe461, 0x0918, 0x3887, 0x4967, 0x313c, 0x1236, 0x2763, 0x0526, 0xcb44, -0xb35a, 0xc8b5, 0xed8f, 0xdf83, 0x05bf, 0x41b2, 0x4548, 0x20ab, 0x1a30, 0x2bd9, -0xf884, 0xce0f, 0xb377, 0xc10c, 0xede8, 0xe5b7, 0x0520, 0x42f2, 0x4b64, 0x19c0, -0x1a50, 0x2fd7, 0xee52, 0xc8d4, 0xbcea, 0xc661, 0xe5d5, 0xe9b8, 0x0e4b, 0x42da, -0x4c23, 0x187a, 0x1ba9, 0x2fdd, 0xe89e, 0xbcad, 0xb84a, 0xd201, 0xe619, 0xeba8, -0x1a61, 0x40ef, 0x41b7, 0x151a, 0x1cbf, 0x2b05, 0xea44, 0xc1f8, 0xbc6c, 0xdb3c, -0xe422, 0xe69f, 0x208e, 0x4881, 0x3cc7, 0x1503, 0x2aa9, 0x27ed, 0xe431, 0xc5cc, -0xbdfa, 0xdd9a, 0xe8e9, 0xf074, 0x2496, 0x4a59, 0x3e74, 0x1374, 0x2e8f, 0x252c, -0xd82c, 0xba66, 0xbeda, 0xe1cc, 0xe3a5, 0xf456, 0x3043, 0x4df4, 0x382a, 0x0d8b, -0x25d9, 0x1516, 0xd249, 0xb805, 0xbbdd, 0xe317, 0xe31f, 0xf717, 0x307f, 0x4438, -0x2ce7, 0x1089, 0x29b8, 0x060d, 0xc757, 0xb6c7, 0xb848, 0xdd4c, 0xde36, 0xf60e, -0x341a, 0x45f6, 0x1fc8, 0x0b03, 0x2ed5, 0xff75, 0xc14a, 0xb3bb, 0xb4dc, 0xd6d2, -0xdc36, 0xfbb0, 0x356c, 0x49c3, 0x2300, 0x0fe9, 0x2f18, 0xf499, 0xb802, 0xb1b2, -0xc06f, 0xdc85, 0xde22, 0x0805, 0x3cc1, 0x488e, 0x1f2e, 0x11d7, 0x2c64, 0xf3b1, -0xbd78, 0xadbb, 0xc5eb, 0xe451, 0xde57, 0x0e82, 0x4140, 0x4137, 0x1916, 0x1b84, -0x28d4, 0xe639, 0xc1a0, 0xb2e7, 0xc697, 0xe4be, 0xdf87, 0x10dc, 0x49b4, 0x457d, -0x1479, 0x2405, 0x2b0f, 0xdcbe, 0xbefc, 0xb3a9, 0xc6dd, 0xe60b, 0xed45, 0x1b13, -0x497b, 0x4531, 0x1324, 0x24b0, 0x243c, 0xd210, 0xb83d, 0xb984, 0xd0bc, 0xe2a6, -0xf3b7, 0x2766, 0x4a82, 0x4204, 0x133f, 0x22b4, 0x1b4e, 0xd4a3, 0xba37, 0xbaaf, -0xdf26, 0xea12, 0xf84b, 0x3177, 0x4e17, 0x39af, 0x193b, 0x308a, 0x13c1, 0xd6b0, -0xc5d3, 0xbffc, 0xe599, 0xeec6, 0xff4d, 0x38ac, 0x5167, 0x346a, 0x1b9d, 0x376a, -0x0ce4, 0xd4a8, 0xc78f, 0xc195, 0xe4f4, 0xf0f5, 0x07bb, 0x3cdc, 0x5981, 0x3510, -0x1b26, 0x3a31, 0x03fb, 0xca72, 0xc53e, 0xcd25, 0xecab, 0xf768, 0x193e, 0x437d, -0x53af, 0x2c6f, 0x15b8, 0x31bf, 0xff10, 0xcc99, 0xc147, 0xd348, 0xee05, 0xee95, -0x1782, 0x2f2b, 0x4b20, 0x5a80, 0x2b14, 0xfd25, 0xdd6a, 0xccb5, 0x9c77, 0xbb91, -0x0abb, 0xfa6d, 0x308d, 0x8b3b, 0x6386, 0x0f1b, 0x0366, 0xd9a6, 0x969a, 0xbb36, -0xbe42, 0xd650, 0x4a43, 0x5e11, 0x39f6, 0x4e5f, 0x3f3f, 0xd1f7, 0xbac7, 0xda01, -0xb75e, 0xdb85, 0x0689, 0x0f50, 0x402d, 0x3a48, 0xf492, 0xf3ba, 0x232e, 0xe3c2, -0xc221, 0x1c7e, 0x103b, 0xda32, 0x0d99, 0x12a9, 0xc374, 0xe4a9, 0x2fee, 0xef59, -0xf571, 0x4551, 0x09ce, 0xee4d, 0x190c, 0xdd87, 0xa5eb, 0xf5c9, 0x0d02, 0xd8dd, -0x32e0, 0x4556, 0xf27b, 0x272d, 0x1a0f, 0xac2d, 0xc929, 0x0d48, 0xd065, 0xe73f, -0x4fb6, 0x0a4b, 0xfcc1, 0x4512, 0xed54, 0xb2b9, 0x0350, 0xfc0b, 0xbed7, 0x1926, -0x2f0c, 0xe1a6, 0x2871, 0x338a, 0xc365, 0xd8fc, 0x2261, 0xe12a, 0xda5e, 0x3836, -0x02a6, 0xeb2d, 0x3c29, 0xfc95, 0xb47f, 0x126c, 0x2240, 0xc4df, 0x1301, 0x42d9, -0xddbd, 0x05c8, 0x2ff4, 0xc609, 0xc9ef, 0x2ebd, 0xf193, 0xda07, 0x46da, 0x0cd3, -0xe052, 0x39eb, 0x0085, 0xa3df, 0xfdbd, 0x1d67, 0xc36d, 0x1321, 0x461a, 0xe3db, -0x121d, 0x3bc5, 0xce7e, 0xc611, 0x1e92, 0xe869, 0xd146, 0x3d6e, 0x12f6, 0xe781, -0x3a2e, 0x100e, 0xb4c0, 0xf87f, 0x2451, 0xce12, 0xfd54, 0x3b61, 0xe9af, 0x0485, -0x363a, 0xd7a5, 0xc531, 0x26b6, 0xfb26, 0xd3af, 0x4024, 0x13d3, 0xcf38, 0x31ca, -0x1fe6, 0xacba, 0xe830, 0x28d0, 0xd67b, 0x03f4, 0x4390, 0xeff5, 0x0d9b, 0x3cdc, -0xd2ef, 0xbce5, 0x1df5, 0xf194, 0xcc0a, 0x3c7b, 0x22be, 0xe9d0, 0x37e4, 0x1f21, -0xbf6a, 0xed14, 0x13f9, 0xccad, 0xfeed, 0x36f2, 0xe931, 0x0bd9, 0x4870, 0xe9a7, -0xc649, 0x2406, 0x009e, 0xc7de, 0x28c6, 0x1d14, 0xe19d, 0x2d2e, 0x24eb, 0xc1ae, -0xf293, 0x2948, 0xd034, 0xfc36, 0x4c80, 0xed33, 0xf76a, 0x4574, 0xe546, 0xb51f, -0x205e, 0x01e4, 0xc8f6, 0x3c9b, 0x2dfd, 0xe000, 0x3231, 0x23e4, 0xac3f, 0xe1e2, -0x2470, 0xc806, 0xf279, 0x51f9, 0xfc93, 0xfd3d, 0x471f, 0xf14b, 0xb564, 0x0d8d, -0xf7a1, 0xbaf7, 0x2ae2, 0x3330, 0xe3cf, 0x2876, 0x2f9d, 0xbe99, 0xd8ed, 0x2066, -0xcc5b, 0xe037, 0x444b, 0xfc55, 0xf1ae, 0x3dee, 0xedb1, 0xa9f3, 0x0f58, 0x052a, -0xb88b, 0x286b, 0x32e9, 0xd191, 0x1a2d, 0x2b19, 0xb1ba, 0xd055, 0x2574, 0xcd9d, -0xe17a, 0x4f5d, 0xf991, 0xe9c8, 0x4424, 0xee22, 0xa03f, 0x0b3e, 0x0833, 0xb2f6, -0x25d7, 0x3fb5, 0xd971, 0x1eca, 0x3552, 0xb55f, 0xc5ed, 0x2341, 0xd2b5, 0xd7c1, -0x523e, 0x069d, 0xe63d, 0x453d, 0xfaec, 0xa137, 0x0414, 0x12c4, 0xb981, 0x1bb8, -0x4054, 0xdb21, 0x1feb, 0x3f6a, 0xbc6b, 0xcb19, 0x2eaa, 0xdddc, 0xd782, 0x53f0, -0x0db9, 0xe5f5, 0x4ae5, 0x09f1, 0xaaaa, 0x08ea, 0x1b96, 0xc280, 0x22c4, 0x4ce0, -0xe24f, 0x1cfe, 0x4491, 0xc23a, 0xc424, 0x2faf, 0xe9a8, 0xd606, 0x50b8, 0x16c3, -0xe5a3, 0x41e4, 0x0b1a, 0xa940, 0xfef6, 0x1bdf, 0xbfb1, 0x1607, 0x4e4e, 0xe252, -0x116e, 0x44af, 0xc71f, 0xbeed, 0x2c56, 0xed57, 0xd13f, 0x46da, 0x1220, 0xdf44, -0x3fcf, 0x0e72, 0xa687, 0xfe99, 0x22c2, 0xc0f4, 0x10f5, 0x4ab0, 0xdcc9, 0x0862, -0x42d3, 0xcce8, 0xbf2b, 0x2c4a, 0xf130, 0xcf23, 0x47a0, 0x1557, 0xd894, 0x3b17, -0x15eb, 0xac91, 0xfc3e, 0x2839, 0xc889, 0x0a6b, 0x4a0a, 0xe524, 0x0571, 0x40a3, -0xd642, 0xc2d3, 0x2b1c, 0xfa19, 0xd4f9, 0x465b, 0x1ef6, 0xe106, 0x371e, 0x15ec, -0xb185, 0xfc2f, 0x2898, 0xcc97, 0x0b55, 0x4c1f, 0xebc4, 0x08bc, 0x3f08, 0xd705, -0xc599, 0x2bb5, 0x081d, 0xcd96, 0x0d3b, 0x0ffa, 0x0823, 0x3f09, 0x1a45, 0xdad9, -0x0662, 0x07b7, 0xbb6a, 0xde9a, 0x0740, 0xda46, 0x0e1e, 0x6549, 0x38b1, 0x04bd, -0x2510, 0xf943, 0xad98, 0xc9a0, 0xca5c, 0xcaba, 0x22c9, 0x2b10, 0x01f0, 0x511a, -0x637c, 0xe409, 0xcfea, 0xff1b, 0xbae4, 0x9b34, 0xfafc, 0x154f, 0xfdb5, 0x2da1, -0x2e82, 0x1b20, 0x2472, 0xf46c, 0xbc6b, 0xd8c3, 0xf895, 0xcd65, 0xe4ee, 0x20c1, -0x1821, 0x08d6, 0x178c, 0x2a03, 0x07e8, 0xfe05, 0xe136, 0xbccf, 0xeb99, 0xdae7, -0xd3aa, 0x1d70, 0x3e94, 0x094b, 0x0900, 0x5434, 0x0901, 0xc4b1, 0xdc6e, 0xc832, -0xca7f, 0xe370, 0xfc55, 0x19fb, 0x5037, 0x3174, 0xfe1c, 0x3bdc, 0x1079, 0xb5a5, -0xbd97, 0xde91, 0xd7dc, 0xd8e9, 0x1ed3, 0x2fd7, 0x3373, 0x2e1c, 0x130d, 0x2861, -0x0ada, 0xcec0, 0xaf81, 0xd3f1, 0xe991, 0xcbf7, 0x0636, 0x408c, 0x3d14, 0x22da, -0x2e7f, 0x334f, 0xf091, 0xd23f, 0xb6f6, 0xbe39, 0xe0ea, 0xdac6, 0x0ab7, 0x52de, -0x5241, 0x1a28, 0x36a7, 0x3ee4, 0xdc10, 0xbfb1, 0xc06c, 0xc69f, 0xdf82, 0xf62e, -0x1f54, 0x4e25, 0x578d, 0x17a6, 0x26c4, 0x37fc, 0xe05d, 0xc099, 0xcd8c, 0xd886, -0xd8c3, 0xfcba, 0x2d83, 0x3fa6, 0x48bb, 0x1e77, 0x2a31, 0x31eb, 0xe98e, 0xbcf5, -0xc196, 0xe4e4, 0xdb34, 0xf4b5, 0x398d, 0x47e8, 0x3526, 0x2168, 0x3d9a, 0x20eb, -0xddcb, 0xbe09, 0xb85c, 0xe775, 0xe3f6, 0xedd1, 0x3731, 0x576a, 0x2d99, 0x1229, -0x4011, 0x0fb4, 0xc58a, 0xc2e0, 0xc163, 0xd8b3, 0xe485, 0xfbc4, 0x28a2, 0x4aba, -0x2b57, 0x06a4, 0x385f, 0x150e, 0xc2cd, 0xb65c, 0xcd33, 0xde60, 0xd4fe, 0x0096, -0x3507, 0x45bc, 0x2781, 0x142c, 0x33ba, 0x049c, 0xc775, 0xb39c, 0xc6ec, 0xe39b, -0xd75b, 0x0203, 0x3c34, 0x4589, 0x1c2f, 0x171d, 0x336b, 0xeff6, 0xbf31, 0xb8c6, -0xc21c, 0xde19, 0xe412, 0x0b3a, 0x399a, 0x4631, 0x1799, 0x116c, 0x3085, 0xeefe, -0xbcec, 0xb922, 0xcdc6, 0xe051, 0xe2d7, 0x10d0, 0x38e1, 0x3dfb, 0x1356, 0x1516, -0x2876, 0xe6f8, 0xbb6a, 0xb636, 0xd128, 0xdda0, 0xdf16, 0x11df, 0x387e, 0x338c, -0x0c5c, 0x211c, 0x2422, 0xdca8, 0xbf10, 0xb435, 0xcd32, 0xde18, 0xe25f, 0x144e, -0x3fe0, 0x3472, 0x0753, 0x2b41, 0x27e8, 0xd555, 0xc117, 0xbddb, 0xcb8c, 0xdaa8, -0xee16, 0x185e, 0x406a, 0x394d, 0x0e4c, 0x3224, 0x2283, 0xcee6, 0xb78f, 0xbe46, -0xd600, 0xd73a, 0xf882, 0x2db5, 0x4253, 0x3500, 0x1558, 0x2eee, 0x13ba, 0xd1b3, -0xb80a, 0xbd32, 0xe28d, 0xdf23, 0xfd44, 0x3980, 0x4ace, 0x2d3f, 0x18f4, 0x34ae, -0x039f, 0xcc97, 0xbb5e, 0xbe2a, 0xe6fa, 0xe555, 0x023b, 0x3de8, 0x4db4, 0x2192, -0x1578, 0x3a68, 0xfe29, 0xc84a, 0xbd0b, 0xc1ef, 0xe3a9, 0xe936, 0x0a3b, 0x3ab4, -0x505a, 0x260d, 0x1903, 0x39c0, 0xf9ac, 0xc53b, 0xbc41, 0xcc45, 0xe5fc, 0xe843, -0x15b8, 0x436d, 0x4dae, 0x24c5, 0x212b, 0x33f2, 0xf1b2, 0xc3a3, 0xb509, 0xcd57, -0xe8db, 0xeb7c, 0x1d1e, 0x4aef, 0x4a01, 0x1af5, 0x24c7, 0x2df1, 0xe4a5, 0xc1ae, -0xb8ad, 0xd2c8, 0xe887, 0xee7b, 0x21d0, 0x4d5f, 0x4806, 0x171f, 0x2854, 0x27e5, -0xdd26, 0xc075, 0xb9c1, 0xd6dc, 0xe94e, 0xf760, 0x2a99, 0x4d0c, 0x4405, 0x1751, -0x2a63, 0x1e7d, 0xd670, 0xba60, 0xb674, 0xde00, 0xe8fc, 0xf57e, 0x3121, 0x51b2, -0x3a66, 0x11d4, 0x29d9, 0x107f, 0xd01f, 0xbbd1, 0xb864, 0xe08f, 0xea2a, 0xfafd, -0x34de, 0x5038, 0x3061, 0x1294, 0x3316, 0x07d3, 0xc6ab, 0xbd82, 0xc0f2, 0xe421, -0xebe4, 0x0398, 0x39c6, 0x54a2, 0x2e8b, 0x0e55, 0x3308, 0x0648, 0xc5ad, 0xbd8e, -0xc53c, 0xdfb2, 0xe8b4, 0x0f4a, 0x3d14, 0x4caa, 0x2bf1, 0x15bf, 0x2e89, 0xf945, -0xc136, 0xb890, 0xcaeb, 0xe950, 0xe7bf, 0x115d, 0x451b, 0x4aa1, 0x20cd, 0x1684, -0x2a91, 0xf0a3, 0xc5b7, 0xb815, 0xc9bf, 0xee16, 0xea12, 0x117d, 0x470b, 0x42c7, -0x113f, 0x1b7c, 0x2b91, 0xe191, 0xc212, 0xbd24, 0xc84b, 0xe419, 0xe8c1, 0x104f, -0x440c, 0x4662, 0x1122, 0x20a6, 0x2d8e, 0xda8b, 0xbcf3, 0xc089, 0xcbf5, 0xde1c, -0xf3c0, 0x2135, 0x414e, 0x3fd9, 0x15e1, 0x26f5, 0x292e, 0xdc7e, 0xb937, 0xb924, -0xd769, 0xe06d, 0xee92, 0x2aa0, 0x48ae, 0x379d, 0x15e3, 0x2afa, 0x167d, 0xd370, -0xbfeb, 0xb8aa, 0xd8da, 0xe695, 0xf331, 0x2abe, 0x4918, 0x2e1c, 0x0f19, 0x32cf, -0x0f46, 0xc822, 0xbf6b, 0xbca2, 0xdadc, 0xe628, 0xf835, 0x2cf1, 0x4b03, 0x2c10, -0x0df9, 0x358e, 0x0a2c, 0xc678, 0xc06b, 0xc05f, 0xd849, 0xe170, 0x02e1, 0x3324, -0x4bd0, 0x2fc8, 0x13c8, 0x3314, 0x0029, 0xbf2e, 0xb455, 0xc1d3, 0xe027, 0xe480, -0x0a7f, 0x2c7a, 0x5506, 0x634d, 0x2129, 0xfadf, 0xe1b5, 0xb620, 0x8bd2, 0xbfae, -0x0a2a, 0x0d5d, 0x5240, 0x8109, 0x4e83, 0x15cf, 0xf367, 0xba2c, 0x9d5f, 0xcd2e, -0xbdc8, 0xf85d, 0x6f35, 0x4484, 0x29e8, 0x54f0, 0x2a04, 0xcc06, 0xd6d5, 0xe253, -0xc387, 0x09da, 0xff4a, 0xebc6, 0x4eb3, 0x2edb, 0xcfcc, 0x0b9a, 0x3d1b, 0xdc1f, -0xf4a1, 0x3fae, 0xeb9b, 0xe915, 0x13be, 0xcb86, 0xb940, 0x1643, 0x18ee, 0xf7d9, -0x4acf, 0x30df, 0xe6dc, 0x16fe, 0xf4d5, 0x9c55, 0xcc79, 0x10aa, 0xe384, 0x0496, -0x4c2c, 0x0504, 0xff4c, 0x2d7e, 0xd93d, 0xacb4, 0x00e4, 0xf5af, 0xc18e, 0x2289, -0x2a64, 0xe0a9, 0x2089, 0x21b8, 0xbaf3, 0xdd89, 0x1ea4, 0xd2e4, 0xe648, 0x37ea, -0xec35, 0xe993, 0x35d6, 0xed2e, 0xbadd, 0x15b4, 0x0a9d, 0xc841, 0x1a7c, 0x191c, -0xcaef, 0x0f4e, 0x1fff, 0xbfae, 0xe155, 0x3460, 0xeb55, 0xe0b6, 0x3048, 0xf2f2, -0xddca, 0x235a, 0xee7f, 0xb7cc, 0x15dd, 0x2097, 0xce52, 0x180f, 0x2dcf, 0xd499, -0x0857, 0x29b8, 0xc4c2, 0xd000, 0x28ff, 0xe652, 0xde94, 0x43c5, 0x05e6, 0xe425, -0x356a, 0xfee7, 0xb2c1, 0x0914, 0x1584, 0xbe7b, 0x0e5a, 0x368a, 0xe276, 0x1274, -0x3391, 0xd008, 0xd357, 0x26c2, 0xec06, 0xdd61, 0x39e2, 0x0868, 0xe6f9, 0x3207, -0x091c, 0xbc0e, 0x0582, 0x1f6d, 0xd133, 0x1cab, 0x4d57, 0xeb81, 0x02e3, 0x3712, -0xde41, 0xc71f, 0x212b, 0xfb67, 0xe7a1, 0x504a, 0x23ac, 0xedd8, 0x3eb6, 0x157e, -0xad36, 0xfc30, 0x2995, 0xcb86, 0x10c5, 0x529b, 0xf7a7, 0x15fe, 0x4546, 0xdc21, -0xcaba, 0x26eb, 0xe984, 0xd493, 0x4c8e, 0x16e6, 0xe56c, 0x463b, 0x1cf9, 0xb4a9, -0xfd71, 0x24ab, 0xc8a4, 0x09c8, 0x41c1, 0xe4ff, 0x0a9c, 0x3d28, 0xd226, 0xc16f, -0x2d0a, 0xf732, 0xcb2f, 0x42b2, 0x1d8f, 0xd5f3, 0x2c26, 0x1649, 0xa7c7, 0xed21, -0x286f, 0xc688, 0x04e2, 0x51dd, 0xe73b, 0x0013, 0x46f0, 0xd1e8, 0xab29, 0x260f, -0xf7aa, 0xbdee, 0x402d, 0x25c0, 0xda8b, 0x3989, 0x21df, 0xa95c, 0xf0ed, 0x290b, -0xbd3d, 0xfb44, 0x4eba, 0xe63a, 0xfecd, 0x4a1d, 0xe177, 0xbc6f, 0x2af3, 0xfdf2, -0xc4cf, 0x3b3d, 0x1ebb, 0xd4a0, 0x319d, 0x221e, 0xaf94, 0xf1b0, 0x3313, 0xcaf9, -0xf7f2, 0x4fd1, 0xeb5b, 0xf54e, 0x3faa, 0xd925, 0xb016, 0x2624, 0x012e, 0xc2f3, -0x3e16, 0x255d, 0xce61, 0x2b67, 0x1e8d, 0xa020, 0xe075, 0x2e2f, 0xc930, 0xf3f3, -0x4f96, 0xe6da, 0xef8a, 0x43fb, 0xdd85, 0xab7b, 0x2022, 0xfe5f, 0xba39, 0x346d, -0x27b4, 0xd20a, 0x2bfd, 0x2882, 0xae6f, 0xe3d3, 0x2b38, 0xc9c6, 0xef8e, 0x49d8, -0xeade, 0xf2c2, 0x46de, 0xe8ca, 0xb2fe, 0x208e, 0x0855, 0xc5c4, 0x338e, 0x29eb, -0xd645, 0x257e, 0x260c, 0xb7e2, 0xebef, 0x3365, 0xd6d8, 0xf811, 0x518d, 0xf457, -0xf134, 0x4558, 0xf41a, 0xb66e, 0x1a28, 0x0bd1, 0xcaa3, 0x31f9, 0x3045, 0xe17a, -0x2ad4, 0x2f2d, 0xbd2b, 0xe13e, 0x2cfe, 0xd3bd, 0xea52, 0x4fb4, 0x020e, 0xf40a, -0x4278, 0xf8f7, 0xb45e, 0x127c, 0x0af1, 0xc2d1, 0x2b03, 0x36f1, 0xdd10, 0x20d2, -0x35ec, 0xc074, 0xd7bd, 0x2eef, 0xd9db, 0xe543, 0x52c9, 0x05cf, 0xec53, 0x41bb, -0xfa65, 0xaf2a, 0x15c6, 0x1274, 0xbe0e, 0x2c97, 0x448e, 0xdd59, 0x1c74, 0x3817, -0xbb2b, 0xcf5b, 0x2f9a, 0xdc0c, 0xe4f0, 0x586d, 0x096c, 0xe913, 0x42c2, 0xf921, -0xa4d2, 0x0f94, 0x157d, 0xbee7, 0x2a97, 0x46c2, 0xe0bf, 0x1d38, 0x3786, 0xb9af, -0xcb2a, 0x2dd9, 0xd952, 0xdcbe, 0x5596, 0x0bab, 0xea4b, 0x4531, 0xffe2, 0xa78b, -0x0c61, 0x136f, 0xb532, 0x2148, 0x44ae, 0xd96e, 0x195c, 0x392c, 0xb898, 0xc8e9, -0x3388, 0xef92, 0xcd85, 0x0ef6, 0xf796, 0x13e1, 0x506a, 0x028d, 0xcd42, 0x14f5, -0xf58e, 0x9d81, 0xe29f, 0xfb3c, 0xcd66, 0x1bbf, 0x6302, 0x30d7, 0x14e4, 0x29bc, -0xdf14, 0xae5e, 0xce80, 0xabdc, 0xca7e, 0x3503, 0x1ee4, 0xf688, 0x5aac, 0x62a7, -0xd87d, 0xd4d5, 0xfc76, 0xb6dd, 0xa5b5, 0xf6e7, 0x0b99, 0x0665, 0x381b, 0x1f19, -0x1206, 0x2ed5, 0xebdd, 0xb56d, 0xde2e, 0xf87e, 0xce4d, 0xed47, 0x2439, 0x121c, -0x1266, 0x1483, 0x16c8, 0x10fd, 0x0650, 0xd754, 0xbaff, 0xf7e2, 0xda39, 0xcd45, -0x2986, 0x40dc, 0x0e53, 0x182a, 0x50a1, 0x01de, 0xcb6e, 0xdc62, 0xb781, 0xcfc4, -0xee54, 0xf542, 0x2240, 0x5981, 0x2c5e, 0xf8e5, 0x3e3b, 0x0d29, 0xaf98, 0xc3b7, -0xd787, 0xd3b1, 0xe2e2, 0x19c0, 0x28df, 0x3943, 0x2fe3, 0x081e, 0x2922, 0x0f78, -0xc962, 0xb6cc, 0xd979, 0xe6a6, 0xd121, 0x07cf, 0x3500, 0x3ad0, 0x2c2a, 0x2624, -0x2c95, 0xfb6f, 0xd7be, 0xb61f, 0xc05e, 0xe300, 0xd931, 0x0c0c, 0x4eae, 0x4878, -0x15a7, 0x2a43, 0x31b1, 0xdd24, 0xc4ea, 0xbda7, 0xc680, 0xe6be, 0xf0c6, 0x13f8, -0x45f0, 0x4cdc, 0x09ee, 0x185b, 0x3039, 0xdd40, 0xc562, 0xd19d, 0xd51e, 0xdcc7, -0xfbf1, 0x1f9a, 0x3311, 0x4108, 0x10a0, 0x1d6d, 0x368a, 0xebc0, 0xbacd, 0xc61c, -0xe6be, 0xd8c6, 0xecc7, 0x2b4f, 0x3ad6, 0x3234, 0x16aa, 0x312b, 0x2c20, 0xe5cf, -0xbf0c, 0xc2d3, 0xef02, 0xde96, 0xe42b, 0x3264, 0x4f6e, 0x2b87, 0x1502, 0x434e, -0x2169, 0xd824, 0xcd4a, 0xcbe5, 0xe67d, 0xe8bc, 0xf5fe, 0x2bd2, 0x4fbf, 0x2b2a, -0x05aa, 0x4482, 0x2857, 0xcb20, 0xc28b, 0xdaa2, 0xe5a8, 0xd616, 0xfaa0, 0x2fc3, -0x4640, 0x2ce5, 0x0f5a, 0x3ab7, 0x1944, 0xcda3, 0xbbcb, 0xcda1, 0xe22a, 0xd491, -0x0143, 0x3873, 0x3fbe, 0x22bb, 0x1736, 0x3ae6, 0x0407, 0xc3a8, 0xb9c4, 0xc437, -0xdc67, 0xd7ba, 0x00f1, 0x3801, 0x41a0, 0x1951, 0x11c6, 0x35de, 0xf7fb, 0xbeac, -0xb811, 0xc658, 0xdc21, 0xd8e4, 0x060e, 0x39e5, 0x4146, 0x14d2, 0x16dd, 0x36f4, -0xed6d, 0xba86, 0xb9b4, 0xcb20, 0xdd7e, 0xdfe1, 0x0f15, 0x3b82, 0x3e9b, 0x1158, -0x1b8a, 0x323f, 0xe8e2, 0xbdfb, 0xb835, 0xd09f, 0xddf2, 0xdd36, 0x148a, 0x40d3, -0x3e5f, 0x13a7, 0x2794, 0x2f4a, 0xde17, 0xbd25, 0xb6e3, 0xcb99, 0xdc56, 0xe6b9, -0x1ce9, 0x4836, 0x40a4, 0x1189, 0x2c70, 0x2966, 0xd436, 0xb74c, 0xb722, 0xd074, -0xdc0f, 0xf37b, 0x2f67, 0x4c6c, 0x3aa1, 0x13df, 0x2e82, 0x1863, 0xcd4e, 0xbc3f, -0xbce0, 0xd9c4, 0xe1be, 0xfb8d, 0x359d, 0x4c01, 0x3625, 0x17b8, 0x32ce, 0x1032, -0xcc89, 0xbc30, 0xbdba, 0xe2ec, 0xe81c, 0x005e, 0x3db9, 0x512c, 0x2c97, 0x17fc, -0x380c, 0x06f5, 0xcdb1, 0xc0d4, 0xbe6b, 0xe241, 0xead9, 0x05a4, 0x3a27, 0x518a, -0x2c10, 0x187d, 0x3809, 0xfd0c, 0xc4ac, 0xbff0, 0xc939, 0xe3de, 0xe907, 0x0ded, -0x3e36, 0x4e75, 0x2456, 0x1759, 0x31b3, 0xf547, 0xc449, 0xba76, 0xcb7c, 0xe629, -0xeb17, 0x17cc, 0x43ae, 0x4970, 0x1dfb, 0x1c5c, 0x2c1b, 0xe97c, 0xc40d, 0xbedd, -0xd492, 0xe90c, 0xea14, 0x18aa, 0x44ec, 0x43bf, 0x15aa, 0x2421, 0x2f68, 0xe453, -0xc2c4, 0xbef4, 0xd4c2, 0xe5c8, 0xf22d, 0x2430, 0x475e, 0x4461, 0x187f, 0x2916, -0x2d8a, 0xe382, 0xc1ee, 0xc2fa, 0xe144, 0xe6ea, 0xf400, 0x30a7, 0x50eb, 0x4265, -0x1b39, 0x2f67, 0x2260, 0xde78, 0xc61b, 0xc3e2, 0xe4c5, 0xea6e, 0xf9b7, 0x32e4, -0x4b24, 0x330a, 0x1529, 0x377b, 0x1ad4, 0xd298, 0xc471, 0xc39e, 0xdfcf, 0xe493, -0xf83e, 0x339f, 0x5169, 0x3178, 0x0fe4, 0x3634, 0x10e7, 0xcad8, 0xc188, 0xc0cb, -0xd97a, 0xe36b, 0x01cf, 0x3255, 0x49ca, 0x2dc9, 0x1019, 0x33a3, 0x059a, 0xbd42, -0xb33f, 0xbe30, 0xd9b3, 0xdc31, 0x0338, 0x37ee, 0x44ae, 0x224f, 0x0cf2, 0x27ab, -0xf5dc, 0xbd67, 0xaf02, 0xbe02, 0xde1c, 0xd738, 0x02b2, 0x3eba, 0x4074, 0x174f, -0x1829, 0x2c82, 0xe72a, 0xbf61, 0xb6b3, 0xbe85, 0xe2b1, 0xe25f, 0x05c1, 0x4181, -0x4a2b, 0x1512, 0x1a96, 0x326a, 0xe3c7, 0xbc8c, 0xb7af, 0xbd6f, 0xd849, 0xe90a, -0x150e, 0x3ed0, 0x469e, 0x176f, 0x1b23, 0x2908, 0xdb26, 0xb2f3, 0xb1f8, 0xca6a, -0xdb06, 0xe6a5, 0x1fb9, 0x4383, 0x3e5d, 0x1596, 0x1e94, 0x1cb0, 0xd8cf, 0xba95, -0xaec4, 0xcef4, 0xe43f, 0xeaf7, 0x2443, 0x4766, 0x336a, 0x0e32, 0x27a3, 0x1356, -0xcd5d, 0xc150, 0xb6ba, 0xd1d8, 0xe528, 0xee84, 0x2426, 0x4adf, 0x3667, 0x10cd, -0x310e, 0x1388, 0xcbbb, 0xc47e, 0xbd1f, 0xd491, 0xe98e, 0x0244, 0x3289, 0x53b3, -0x3eb9, 0x1566, 0x3290, 0x11e3, 0xcc07, 0xc004, 0xc4fd, 0xe223, 0xed0a, 0x10ae, -0x3ff0, 0x5037, 0x36e6, 0x164f, 0x29a1, 0x0284, 0xcdb8, 0xbdcd, 0xc637, 0xed96, -0xeeb1, 0x0cd0, 0x4050, 0x49bb, 0x239a, 0x1780, 0x2eab, 0xf6b6, 0xce3d, 0xbc38, -0xc8a0, 0x00e6, 0xefad, 0x0021, 0x33f9, 0x3ae3, 0x3cd3, 0x3fb1, 0x1838, 0xd924, -0xd541, 0xaecf, 0xa5de, 0xf7be, 0xfc4c, 0x140b, 0x790a, 0x7937, 0x1616, 0xfecc, -0xe9f4, 0x93de, 0xa330, 0xc0f0, 0xcf7e, 0x2cbc, 0x6286, 0x4ccf, 0x462d, 0x450f, -0xe74f, 0xa99c, 0xd6b1, 0xc669, 0xba55, 0xfefe, 0x25a9, 0x3149, 0x43ec, 0x1f26, -0xede2, 0x0adf, 0xff6f, 0xbdad, 0xf121, 0x2c23, 0xf18d, 0xf0cc, 0x2718, 0xe6c1, -0xbbba, 0x1d92, 0x14c7, 0xde85, 0x3e57, 0x31f4, 0xe295, 0x0f8d, 0xf992, 0xa267, -0xd117, 0x1bec, 0xe179, 0x07c9, 0x5af9, 0x03a7, 0x02e9, 0x3014, 0xd04d, 0xa794, -0xfb7e, 0xf282, 0xc715, 0x36b9, 0x3764, 0xea25, 0x307a, 0x1cb0, 0xb2ec, 0xd406, -0x117c, 0xd2a9, 0xea54, 0x43e9, 0xfc21, 0xfaa3, 0x3fc1, 0xeafd, 0xb060, 0x0b4c, -0x0ff9, 0xc461, 0x1954, 0x3040, 0xd984, 0x1601, 0x2825, 0xbc3f, 0xdbce, 0x3875, -0xe248, 0xdd2e, 0x4e02, 0xfd2c, 0xd91c, 0x3749, 0xf658, 0xaa69, 0x13e1, 0x2498, -0xcc14, 0x21ba, 0x3a26, 0xdb15, 0x1623, 0x2c3d, 0xba40, 0xcd2e, 0x2cf5, 0xe4ae, -0xe186, 0x52e0, 0x0d31, 0xe5cf, 0x3778, 0xffc1, 0xac3d, 0xfddf, 0x1bac, 0xc8ec, -0x14d4, 0x4029, 0xe79d, 0x15d6, 0x3792, 0xcc34, 0xca86, 0x3225, 0xf50a, 0xcf32, -0x3e28, 0x12b9, 0xe181, 0x364b, 0x1002, 0xb4c9, 0x031c, 0x26d6, 0xd0a9, 0x1e6a, -0x443a, 0xd350, 0x08d3, 0x41ed, 0xc7a2, 0xb99c, 0x2fe3, 0xff21, 0xdc75, 0x4839, -0x19bb, 0xe84c, 0x32f7, 0x01b7, 0xaddf, 0xfef1, 0x1e1c, 0xcae8, 0x16e4, 0x4bda, -0xecbb, 0x0caf, 0x3f24, 0xe0dc, 0xc4bc, 0x18da, 0xf67e, 0xdd77, 0x3908, 0x10f4, -0xed5b, 0x4369, 0x1b8d, 0xb980, 0x0190, 0x297a, 0xc6e3, 0x009e, 0x4462, 0xea33, -0x0155, 0x3cba, 0xe27f, 0xcbef, 0x2bda, 0xf9a0, 0xd4bc, 0x439f, 0x150b, 0xd559, -0x344d, 0x1a5b, 0xaf57, 0xf935, 0x2de0, 0xcf9a, 0x0979, 0x49a9, 0xea95, 0x051a, -0x3cdb, 0xd6df, 0xbc25, 0x2174, 0xf28b, 0xc89b, 0x4222, 0x2645, 0xdfc1, 0x315e, -0x201e, 0xb2fc, 0xe3d4, 0x181b, 0xc442, 0xfd80, 0x4a43, 0xed31, 0xff1f, 0x4381, -0xdf06, 0xb4dc, 0x1fd7, 0xf6dc, 0xbffe, 0x3a30, 0x24f3, 0xd6d6, 0x297b, 0x1c5d, -0xaf37, 0xedb8, 0x286b, 0xc61d, 0xfe95, 0x515b, 0xe559, 0xf3c9, 0x4090, 0xd830, -0xae85, 0x1db8, 0xf7ec, 0xc607, 0x3f9f, 0x264d, 0xd94a, 0x2a8a, 0x1485, 0xa002, -0xdeaf, 0x235b, 0xc381, 0xfa49, 0x5796, 0xefa6, 0xf6b9, 0x4056, 0xd904, 0xa813, -0x17bf, 0xf931, 0xbf26, 0x3e31, 0x2c1d, 0xd50a, 0x2d55, 0x22b9, 0xa846, 0xdf9f, -0x28c5, 0xc598, 0xef6b, 0x51e5, 0xf17d, 0xfa6e, 0x46ce, 0xddcb, 0xac63, 0x1bcf, -0xff42, 0xc08e, 0x3b8a, 0x30ab, 0xd540, 0x2b2a, 0x2b14, 0xb01e, 0xe003, 0x2eb4, -0xd207, 0xf705, 0x5a04, 0xf5f1, 0xf4f2, 0x49c1, 0xe735, 0xac7d, 0x1ff2, 0x0bc1, -0xc150, 0x3a15, 0x3cd0, 0xdc44, 0x29ee, 0x3079, 0xb504, 0xdcbb, 0x2cd7, 0xcfca, -0xeed8, 0x5c84, 0xfe19, 0xf346, 0x4ec0, 0xf24e, 0xa9a2, 0x1b01, 0x120e, 0xbf61, -0x31bd, 0x3ed0, 0xdca8, 0x27d7, 0x3553, 0xb79f, 0xddc0, 0x376a, 0xd743, 0xea45, -0x592a, 0xf9f4, 0xe94c, 0x4a1d, 0xf552, 0xa9a1, 0x19d2, 0x14c6, 0xbc9b, 0x2b11, -0x3913, 0xd2dc, 0x1f02, 0x340a, 0xb6b6, 0xd426, 0x33f8, 0xd9a8, 0xdc7f, 0x5123, -0xfd3a, 0xde46, 0x40ce, 0xfa10, 0xa997, 0x14a8, 0x1bb4, 0xc08d, 0x2376, 0x3d24, -0xd503, 0x166b, 0x35f1, 0xbc86, 0xd0fa, 0x36e8, 0xf1a1, 0xd628, 0x16ed, 0xf145, -0x0d1a, 0x55e9, 0x09b3, 0xd0ea, 0x189e, 0xfae5, 0xa1d9, 0xe581, 0xfd1f, 0xd241, -0x1ffa, 0x5b9f, 0x2d05, 0x1f94, 0x316b, 0xe330, 0xba22, 0xd9ab, 0xb354, 0xcfaa, -0x3059, 0x1e34, 0xfcfc, 0x565e, 0x623a, 0xe738, 0xe027, 0xfe57, 0xc099, 0xb327, -0xf470, 0x0c3c, 0x0e38, 0x38fd, 0x1a11, 0x0892, 0x3339, 0xf5c3, 0xbab1, 0xe297, -0xf843, 0xce95, 0xe620, 0x1c2a, 0x09ee, 0x1283, 0x1b02, 0x1187, 0x188b, 0x0e86, -0xd87e, 0xb652, 0xeca9, 0xdb2d, 0xc7dd, 0x1fe5, 0x379e, 0x0fd2, 0x1c45, 0x4c2e, -0x1316, 0xd85d, 0xdfc8, 0xb6df, 0xc87f, 0xeb35, 0xe54a, 0x1628, 0x57e4, 0x3419, -0xff6c, 0x4156, 0x1c74, 0xb5b4, 0xca06, 0xd49b, 0xc6db, 0xdff3, 0x0f42, 0x1c9b, -0x38c2, 0x3cc3, 0x059b, 0x2a27, 0x2699, 0xce85, 0xb4f9, 0xd393, 0xdea9, 0xcdb3, -0x03e7, 0x3281, 0x3d54, 0x3c61, 0x2583, 0x2ce2, 0x0c86, 0xdb4d, 0xb3bf, 0xbb9c, -0xe353, 0xd3fa, 0x034f, 0x4cdb, 0x4ab4, 0x21fa, 0x2b59, 0x391f, 0xeb27, 0xc6cb, -0xb8e4, 0xba9a, 0xe811, 0xec0e, 0x0c31, 0x4bdb, 0x5772, 0x163d, 0x1a19, 0x3f4d, -0xe9e8, 0xc3a2, 0xce2a, 0xc788, 0xd9f4, 0xf83e, 0x1bc6, 0x3ce8, 0x5548, 0x1db0, -0x1a55, 0x4521, 0xf1e2, 0xb3ab, 0xc2fd, 0xdbe1, 0xd800, 0xed52, 0x2c30, 0x402d, -0x430b, 0x23ff, 0x260b, 0x2f76, 0xea68, 0xb3d7, 0xb28e, 0xe14a, 0xdd06, 0xe066, -0x33b3, 0x51ec, 0x33b4, 0x1667, 0x3052, 0x1b77, 0xd41e, 0xc1bd, 0xb6c4, 0xd7a4, -0xeae0, 0xe9c9, 0x2119, 0x50ee, 0x3348, 0x0504, 0x35a7, 0x2224, 0xc555, 0xbad4, -0xc362, 0xd0c4, 0xdade, 0xf6c3, 0x247a, 0x463c, 0x3726, 0x098e, 0x2ae9, 0x1a2f, -0xc9d7, 0xb0fe, 0xc012, 0xdaac, 0xd5e1, 0xf9b0, 0x351f, 0x47da, 0x2cc6, 0x10fb, -0x2d9f, 0x068e, 0xc5e3, 0xb7fe, 0xc297, 0xe2b0, 0xe422, 0x01db, 0x36ef, 0x48cb, -0x23b7, 0x10de, 0x3507, 0x0307, 0xc7bf, 0xbff3, 0xc77c, 0xe128, 0xe56e, 0x0857, -0x3924, 0x4aaf, 0x2249, 0x103e, 0x31eb, 0xfa7b, 0xc42a, 0xbff2, 0xca69, 0xdf50, -0xe715, 0x1070, 0x37d6, 0x4303, 0x1eac, 0x16ba, 0x3184, 0xf739, 0xc4f5, 0xba46, -0xcd7e, 0xe4d0, 0xe592, 0x10ee, 0x3d09, 0x4044, 0x17ce, 0x2016, 0x2f3f, 0xe9e5, -0xc633, 0xbad5, 0xc6f9, 0xdeac, 0xe624, 0x121c, 0x413b, 0x4301, 0x1783, 0x29fd, -0x2ba5, 0xdaa4, 0xbe2e, 0xbb8e, 0xcbc8, 0xdde4, 0xf1e9, 0x21db, 0x439b, 0x4190, -0x172e, 0x27a0, 0x21f8, 0xd636, 0xbad8, 0xb8bf, 0xd096, 0xdf64, 0xf8a8, 0x2b93, -0x443b, 0x3b96, 0x185e, 0x26da, 0x10a6, 0xd167, 0xbdb5, 0xb977, 0xdb3a, 0xe708, -0xfa86, 0x2f52, 0x4806, 0x3265, 0x17a3, 0x31bc, 0x0e05, 0xd510, 0xc461, 0xbd66, -0xe192, 0xed77, 0x0119, 0x32ba, 0x50ed, 0x31ea, 0x16d3, 0x36ec, 0x0732, 0xcfc7, -0xc460, 0xc184, 0xdf83, 0xea58, 0x0626, 0x3550, 0x5246, 0x2def, 0x16cd, 0x3427, -0xfd3a, 0xc953, 0xbd3c, 0xc544, 0xe4ec, 0xf153, 0x166b, 0x3e10, 0x4e6d, 0x26d4, -0x1968, 0x2f16, 0xf428, 0xcae0, 0xbedd, 0xccf8, 0xe956, 0xefa2, 0x1950, 0x4297, -0x4a75, 0x1de5, 0x1955, 0x29ba, 0xeb91, 0xc61c, 0xbb12, 0xce33, 0xe7ce, 0xefe6, -0x18c6, 0x3e32, 0x43c5, 0x1922, 0x1cc3, 0x20fd, 0xde2b, 0xbf55, 0xb70b, 0xd16f, -0xe788, 0xef8e, 0x20c4, 0x4654, 0x3d93, 0x11d7, 0x1d2e, 0x15c5, 0xd810, 0xc122, -0xb7df, 0xd5bd, 0xe934, 0xf5a0, 0x24cd, 0x4321, 0x356b, 0x10be, 0x2522, 0x0e25, -0xcdbf, 0xc120, 0xbd1c, 0xd9ef, 0xe950, 0xf8ec, 0x2940, 0x4a04, 0x31e9, 0x09bb, -0x2935, 0x0de2, 0xcd32, 0xc3e0, 0xc0ad, 0xd96f, 0xebee, 0x0617, 0x2f65, 0x4a9d, -0x32b8, 0x104d, 0x2b20, 0x042c, 0xc6fe, 0xc0fc, 0xccb1, 0xe4c6, 0xe7e5, 0x0a2f, -0x3646, 0x44a9, 0x27ed, 0x0f84, 0x2482, 0xf9ba, 0xc9c9, 0xbc46, 0xc6f1, 0xe7c9, -0xe7b4, 0x0935, 0x38ab, 0x3dfb, 0x17f4, 0x1202, 0x2366, 0xe8cf, 0xc894, 0xc02b, -0xc4e7, 0xe682, 0xe9b4, 0x07dc, 0x39bc, 0x4323, 0x1255, 0x11a3, 0x2383, 0xde38, -0xc201, 0xc28c, 0xc797, 0xe30e, 0xf319, 0x11aa, 0x3329, 0x3e07, 0x11d9, 0x1242, -0x2173, 0xdd6c, 0xbc12, 0xbfc4, 0xd3cc, 0xe301, 0xf14f, 0x1ed7, 0x3c0b, 0x3bbe, -0x150a, 0x16d9, 0x1504, 0xded6, 0xc844, 0xc0c3, 0xdedd, 0xf0d7, 0xf666, 0x25d8, -0x4424, 0x33d0, 0x130e, 0x2876, 0x1370, 0xd7a6, 0xcf17, 0xc604, 0xdfb9, 0xf3d2, -0xfde9, 0x29bc, 0x4a3a, 0x328f, 0x0b59, 0x293f, 0x0f84, 0xd398, 0xd04b, 0xc938, -0xdc96, 0xf077, 0x05ff, 0x2a1d, 0x46e9, 0x33e3, 0x0e06, 0x27af, 0x084b, 0xcbee, -0xc5ed, 0xce50, 0xe4ec, 0xee95, 0x0fc4, 0x3481, 0x4291, 0x2ab8, 0x0ee7, 0x217a, -0xfbca, 0xd072, 0xc584, 0xcdc2, 0xeeae, 0xf284, 0x0beb, 0x2a91, 0x499c, 0x4ab9, -0x1751, 0xfb4e, 0xdff4, 0xc548, 0xaa11, 0xcb25, 0x079b, 0x0c5f, 0x3e98, 0x6247, -0x3e07, 0x0f43, 0xf0e4, 0xca5f, 0xb34f, 0xd401, 0xc893, 0xf3d1, 0x4c26, 0x308b, -0x22b3, 0x41c0, 0x2279, 0xdc8b, 0xdff8, 0xe310, 0xcceb, 0x027f, 0xf615, 0xee06, -0x3db0, 0x2392, 0xe01d, 0x1366, 0x3415, 0xe34c, 0xf6fc, 0x29c3, 0xe8c4, 0xee7a, -0x0fa2, 0xddee, 0xd84b, 0x1d1b, 0x1722, 0xff58, 0x3ac2, 0x1f74, 0xf03f, 0x1870, -0xfcd6, 0xbff1, 0xe68f, 0x1180, 0xed1f, 0x0a12, 0x3895, 0x07ec, 0x0a31, 0x24b3, -0xe648, 0xc9fc, 0x0734, 0xf757, 0xd412, 0x1f4f, 0x1e8b, 0xee37, 0x1f0e, 0x1be3, -0xd43a, 0xefa3, 0x194b, 0xddce, 0xf237, 0x287d, 0xeedf, 0xf88e, 0x2f7f, 0xf704, -0xd874, 0x1c88, 0x0972, 0xd8d5, 0x1702, 0x0f28, 0xe05e, 0x15f4, 0x1a7c, 0xd9ff, -0xf6eb, 0x29b0, 0xf002, 0xf068, 0x23d5, 0xf4b8, 0xf046, 0x1d23, 0xf157, 0xd110, -0x1519, 0x14cc, 0xdede, 0x142d, 0x1a12, 0xe515, 0x0b1c, 0x1636, 0xd4d5, 0xe629, -0x1cf8, 0xe7c6, 0xea58, 0x2b1f, 0xfc3e, 0xeeea, 0x236a, 0xfb53, 0xcfca, 0x089d, -0x0488, 0xd160, 0x0a55, 0x141d, 0xe5cd, 0x107d, 0x186d, 0xd8c8, 0xeaf7, 0x1870, -0xe826, 0xe955, 0x18d7, 0xf4b0, 0xf2f4, 0x198f, 0xf637, 0xd898, 0x0a60, 0x099d, -0xe121, 0x112c, 0x19f0, 0xe9f0, 0x0257, 0x16f3, 0xea68, 0xe825, 0x11db, 0xf73d, -0xf784, 0x22c2, 0x01a7, 0xfa94, 0x2329, 0xff98, 0xd3ac, 0x066b, 0x0f20, 0xe092, -0x0cc3, 0x1ddc, 0xf6f5, 0x1320, 0x1de5, 0xe877, 0xf111, 0x167b, 0xeabe, 0xf59b, -0x2682, 0xf99e, 0xf763, 0x2689, 0x0359, 0xdfbd, 0x0e98, 0x0da2, 0xe78d, 0x0e20, -0x0ddb, 0xeff0, 0x11e2, 0x1312, 0xe459, 0xf4b0, 0x1704, 0xee92, 0xf4ab, 0x1ec2, -0xfc20, 0xf450, 0x169f, 0xfcad, 0xdbac, 0x0468, 0x089e, 0xe794, 0x0e53, 0x118e, -0xefe9, 0x0e4a, 0x169a, 0xe1da, 0xebc0, 0x15eb, 0xefcb, 0xf2ee, 0x1beb, 0xfce8, -0xfa9c, 0x1c17, 0xffdb, 0xe6b1, 0x0fc5, 0x065c, 0xe6be, 0x10a6, 0x0c31, 0xefee, -0x1306, 0x13f9, 0xe94c, 0xfe45, 0x19f0, 0xf32a, 0xfe85, 0x1905, 0xf62a, 0xfd6b, -0x1747, 0xf729, 0xeaea, 0x11d1, 0x0593, 0xf301, 0x1786, 0x0b5f, 0xf124, 0x0940, -0x0668, 0xe6ec, 0xfe96, 0x1506, 0xf474, 0x04f3, 0x1923, 0xf708, 0xfe00, 0x1197, -0xf0a5, 0xe784, 0x0dcb, 0xff73, 0xf084, 0x1476, 0x091e, 0xf640, 0x0af0, 0x0286, -0xe773, 0xfd6b, 0x0efa, 0xf175, 0xf89a, 0x07af, 0xf9a6, 0x0516, 0x128d, 0x00ff, -0xfb4e, 0x0ed0, 0xfc70, 0xe60e, 0xf53d, 0xf46c, 0xf494, 0x0bff, 0x1551, 0x06e1, -0x0c9f, 0x0f55, 0xefab, 0xed70, 0xf731, 0xf0fe, 0xfd13, 0x0d56, 0x068f, 0x0174, -0x0fee, 0xffe4, 0xea15, 0xfb9a, 0xffd4, 0xf166, 0xfe33, 0x0e6f, 0xfeb8, 0x0068, -0x0cad, 0xf543, 0xef47, 0x0118, 0xf5be, 0xeb4f, 0x04dc, 0x09b3, 0xf586, 0x0081, -0x05bb, 0xf123, 0xf60d, 0x03d9, 0xf8d9, 0xfe28, 0x0dac, 0xfb79, 0xf27c, 0xfe7f, -0xf2c5, 0xeeb0, 0x04d2, 0x030d, 0xfb07, 0x0e9e, 0x0d11, 0xf686, 0xfc94, 0xffbf, -0xed59, 0xf589, 0x05ab, 0xfa48, 0xffa7, 0x10c1, 0x014b, 0xfa25, 0x09af, 0xfdfc, -0xf0ef, 0x03b9, 0x0400, 0xf635, 0x063f, 0x0b0b, 0xf9ca, 0x03dd, 0x0d8d, 0xfce6, -0x00df, 0x0f4b, 0x024e, 0xff59, 0x0dc5, 0x0159, 0xf654, 0x067a, 0x0580, 0xfe5c, -0x0e74, 0x0f97, 0x0220, 0x099e, 0x09f1, 0xf6cc, 0xfa19, 0x04a0, 0xfb69, 0x0199, -0x10a2, 0x0726, 0x02f2, 0x0c0d, 0x00c5, 0xf7e4, 0x056d, 0x020c, 0xf921, 0x0880, -0x0a05, 0xfc9b, 0x05b1, 0x0ba8, 0xfd73, 0x0126, 0x0afa, 0xfedb, 0xfe8e, 0x0754, -0xff08, 0x010e, 0x0a12, 0xff2a, 0xfc7a, 0x0965, 0x0306, 0xfbf9, 0x0735, 0x05c2, -0xfdbe, 0x020b, 0x01d4, 0xfd23, 0x035d, 0x0690, 0x0006, 0x0536, 0x0b42, 0x02fd, -0x0048, 0x04d9, 0xffac, 0xfb96, 0x0522, 0x06ea, 0x01a3, 0x0640, 0x0721, 0x02e7, -0x04ad, 0x0494, 0xfe70, 0x009e, 0x0380, 0xfafb, 0xfecf, 0x0890, 0x0224, 0xffd6, -0x0613, 0x005f, 0xfada, 0x0129, 0xfdf3, 0xf949, 0x014d, 0x006d, 0xfa91, 0x0029, -0x002c, 0xf7d8, 0xff02, 0x0615, 0xfbc4, 0xfc2b, 0x035b, 0xfb89, 0xf601, 0xfa4f, -0xfabf, 0xfbd9, 0x00a3, 0xfe0a, 0xfbd4, 0x0009, 0xfe8b, 0xf866, 0xf96c, 0xfcbd, -0xf750, 0xf6d5, 0xfee5, 0xfea6, 0xfc47, 0x00a5, 0x013f, 0xfaa4, 0xf910, 0xf98a, -0xf6f0, 0xfa55, 0xfc01, 0xfac1, 0xff53, 0x001d, 0xfac1, 0xfb9d, 0xfef2, 0xf9c9, -0xf9ce, 0xff10, 0xfabf, 0xfab8, 0xfe59, 0xfa05, 0xfa90, 0x00a1, 0xff54, 0xfdbe, -0x0243, 0xff6b, 0xfac6, 0xfde2, 0xfc78, 0xf927, 0xfc12, 0xfe2f, 0xfc8a, 0xfe15, -0x00a9, 0xff55, 0x0011, 0x0049, 0xfbfb, 0xfa18, 0xfd23, 0xfbc7, 0xf7ac, 0xfd25, -0x019e, 0x0030, 0x02a9, 0x0344, 0x0053, 0x0036, 0x003f, 0xfc42, 0xfd77, 0x047f, -0x022f, 0xffd5, 0x0747, 0x065b, 0xffcd, 0x058c, 0x0801, 0x00b8, 0x029f, 0x057c, -0x0228, 0x02a9, 0x03ae, 0x0336, 0x052e, 0x06d3, 0x0355, 0x0367, 0x097b, 0x0767, -0x040b, 0x0708, 0x062b, 0x00f3, 0x010e, 0x0624, 0x0661, 0x0641, 0x0870, 0x08cf, -0x095c, 0x0672, 0x0114, 0x0064, 0x0450, 0x0372, 0x00f0, 0x074e, 0x0927, 0x0375, -0x04d0, 0x06fa, 0x0278, 0x00e0, 0x023e, 0x0105, 0x0392, 0x0478, 0x01dc, 0x0633, -0x06ad, 0x0137, 0x035b, 0x071e, 0x039a, 0x00b9, 0x0238, 0x0257, 0x03b3, 0x032c, -0x00b5, 0x062b, 0x0920, 0x0425, 0x05d7, 0x09b0, 0x0457, 0x011e, 0x02cb, 0x0112, -0x01e7, 0x03e0, 0x04a0, 0x09f0, 0x0993, 0x025d, 0x03ab, 0x06f0, 0x01ce, 0xffa9, -0x0343, 0x03bf, 0x02bc, 0x0192, 0x0175, 0x03f8, 0x025f, 0xfe13, 0xffaf, 0x037b, -0x017f, 0xff0d, 0x010c, 0x01cc, 0xff47, 0xfda7, 0xff79, 0xffc8, 0xfd08, 0xfdcd, -0x01e5, 0x03c0, 0x00fa, 0xfdf8, 0xfdb9, 0xff87, 0xff9b, 0xfbe2, 0xfeae, 0x0380, -0x0014, 0xfe6f, 0x0115, 0x0048, 0xfe1d, 0xff3b, 0xff7b, 0xff0a, 0x0012, 0xfed0, -0xfed6, 0xffe0, 0xfcbf, 0xfbdb, 0x005d, 0xffb1, 0xf9ae, 0xfb2b, 0x0004, 0xff44, -0xfc6c, 0xfb62, 0xfdf8, 0xff0d, 0xfbbb, 0xfae7, 0xfd8a, 0xfd70, 0xfaee, 0xfbe3, -0xfdf3, 0xfc89, 0xfa9a, 0xfb6e, 0xfd82, 0xfcb8, 0xfaa3, 0xfc2a, 0xfeb9, 0xfc5c, -0xf8a1, 0xfb85, 0x000c, 0xfee7, 0xfbc5, 0xfc1f, 0xfee8, 0xfdac, 0xfae0, 0xfc8b, -0xfef5, 0xfebd, 0xfdf5, 0xfec1, 0x0023, 0xff0f, 0xfc4a, 0xfdd7, 0x00f5, 0xfea6, -0xfd0c, 0x002f, 0x0087, 0xfc77, 0xfc93, 0x0024, 0x013b, 0x00dc, 0xfdb0, 0xfcbd, -0x0060, 0xfed8, 0xfc65, 0xfff5, 0x0160, 0xfef8, 0x0045, 0x0243, 0x0065, 0xfff6, -0x0124, 0x00f9, 0x00f5, 0x00ae, 0xff8e, 0x00df, 0x02e1, 0xffa3, 0xfea2, 0x0390, -0x035d, 0xff35, 0xff1f, 0x009b, 0x0048, 0xffed, 0x0005, 0xfff7, 0x0019, 0xff23, -0xfe36, 0xff91, 0x0112, 0x0088, 0xfff6, 0x00fa, 0x00a3, 0xfe18, 0xfe82, 0x01b6, -0x00ae, 0xfead, 0x018f, 0x02ef, 0x0126, 0x002b, 0xffb6, 0x01e3, 0x0382, 0x00b0, -0x00bd, 0x0402, 0x024e, 0x0002, 0x02e3, 0x0327, 0x00b6, 0x01cf, 0x02ca, 0x0250, -0x0280, 0x01bd, 0x021d, 0x04f6, 0x0340, 0xfee9, 0x0095, 0x0305, 0x00bc, 0x012c, -0x04fc, 0x0469, 0x0257, 0x027e, 0x0242, 0x01ad, 0x014b, 0x00c3, 0x0147, 0x037a, -0x036a, 0x00b5, 0x0068, 0x0121, 0xff9a, 0xfec8, 0x0143, 0x0151, 0xfd18, 0xfced, -0xffbd, 0x0066, 0x007d, 0xff3b, 0xfdd4, 0xfe9a, 0xff0d, 0xfdc1, 0xfe33, 0xffd7, -0xfd45, 0xfbe9, 0xffc6, 0xff2d, 0xfb3e, 0xfd69, 0x0057, 0xff59, 0xffc4, 0x00ad, -0x0017, 0xffc4, 0xfde4, 0xfc22, 0xfe08, 0x004b, 0xffd4, 0x0028, 0x0265, 0x00fa, -0xfdc5, 0xfe0b, 0xff0e, 0xfe78, 0xfdca, 0xfe74, 0xff9b, 0x005c, 0x0106, 0x00eb, -0x015c, 0x01a8, 0xff52, 0xfee2, 0x00ee, 0xffbe, 0xfec8, 0x010d, 0x01ab, 0x00d9, -0x0106, 0x010d, 0x00e8, 0x0118, 0x00e8, 0x012b, 0x02b4, 0x0211, 0xff7a, 0xff8e, -0x0115, 0x0100, 0x00de, 0x02dd, 0x0379, 0x00f9, 0xffd1, 0x0018, 0xfff6, 0xfffa, -0x000c, 0xfff1, 0x0004, 0x0012, 0xffc4, 0x009c, 0x010c, 0xfffb, 0xffe4, 0x001a, -0xfff2, 0xfffc, 0x0012, 0xffe2, 0x0015, 0x015a, 0x0257, 0x01c9, 0x004d, 0xfff9, -0xffdd, 0x0059, 0x019b, 0x004f, 0x0095, 0x030f, 0x0191, 0xff96, 0x015d, 0x0296, -0x01ee, 0x0205, 0x01f5, 0x0220, 0x0303, 0x0276, 0x01b6, 0x02a2, 0x02f4, 0x01f9, -0x0209, 0x01e3, 0x0073, 0x00ad, 0x0219, 0x0205, 0x00f2, 0x000a, 0xffc0, 0x00c9, -0x0105, 0xffac, 0x007c, 0x0198, 0x0051, 0xffcf, 0x001b, 0xfff4, 0x0001, 0x0002, -0xfffe, 0xfffb, 0x0005, 0xfffd, 0xfff3, 0x0025, 0xff14, 0xfe3b, 0xffa5, 0xfffc, -0xfee0, 0xff17, 0xffe8, 0x0010, 0xffec, 0x0017, 0xffe5, 0xff21, 0xfef3, 0xfef8, -0xff4b, 0x002c, 0xffc0, 0xfee9, 0xffc3, 0x001f, 0xff65, 0xffbc, 0x001d, 0xfff2, -000000, 0x0003, 0xfffc, 0xfffc, 0x000a, 0xffec, 0x0015, 0x0095, 0x002c, 0xffc9, -0x006a, 0x0078, 0xfff2, 0xfffb, 0x0006, 0xfffa, 0x0002, 0xfffd, 0x0004, 0xfff4, -0x0010, 0xfff2, 0xfff7, 0x003a, 0xfeb1, 0xfe3a, 0xfff4, 0x0020, 0xffde, 0x0015, -0xfff6, 000000, 0x0002, 0xfffc, 0x0001, 0xfffe, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, -000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000 }; diff --git a/1.4.23-rc4/channels/ring_tone.h b/1.4.23-rc4/channels/ring_tone.h deleted file mode 100644 index 559c42a7b..000000000 --- a/1.4.23-rc4/channels/ring_tone.h +++ /dev/null @@ -1,30 +0,0 @@ -/* ringtone.h: Generated from frequencies 440 and 480 - by gentone. 200 samples */ -static short ringtone[200] = { - 0, 11581, 21659, 28927, 32445, 31764, 26981, 18727, - 8084, -3559, -14693, -23875, -29927, -32083, -30088, -24228, - -15290, -4453, 6864, 17195, 25212, 29902, 30693, 27526, - 20856, 11585, 944, -9673, -18899, -25560, -28837, -28357, - -24244, -17089, -7868, 2192, 11780, 19667, 24872, 26779, - 25212, 20450, 13179, 4396, -4731, -13019, -19421, -23164, - -23839, -21446, -16384, -9384, -1408, 6484, 13281, 18145, - 20517, 20182, 17286, 12301, 5951, -887, -7314, -12519, - -15886, -17068, -16017, -12983, -8458, -3109, 2327, 7142, - 10750, 12757, 13007, 11585, 8793, 5095, 1044, -2800, - -5951, -8053, -8921, -8560, -7141, -4967, -2421, 104, - 2260, 3791, 4567, 4589, 3977, 2941, 1733, 600, - -257, -722, -772, -481, 0, 481, 772, 722, - 257, -600, -1733, -2941, -3977, -4589, -4567, -3791, - -2260, -104, 2421, 4967, 7141, 8560, 8921, 8053, - 5951, 2800, -1044, -5095, -8793, -11585, -13007, -12757, - -10750, -7142, -2327, 3109, 8458, 12983, 16017, 17068, - 15886, 12519, 7314, 887, -5951, -12301, -17286, -20182, - -20517, -18145, -13281, -6484, 1408, 9384, 16384, 21446, - 23839, 23164, 19421, 13019, 4731, -4396, -13179, -20450, - -25212, -26779, -24872, -19667, -11780, -2192, 7868, 17089, - 24244, 28357, 28837, 25560, 18899, 9673, -944, -11585, - -20856, -27526, -30693, -29902, -25212, -17195, -6864, 4453, - 15290, 24228, 30088, 32083, 29927, 23875, 14693, 3559, - -8084, -18727, -26981, -31764, -32445, -28927, -21659, -11581, - -}; |