This file is indexed.

/usr/include/opal/opal/manager.h is in libopal-dev 3.10.10~dfsg2-2.1build2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
/*
 * manager.h
 *
 * OPAL system manager.
 *
 * Open Phone Abstraction Library (OPAL)
 * Formally known as the Open H323 project.
 *
 * Copyright (c) 2001 Equivalence Pty. Ltd.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Open Phone Abstraction Library.
 *
 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
 *
 * Contributor(s): ______________________________________.
 *
 * $Revision: 27984 $
 * $Author: rjongbloed $
 * $Date: 2012-07-10 03:24:24 -0500 (Tue, 10 Jul 2012) $
 */

#ifndef OPAL_OPAL_MANAGER_H
#define OPAL_OPAL_MANAGER_H

#ifdef P_USE_PRAGMA
#pragma interface
#endif

#include <opal/buildopts.h>

#include <opal/pres_ent.h>
#include <opal/call.h>
#include <opal/connection.h> //OpalConnection::AnswerCallResponse
#include <opal/guid.h>
#include <codec/silencedetect.h>
#include <codec/echocancel.h>
#include <ptclib/pstun.h>
#include <ptclib/url.h>

#if OPAL_VIDEO
#include <ptlib/videoio.h>
#endif

class OpalEndPoint;
class OpalMediaPatch;


/**This class is the central manager for OPAL.
   The OpalManager embodies the root of the tree of objects that constitute an
   OPAL system. It contains all of the endpoints that make up the system. Other
   entities such as media streams etc are in turn contained in these objects.
   It is expected that an application would only ever have one instance of
   this class, and also descend from it to override call back functions.

   The manager is the eventual destination for call back indications from
   various other objects. It is possible, for instance, to get an indication
   of a completed call by creating a descendant of the OpalCall and overriding
   the OnClearedCall() virtual. However, this could quite unwieldy for all of
   the possible classes, so the default behaviour is to call the equivalent
   function on the OpalManager. This allows most applications to only have to
   create a descendant of the OpalManager and override virtual functions there
   to get all the indications it needs.
 */
class OpalManager : public PObject
{
    PCLASSINFO(OpalManager, PObject);
  public:
  /**@name Construction */
  //@{
    /**Create a new manager.
     */
    OpalManager();

    /**Destroy the manager.
       This will clear all calls, then delete all endpoints still attached to
       the manager.
     */
    ~OpalManager();
  //@}

  /**@name Endpoint management */
  //@{
    /**Attach a new endpoint to the manager.
       This is an internal function called by the OpalEndPoint constructor.

       Note that usually the endpoint is automatically "owned" by the manager.
       They should not be deleted directly. The DetachEndPoint() command
       should be used to do this.
      */
    void AttachEndPoint(
      OpalEndPoint * endpoint,    ///< EndPoint to add to the manager
      const PString & prefix = PString::Empty()  ///< Prefix to use, if empty uses endpoint->GetPrefixName()
    );

    /**Remove an endpoint from the manager.
       This will delete the endpoint object.
      */
    void DetachEndPoint(
      const PString & prefix
    );
    void DetachEndPoint(
      OpalEndPoint * endpoint
    );

    /**Find an endpoint instance that is using the specified prefix.
      */
    OpalEndPoint * FindEndPoint(
      const PString & prefix
    );

    /**Get the endpoints attached to this manager.
      */
    PList<OpalEndPoint> GetEndPoints() const;

    /**Shut down all of the endpoints, clearing all calls.
       This is synchonous and will wait till everything is shut down.
       This will also assure no new calls come in whilein the process of
       shutting down.
      */
    void ShutDownEndpoints();
  //@}

  /**@name Call management */
  //@{
    /**Set up a call between two parties.
       This is used to initiate a call. Incoming calls are "answered" using a
       different mechanism.

       The A party and B party strings indicate the protocol and address of
       the party to call in the style of a URL. The A party is the initiator
       of the call and the B party is the remote system being called. See the
       MakeConnection() function for more details on the format of these
       strings.

       The token returned is a unique identifier for the call that allows an
       application to gain access to the call at later time. This is necesary
       as any pointer being returned could become invalid (due to being
       deleted) at any time due to the multithreaded nature of the OPAL
       system. 
     */
    virtual PSafePtr<OpalCall> SetUpCall(
      const PString & partyA,       ///<  The A party of call
      const PString & partyB,       ///<  The B party of call
      void * userData = NULL,       ///<  user data passed to Call and Connection
      unsigned options = 0,         ///<  options passed to connection
      OpalConnection::StringOptions * stringOptions = NULL ///< Options to pass to connection
    );
    virtual PBoolean SetUpCall(
      const PString & partyA,       ///<  The A party of call
      const PString & partyB,       ///<  The B party of call
      PString & token,              ///<  Token for call
      void * userData = NULL,       ///<  user data passed to Call and Connection
      unsigned options = 0,         ///<  options passed to connection
      OpalConnection::StringOptions * stringOptions = NULL ///< Options to pass to connection
    );

    /**Determine if a call is active.
       Return true if there is an active call with the specified token. Note
       that the call could clear any time (even milliseconds) after this
       function returns true.
      */
    virtual PBoolean HasCall(
      const PString & token  ///<  Token for identifying call
    ) { return activeCalls.FindWithLock(token, PSafeReference) != NULL; }

    /**Return the number of active calls.
      */
    PINDEX GetCallCount() const { return activeCalls.GetSize(); }

    /**Get all tokens for active calls.
      */
     PArray<PString> GetAllCalls() const { return activeCalls.GetKeys(); }

    /**Find a call with the specified token.
       This searches the manager database for the call that contains the token
       as provided by functions such as SetUpCall().

       Note the caller of this function MUST call the OpalCall::Unlock()
       function if this function returns a non-NULL pointer. If it does not
       then a deadlock can occur.
      */
    PSafePtr<OpalCall> FindCallWithLock(
      const PString & token,  ///<  Token to identify connection
      PSafetyMode mode = PSafeReadWrite ///< Lock mode
    ) { return activeCalls.FindWithLock(token, mode); }

    /**A call back function whenever a call is completed.
       In telephony terminology a completed call is one where there is an
       established link between two parties.

       This called from the OpalCall::OnEstablished() function.

       The default behaviour does nothing.
      */
    virtual void OnEstablishedCall(
      OpalCall & call   ///<  Call that was completed
    );

    /**Determine if a call is established.
       Return true if there is an active call with the specified token and
       that call has at least two parties with media flowing between them.
       Note that the call could clear any time (even milliseconds) after this
       function returns true.
      */
    virtual PBoolean IsCallEstablished(
      const PString & token  ///<  Token for identifying call
    );

    /**Clear a call.
       This finds the call by using the token then calls the OpalCall::Clear()
       function on it. All connections are released, and the connections and
       call are disposed of. Note that this function returns quickly and the
       disposal happens at some later time in a background thread. It is safe
       to call this function from anywhere.

       If \p sync is not NULL then it is signalled when the calls are cleared.
      */
    virtual PBoolean ClearCall(
      const PString & token,    ///<  Token for identifying connection
      OpalConnection::CallEndReason reason = OpalConnection::EndedByLocalUser, ///<  Reason for call clearing
      PSyncPoint * sync = NULL  ///<  Sync point to wait on.
    );

    /**Clear a call.
       This finds the call by using the token then calls the OpalCall::Clear()
       function on it. All connections are released, and the connections and
       caller disposed of. Note that this function waits until the call has
       been cleared and all responses timeouts etc completed. Care must be
       used as to when it is called as deadlocks may result.
      */
    virtual PBoolean ClearCallSynchronous(
      const PString & token,    ///<  Token for identifying connection
      OpalConnection::CallEndReason reason = OpalConnection::EndedByLocalUser ///<  Reason for call clearing
    );

    /**Clear all current calls.
       This effectively executes OpalCall::Clear() on every call that the
       manager has active.
       This function can not be called from several threads at the same time.
      */
    virtual void ClearAllCalls(
      OpalConnection::CallEndReason reason = OpalConnection::EndedByLocalUser, ///<  Reason for call clearing
      PBoolean wait = true   ///<  Flag to wait for calls to e cleared.
    );

    /**A call back function whenever a call is cleared.
       A call is cleared whenever there is no longer any connections attached
       to it. This function is called just before the call is deleted.
       However, it may be used to display information on the call after
       completion, eg the call parties and duration.

       Note that there is not a one to one relationship with the
       OnEstablishedCall() function. This function may be called without that
       function being called. For example if MakeConnection() was used but
       the call never completed.

       The default behaviour removes the call from the activeCalls dictionary.
      */
    virtual void OnClearedCall(
      OpalCall & call   ///<  Connection that was established
    );

    /**Create a call object.
       This function allows an application to have the system create
       desccendants of the OpalCall class instead of instances of that class
       directly. The application can thus override call backs or add extra
       information that it wishes to maintain on a call by call basis.

       The default behavious returns an instance of OpalCall.
      */
    virtual OpalCall * CreateCall(
      void * userData            ///<  user data passed to SetUpCall
    );
    OpalCall * InternalCreateCall();

    /**Destroy a call object.
       This gets called from background thread that garbage collects all calls
       and connections. If an application has object lifetime issues with the
       threading, it can override this function and take responsibility for
       deleting the object at some later time.

       The default behaviour simply calls "delete call".
      */
    virtual void DestroyCall(
      OpalCall * call
    );

    /**Get next unique token ID for calls or connections.
       This is an internal function called by the OpalCall and other
       constructors.
      */
    virtual PString GetNextToken(char prefix);
  //@}

  /**@name Presence management */
  //@{
    /**Add a presentity.
       If the presentity is already present, a new one is not added, and the
       existing instance is returned.

       Returns a Read/Write locked pointer to presentity.
      */
    virtual PSafePtr<OpalPresentity> AddPresentity(
      const PString & presentity  ///< Presentity URI
    );

    /**Get a presentity.
      */
    virtual PSafePtr<OpalPresentity> GetPresentity(
      const PString & presentity,         ///< Presentity URI
      PSafetyMode mode = PSafeReference   ///< Safety mode for presentity
    );

    /**Get all presentities.
      */
    virtual PStringList GetPresentities() const;

    /**Remove a presentity.
      */
    virtual bool RemovePresentity(
      const PString & presentity  ///< Presentity URI
    );
  //@}

  /**@name Instant Messaging management */
  //@{
    /**Send an IM to a remote party.
     */
    virtual PBoolean Message(
      const PString & to, 
      const PString & body
    );
    virtual PBoolean Message(
      const PURL & to, 
      const PString & type,
      const PString & body,
      PURL & from, 
      PString & conversationId
    );
    virtual PBoolean Message(
      OpalIM & message
    );

    /**Called when text message received
     */
    virtual void OnMessageReceived(
      const OpalIM & message
    );

  //@}

  /**@name Connection management */
  //@{
    /**Set up a connection to a remote party.
       An appropriate protocol (endpoint) is determined from the party
       parameter. That endpoint is then called to create a connection and that
       connection is attached to the call provided.
       
       If the endpoint is already occupied in a call then the endpoints list
       is further searched for additional endpoints that support the protocol.
       For example multiple pstn endpoints may be present for multiple LID's.
       
       The general form for this party parameter is:

            [proto:][alias@][transport$]address[:port]

       where the various fields will have meanings specific to the endpoint
       type. For example, with H.323 it could be "h323:Fred@site.com" which
       indicates a user Fred at gatekeeper size.com. Whereas for the PSTN
       endpoint it could be "pstn:5551234" which is to call 5551234 on the
       first available PSTN line.

       The default for the proto is the name of the protocol for the first
       endpoint attached to the manager. Other fields default to values on an
       endpoint basis.

       This function usually returns almost immediately with the connection
       continuing to occur in a new background thread.

       If false is returned then the connection could not be established. For
       example if a PSTN endpoint is used and the associated line is engaged
       then it may return immediately. Returning a non-NULL value does not
       mean that the connection will succeed, only that an attempt is being
       made.

       The default behaviour is pure.
     */
    virtual PSafePtr<OpalConnection> MakeConnection(
      OpalCall & call,                   ///<  Owner of connection
      const PString & party,             ///<  Party to call
      void * userData = NULL,            ///<  user data to pass to connections
      unsigned int options = 0,          ///<  options to pass to conneciton
      OpalConnection::StringOptions * stringOptions = NULL ///< Options to pass to connection
    );

    /**Call back for a new connection has been constructed.
       This is called after CreateConnection has returned a new connection.
       It allows an application to make any custom adjustments to the
       connection before it begins to process the protocol. behind it.
      */
    virtual void OnNewConnection(
      OpalConnection & connection   ///< New connection just created
    );

    /**Call back for answering an incoming call.
       This function is used for an application to control the answering of
       incoming calls.

       If true is returned then the connection continues. If false then the
       connection is aborted.

       Note this function should not block for any length of time. If the
       decision to answer the call may take some time eg waiting for a user to
       pick up the phone, then AnswerCallPending or AnswerCallDeferred should
       be returned.

       If an application overrides this function, it should generally call the
       ancestor version to complete calls. Unless the application completely
       takes over that responsibility. Generally, an application would only
       intercept this function if it wishes to do some form of logging. For
       this you can obtain the name of the caller by using the function
       OpalConnection::GetRemotePartyName().

       The default behaviour is to call OnRouteConnection to determine a
       B party for the connection.

       If the call associated with the incoming call already had two parties
       and this connection is a third party for a conference call then
       AnswerCallNow is returned as a B party is not required.
     */
    virtual PBoolean OnIncomingConnection(
      OpalConnection & connection,   ///<  Connection that is calling
      unsigned options,              ///<  options for new connection (can't use default as overrides will fail)
      OpalConnection::StringOptions * stringOptions ///< Options to pass to connection
    );

    /**Route a connection to another connection from an endpoint.

       The default behaviour gets the destination address from the connection
       and translates it into an address by using the routeTable member
       variable and uses MakeConnection() to start the B-party connection.
      */
    virtual bool OnRouteConnection(
      PStringSet & routesTried,     ///< Set of routes already tried
      const PString & a_party,      ///< Source local address
      const PString & b_party,      ///< Destination indicated by source
      OpalCall & call,              ///< Call for new connection
      unsigned options,             ///< Options for new connection (can't use default as overrides will fail)
      OpalConnection::StringOptions * stringOptions ///< Options to pass to connection
    );

    /**Call back for remote party is now responsible for completing the call.
       This function is called when the remote system has been contacted and it
       has accepted responsibility for completing, or failing, the call. This
       is distinct from OnAlerting() in that it is not known at this time if
       anything is ringing. This indication may be used to distinguish between
       "transport" level error, in which case another host may be tried, and
       that finalising the call has moved "upstream" and the local system has
       no more to do but await a result.

       If an application overrides this function, it should generally call the
       ancestor version for correct operation.

       The default behaviour calls the OnProceeding() on the connection's
       associated OpalCall object.
     */
    virtual void OnProceeding(
      OpalConnection & connection   ///<  Connection that is proceeeding
    );

    /**Call back for remote party being alerted on outgoing call.
       This function is called after the connection is informed that the
       remote endpoint is "ringing". This function is generally called
       some time after the MakeConnection() function was called.

       If false is returned the connection is aborted.

       If an application overrides this function, it should generally call the
       ancestor version for correct operation. An application would typically
       only intercept this function if it wishes to do some form of logging.
       For this you can obtain the name of the caller by using the function
       OpalConnection::GetRemotePartyName().

       The default behaviour calls the OnAlerting() on the connection's
       associated OpalCall object.
     */
    virtual void OnAlerting(
      OpalConnection & connection   ///<  Connection that was established
    );

    /**Call back for answering an incoming call.
       This function is called after the connection has been acknowledged
       but before the connection is established

       This gives the application time to wait for some event before
       signalling to the endpoint that the connection is to proceed. For
       example the user pressing an "Answer call" button.

       If AnswerCallDenied is returned the connection is aborted and the
       connetion specific end call PDU is sent. If AnswerCallNow is returned 
       then the connection proceeding, Finally if AnswerCallPending is returned then the
       protocol negotiations are paused until the AnsweringCall() function is
       called.

       The default behaviour simply returns AnswerNow.
     */
    virtual OpalConnection::AnswerCallResponse OnAnswerCall(
      OpalConnection & connection,    ///<  connection that is being answered
       const PString & caller         ///<  caller
    );

    /**A call back function whenever a connection is "connected".
       This indicates that a connection to an endpoint was connected. That
       is the endpoint received acknowledgement via whatever protocol it uses
       that the connection may now start media streams.

       In the context of H.323 this means that the CONNECT pdu has been
       received.

       The default behaviour calls the OnConnected() on the connections
       associated OpalCall object.
      */
    virtual void OnConnected(
      OpalConnection & connection   ///<  Connection that was established
    );

    /**A call back function whenever a connection is "established".
       This indicates that a connection to an endpoint was established. This
       usually occurs after OnConnected() and indicates that the connection
       is both connected and has media flowing.

       In the context of H.323 this means that the CONNECT pdu has been
       received and either fast start was in operation or the subsequent Open
       Logical Channels have occurred. For SIP it indicates the INVITE/OK/ACK
       sequence is complete.

       The default behaviour calls the OnEstablished() on the connection's
       associated OpalCall object.
      */
    virtual void OnEstablished(
      OpalConnection & connection   ///<  Connection that was established
    );

    /**A call back function whenever a connection is released.
       This function can do any internal cleaning up and waiting on background
       threads that may be using the connection object.

       Classes that override this function should make sure they call the
       ancestor version for correct operation.

       An application will not typically call this function as it is used by
       the OpalManager during a release of the connection.

       The default behaviour calls OnReleased() on the connection's
       associated OpalCall object. This indicates to the call that the
       connection has been released so it can release the last remaining
       connection and then returns true.
      */
    virtual void OnReleased(
      OpalConnection & connection   ///<  Connection that was established
    );
    
    /**A call back function whenever a connection is "held" or "retrieved".
       This indicates that a connection to an endpoint was held, or
       retrieved, either locally or by the remote endpoint.

       The default behaviour does nothing.
      */
    virtual void OnHold(
      OpalConnection & connection,   ///<  Connection that was held/retrieved
      bool fromRemote,               ///<  Indicates remote has held local connection
      bool onHold                    ///<  Indicates have just been held/retrieved.
    );
    virtual void OnHold(OpalConnection & connection); // For backward compatibility

    /**A call back function whenever a connection is forwarded.

       The default behaviour does nothing.
      */
    virtual PBoolean OnForwarded(
      OpalConnection & connection,  ///<  Connection that was held
      const PString & remoteParty         ///<  The new remote party
    );

    /**A call back function to monitor the progress of a transfer.
       When a transfer operation is initiated, the Transfer() function will
       generally return immediately and the transfer may take some time. This
       call back can give an indication to the application of the progress of
       the transfer.
       the transfer.

       For example in SIP, the OpalCall::Transfer() function will have sent a
       REFER request to the remote party. The remote party sends us NOTIFY
       requests about the progress of the REFER request.

       An application can now make a decision during the transfer operation
       to short circuit the sequence, or let it continue. It can also
       determine if the transfer did not go through, and it should "take back"
       the call. Note no action is required to "take back" the call other than
       indicate to the user that they are back on.

       A return value of false will immediately disconnect the current call.

       The exact format of the \p info parameter is dependent on the protocol
       being used. As a minimum, it will always have a values info["result"]
       and info["party"].

       The info["party"] indicates the part the \p connection is playing in
       the transfer. This will be:
          "A"   party being transferred
          "B"   party initiating the transfer of "A"
          "C"   party "A" is being transferred to

       The info["result"] will be at least one of the following:
          "success"     Transfer completed successfully (party A or B)
          "incoming"    New call was from a transfer (party C)
          "started"     Transfer operation has started (party A)
          "progress"    Transfer is in progress (party B)
          "blind"       Transfer is blind, no further notification (party B)
          "error"       Transfer could not begin (party B)
          "failed"      Transfer started but did not complete (party A or B)

       For SIP, there may be an additional info["state"] containing the NOTIFY
       subscription state, an info["code"] entry containing the 3 digit
       code returned in the NOTIFY body and info["Referred-By"] indicating the
       URI of party B. Other fields may also be present.

       The default behaviour returns false if info["result"] == "success".
      */
    virtual bool OnTransferNotify(
      OpalConnection & connection,  ///< Connection being transferred.
      const PStringToString & info  ///< Information on the transfer
    );
  //@}


  /**@name Media Streams management */
  //@{
    /**Get common media formats.
       This is called by various places to get common media formats for the
       basic connection classes.

       The default behaviour uses the mediaFormatOrder and mediaFormatMask
       member variables to adjust the mediaFormats list.
      */
    virtual OpalMediaFormatList GetCommonMediaFormats(
      bool transportable,  ///< Include transportable media formats
      bool pcmAudio        ///< Include raw PCM audio media formats
    ) const;

    /**Adjust media formats available on a connection.
       This is called by a connection after it has called
       OpalCall::GetMediaFormats() to get all media formats that it can use so
       that an application may remove or reorder the media formats before they
       are used to open media streams.

       The default behaviour uses the mediaFormatOrder and mediaFormatMask
       member variables to adjust the mediaFormats list.
      */
    virtual void AdjustMediaFormats(
      bool local,                         ///<  Media formats a local ones to be presented to remote
      const OpalConnection & connection,  ///<  Connection that is about to use formats
      OpalMediaFormatList & mediaFormats  ///<  Media formats to use
    ) const;

    /**See if the media can bypass the local host.
     */
    virtual PBoolean IsMediaBypassPossible(
      const OpalConnection & source,      ///<  Source connection
      const OpalConnection & destination, ///<  Destination connection
      unsigned sessionID                  ///<  Session ID for media channel
    ) const;

    /**Call back when opening a media stream.
       This function is called when a connection has created a new media
       stream according to the logic of its underlying protocol.

       The usual requirement is that media streams are created on all other
       connections participating in the call and all of the media streams are
       attached to an instance of an OpalMediaPatch object that will read from
       one of the media streams passing data to the other media streams.

       The default behaviour achieves the above using the FindMatchingCodecs()
       to determine what (if any) software codecs are required, the
       OpalConnection::CreateMediaStream() function to open streams and the
       CreateMediaPatch() function to create a patch for all of the streams
       and codecs just produced.
      */
    virtual PBoolean OnOpenMediaStream(
      OpalConnection & connection,  ///<  Connection that owns the media stream
      OpalMediaStream & stream    ///<  New media stream being opened
    );

    /**Create an RTP session.
       This function allows an application to have the system create descendant
       class versions of the RTP_UDP class. The application could use this to 
	   modify the default RTP transfer behaviour.
	   This is called when a connection determines that RTP is required for 
	   transporting media.
       The default behaviour returns a new RTP_UDP.
      */
	virtual RTP_UDP * CreateRTPSession (const RTP_Session::Params & params);

	/**Callback from the RTP session for statistics monitoring.
       This is called every so many packets on the transmitter and receiver
       threads of the RTP session indicating that the statistics have been
       updated.

       The default behaviour does nothing.
      */
    virtual void OnRTPStatistics(
      const OpalConnection & connection,  ///<  Connection for the channel
      const RTP_Session & session         ///<  Session with statistics
    );

    /**Indicate is a local RTP connection.
       This is called when a new media stream has been created and it has been
       detected that media will be flowing between two RTP sessions within the
       same process. An application could take advantage of this by optimising
       the transfer in some way, rather than the full media path of codecs and
       sockets which might not be necessary.

       Note this is the complement to SetMediaPassThrough() as this function stops
       RTP data from being sent/received, while SetMediaPassThrough() transfers
       RTP data between the two endpoints.

       The default behaviour returns false.

       @return true if the application is going to execute some form of
               bypass, and the media patch threads should not be started.
      */
    virtual bool OnLocalRTP(
      OpalConnection & connection1, ///< First connection
      OpalConnection & connection2, ///< Second connection
      unsigned sessionID,           ///< Session ID of RTP session
      bool opened                   ///< Media streams are opened/closed
    ) const;

    /**Set pass though mode for media.

       Bypass the internal media handling, passing RTP data directly from
       one call/connection to another.

       This can be useful for back to back calls that happen to be the same
       media format and you wish to avoid double decoding and encoding of
       media. Note this scenario is not the same as two OpalConnections within
       the same OpalCall, but two completely independent OpalCall where one
       connection is to be bypassed. For example, two OpalCall instances might
       have two SIPConnection instances and two OpalMixerConnection instances
       connected via a single OpalMixerNode. Now while there are ONLY two
       calls in the node, it is a waste to decode the audio, add to mixer and
       re-encode it again. In practice this is identical to just bypassing the
       mixer node completely, until a third party is added, then we need to
       switch back to normal (non-pass-through) operation.

       Note this is the complement to OnLocalRTP() as this function transfers
       RTP data directly between the two endpoints, while OnLocalRTP() stops
       the RTP data from being sent/received.

       @return true if pass through is started/stopped, false if there was no
               such call/connection/stream, the streams are incompatible formats
               or a conflicting bypass is already in place.
      */
    bool SetMediaPassThrough(
      const PString & token1, ///< First calls token
      const PString & token2, ///< Second calls token
      bool bypass,            ///< Bypass the media
      unsigned sessionID = 0, ///< Session ID of media stream, 0 indicates all
      bool network  = true    ///< Pass through the network connections of the calls only
    );
    static bool SetMediaPassThrough(
      OpalConnection & connection1, ///< First connection
      OpalConnection & connection2, ///< Second connection
      bool bypass,                  ///< Bypass the media
      unsigned sessionID = 0        ///< Session ID of media stream, 0 indicates all
    );

    /**Call back for closed a media stream.

       The default behaviour does nothing.
      */
    virtual void OnClosedMediaStream(
      const OpalMediaStream & stream     ///<  Stream being closed
    );

#if OPAL_VIDEO
    /**Create a PVideoInputDevice for a source media stream.
      */
    virtual PBoolean CreateVideoInputDevice(
      const OpalConnection & connection,    ///<  Connection needing created video device
      const OpalMediaFormat & mediaFormat,  ///<  Media format for stream
      PVideoInputDevice * & device,         ///<  Created device
      PBoolean & autoDelete                     ///<  Flag for auto delete device
    );

    /**Create an PVideoOutputDevice for a sink media stream or the preview
       display for a source media stream.
      */
    virtual PBoolean CreateVideoOutputDevice(
      const OpalConnection & connection,    ///<  Connection needing created video device
      const OpalMediaFormat & mediaFormat,  ///<  Media format for stream
      PBoolean preview,                         ///<  Flag indicating is a preview output
      PVideoOutputDevice * & device,        ///<  Created device
      PBoolean & autoDelete                     ///<  Flag for auto delete device
    );
#endif

    /**Create a OpalMediaPatch instance.
       This function allows an application to have the system create descendant
       class versions of the OpalMediPatch class. The application could use
       this to modify the default behaviour of a patch.

       The default behaviour returns an instance of OpalMediaPatch.
      */
    virtual OpalMediaPatch * CreateMediaPatch(
      OpalMediaStream & source,         ///<  Source media stream
      PBoolean requiresPatchThread = true  ///< The patch requires a thread
    );

    /**Call back for a media patch thread starting.
       This function is called within the context of the thread associated
       with the media patch.

       The default behaviour does nothing
      */
    virtual void OnStartMediaPatch(
      OpalConnection & connection,  ///< Connection patch is in
      OpalMediaPatch & patch        ///< Media patch being started
    );

    /**Call back when media stream patch thread stops.
      */
    virtual void OnStopMediaPatch(
      OpalConnection & connection,  ///< Connection patch is in
      OpalMediaPatch & patch        ///< Media Patch being stopped
    );
  //@}

  /**@name User indications */
  //@{
    /**Call back for remote endpoint has sent user input as a string.

       The default behaviour call OpalConnection::SetUserInput() which
       saves the value so the GetUserInput() function can return it.
      */
    virtual void OnUserInputString(
      OpalConnection & connection,  ///<  Connection input has come from
      const PString & value         ///<  String value of indication
    );

    /**Call back for remote enpoint has sent user input as tones.
       If duration is zero then this indicates the beginning of the tone. If
       duration is non-zero then it indicates the end of the tone output.

       The default behaviour calls the OpalCall function of the same name.
      */
    virtual void OnUserInputTone(
      OpalConnection & connection,  ///<  Connection input has come from
      char tone,                    ///<  Tone received
      int duration                  ///<  Duration of tone
    );

    /**Read a sequence of user indications from connection with timeouts.
      */
    virtual PString ReadUserInput(
      OpalConnection & connection,        ///<  Connection to read input from
      const char * terminators = "#\r\n", ///<  Characters that can terminte input
      unsigned lastDigitTimeout = 4,      ///<  Timeout on last digit in string
      unsigned firstDigitTimeout = 30     ///<  Timeout on receiving any digits
    );
  //@}

  /**@name Other services */
  //@{
    /// Message waiting sub-types
    enum MessageWaitingType { 
      NoMessageWaiting,
      VoiceMessageWaiting, 
      FaxMessageWaiting,
      PagerMessageWaiting,
      MultimediaMessageWaiting,
      TextMessageWaiting,
      NumMessageWaitingTypes
    };

    /**Callback called when Message Waiting Indication (MWI) is received.
       Multiple callbacks may occur with each MessageWaitingType. A \p type
       of NumMessageWaitingTypes indicates the server is unable to distinguish
       the message type.

       The \p extraInfo parameter is generally of the form "a/b" where a and b
       unsigned integers representing new and old message count. However, it
       may be a simple "yes" or "no" if the remote cannot provide a message
       count.
     */
    virtual void OnMWIReceived(
      const PString & party,    ///< Name of party MWI is for
      MessageWaitingType type,  ///< Type of message that is waiting
      const PString & extraInfo ///< Addition information on the MWI
    );
    
    
    class RouteEntry : public PObject
    {
        PCLASSINFO(RouteEntry, PObject);
      public:
        RouteEntry(const PString & pat, const PString & dest);
        void PrintOn(ostream & strm) const;
        PString            pattern;
        PString            destination;
        PRegularExpression regex;
    };
    PARRAY(RouteTable, RouteEntry);

    /**Add a route entry to the route table.

       The specification string is of the form:

                 pattern '=' destination 
       where:

            pattern      regular expression used to select route

            destination  destination for the call

       The "pattern" string regex is compared against routing strings that are built 
       as follows:
       
                 a_party '\\t' b_party

       where:

            a_party      name associated with a local connection i.e. "pots:vpb:1/2" or
                         "h323:myname@myhost.com". 

            b_party      destination specified by the call, which may be a full URI
                         or a simple digit string

       Note that all "pattern" strings have an implied '^' at the beginning and
       a '$' at the end. This forces the "pattern" to match the entire source string. 
       For convenience, the sub-expression ".*\\t" is inserted immediately after
       any ':' character if no '\\t' is present.

       Route entries are stored and searched in the route table in the order they are added. 
       
       The "destination" string is determines the endpoint used for the outbound
       leg of the route, when a match to the "pattern" is found. It can be a literal string, 
       or can be constructed using various meta-strings that correspond to parts of the source.
       See below for a list of the available meta-strings

       A "destination" starting with the string 'label:' causes the router to restart 
       searching from the beginning of the route table using the new string as the "a_party". 
       Thus, a route table with the folllwing entries:
       
                  "label:speeddial=h323:10.0.1.1" 
                  "pots:26=label:speeddial" 

       will produce the same result as the single entry "pots:26=h323:10.0.1.1".

       If the "destination" parameter is of the form \@filename, then the file
       is read with each line consisting of a pattern=destination route
       specification. 
       
       "destination" strings without an equal sign or beginning with '#' are ignored.

       Pattern Regex Examples:
       -----------------------

       1) A local H.323 endpoint with with name of "myname@myhost.com" that receives a 
          call with a destination h323Id of "boris" would generate:
          
                          "h323:myname@myhost.com\\tboris"

       2) A local SIP endpoint with with name of "fred@nurk.com" that receives a 
          call with a destination of "sip:fred@nurk.com" would generate:
          
                          "sip:fred@nurk.com\\tsip:fred@nurk.com"

       3) Using line 0 of a PhoneJACK handset with a serial # of 01AB3F4 to dial
          the digits 2, 6 and # would generate:

                          "pots:Quicknet:01AB3F4:0\\t26"


       Destination meta-strings:
       -------------------------

       The available meta-strings are:
       
         <da>    Replaced by the "b_party" string. For example
                 "pc:.*\\t.* = sip:<da>" directs calls to the SIP protocol. In
                 this case there is a special condition where if the original
                 destination had a valid protocol, eg h323:fred.com, then
                 the entire string is replaced not just the <da> part.

         <db>    Same as <da>, but without the special condtion.

         <du>    Copy the "user" part of the "b_party" string. This is
                 essentially the component after the : and before the '@', or
                 the whole "b_party" string if these are not present.

         <!du>   The rest of the "b_party" string after the <du> section. The 
                 protocol is still omitted. This is usually the '@' and onward.
                 Note if there is already an '@' in the destination before the
                 <!du> and what is abour to replace it also has an '@' then
                 everything between the @ and the <!du> (inclusive) is deleted,
                 then the substitution is made so a legal URL can result.

         <dn>    Copy all valid consecutive E.164 digits from the "b_party" so
                 pots:0061298765\@vpb:1/2 becomes sip:0061298765@carrier.com

         <dnX>   As above but skip X digits, eg <dn2> skips 2 digits, so
                 pots:00612198765 becomes sip:61298765@carrier.com

         <!dn>   The rest of the "b_party" after the <dn> or <dnX> sections.

         <dn2ip> Translate digits separated by '*' characters to an IP
                 address. e.g. 10*0*1*1 becomes 10.0.1.1, also
                 1234*10*0*1*1 becomes 1234\@10.0.1.1 and
                 1234*10*0*1*1*1722 becomes 1234\@10.0.1.1:1722.


       Returns true if an entry was added.
      */
    virtual PBoolean AddRouteEntry(
      const PString & spec  ///<  Specification string to add
    );

    /**Parse a route table specification list for the manager.
       This removes the current routeTable and calls AddRouteEntry for every
       string in the array.

       Returns true if at least one entry was added.
      */
    PBoolean SetRouteTable(
      const PStringArray & specs  ///<  Array of specification strings.
    );

    /**Set a route table for the manager.
       Note that this will make a copy of the table and not maintain a
       reference.
      */
    void SetRouteTable(
      const RouteTable & table  ///<  New table to set for routing
    );

    /**Get the active route table for the manager.
      */
    const RouteTable & GetRouteTable() const { return m_routeTable; }

    /**Route the source address to a destination using the route table.
       The source parameter may be something like pots:vpb:1/2 or
       sip:fred\@nurk.com.

       The destination parameter is a partial URL, it does not include the
       protocol, but may be of the form user\@host, or simply digits.
      */
    virtual PString ApplyRouteTable(
      const PString & source,      ///< Source address, including endpoint protocol
      const PString & destination, ///< Destination address read from source protocol
      PINDEX & entry               ///< Index into table to start search
    );
  //@}

#if OPAL_HAS_MIXER
  /**@name Call recording */
  //@{
    /**Start recording a call.
       Current version saves to a WAV file. It may either mix the receive and
       transmit audio stream to a single mono file, or the streams are placed
       into the left and right channels of a stereo WAV file.

       Returns true if the call exists and there is no recording in progress
               for the call.
      */
    virtual PBoolean StartRecording(
      const PString & callToken,  ///< Call token for call to record
      const PFilePath & filename, ///< File into which to record
      const OpalRecordManager::Options & options = false ///< Record mixing options
    );

    /**Indicate if recording is currently active on call.
      */
    virtual bool IsRecording(
      const PString & callToken   ///< Call token for call to check if recording
    );

    /** Stop a recording.
        Returns true if the call does exists, that recording is active is
                not indicated.
      */
    virtual bool StopRecording(
      const PString & callToken   ///< Call token for call to stop recording
    );

  //@}
#endif

  /**@name Member variable access */
  //@{
    /**Get the product info for all endpoints.
      */
    const OpalProductInfo & GetProductInfo() const { return productInfo; }

    /**Set the product info for all endpoints.
      */
    void SetProductInfo(
      const OpalProductInfo & info, ///< New information
      bool updateAll = true         ///< Update all registered endpoints
    );

    /**Get the default username for all endpoints.
      */
    const PString & GetDefaultUserName() const { return defaultUserName; }

    /**Set the default username for all endpoints.
      */
    void SetDefaultUserName(
      const PString & name,   ///< New name
      bool updateAll = true   ///< Update all registered endpoints
    );

    /**Get the default display name for all endpoints.
      */
    const PString & GetDefaultDisplayName() const { return defaultDisplayName; }

    /**Set the default display name for all endpoints.
      */
    void SetDefaultDisplayName(
      const PString & name,   ///< New name
      bool updateAll = true   ///< Update all registered endpoints
    );

#if OPAL_VIDEO

    //
    // these functions are deprecated and used only for backwards compatibility
    // applications should use OpalConnection::GetAutoStart() to check whether
    // a specific media type can be auto-started
    //

    /**See if should auto-start receive video channels on connection.
     */
    bool CanAutoStartReceiveVideo() const { return (OpalMediaType::Video().GetAutoStart()&OpalMediaType::Receive) != 0; }

    /**Set if should auto-start receive video channels on connection.
     */
    void SetAutoStartReceiveVideo(bool can) { OpalMediaType::Video().GetDefinition()->SetAutoStart(OpalMediaType::Receive, can); }

    /**See if should auto-start transmit video channels on connection.
     */
    bool CanAutoStartTransmitVideo() const { return (OpalMediaType::Video().GetAutoStart()&OpalMediaType::Transmit) != 0; }

    /**Set if should auto-start transmit video channels on connection.
     */
    void SetAutoStartTransmitVideo(bool can) { OpalMediaType::Video().GetDefinition()->SetAutoStart(OpalMediaType::Transmit, can); }

#endif

    /**Determine if the address is "local", ie does not need any address
       translation (fixed or via STUN) to access.

       The default behaviour checks if remoteAddress is a RFC1918 private
       IP address: 10.x.x.x, 172.16.x.x or 192.168.x.x.
     */
    virtual PBoolean IsLocalAddress(
      const PIPSocket::Address & remoteAddress
    ) const;

    /**Determine if the RTP session needs to accommodate a NAT router.
       For endpoints that do not use STUN or something similar to set up all the
       correct protocol embeddded addresses correctly when a NAT router is between
       the endpoints, it is possible to still accommodate the call, with some
       restrictions. This function determines if the RTP can proceed with special
       NAT allowances.

       The special allowance is that the RTP code will ignore whatever the remote
       indicates in the protocol for the address to send RTP data and wait for
       the first packet to arrive from the remote and will then proceed to send
       all RTP data back to that address AND port.

       The default behaviour checks the values of the physical link
       (localAddr/peerAddr) against the signaling address the remote indicated in
       the protocol, eg H.323 SETUP sourceCallSignalAddress or SIP "To" or
       "Contact" fields, and makes a guess that the remote is behind a NAT router.
     */
    virtual PBoolean IsRTPNATEnabled(
      OpalConnection & connection,            ///< Connection being checked
      const PIPSocket::Address & localAddr,   ///< Local physical address of connection
      const PIPSocket::Address & peerAddr,    ///< Remote physical address of connection
      const PIPSocket::Address & signalAddr,  ///< Remotes signaling address as indicated by protocol of connection
      PBoolean incoming                       ///< Incoming/outgoing connection
    );

    /**Provide address translation hook.
       This will check to see that remoteAddress is NOT a local address by
       using IsLocalAddress() and if not, set localAddress to the
       translationAddress (if valid) which would normally be the router
       address of a NAT system.
     */
    virtual PBoolean TranslateIPAddress(
      PIPSocket::Address & localAddress,
      const PIPSocket::Address & remoteAddress
    );

    /**Get the translation host to use for TranslateIPAddress().
      */
    const PString & GetTranslationHost() const { return translationHost; }

    /**Set the translation host to use for TranslateIPAddress().
      */
    bool SetTranslationHost(
      const PString & host
    );

    /**Get the translation address to use for TranslateIPAddress().
      */
    const PIPSocket::Address & GetTranslationAddress() const { return translationAddress; }

    /**Set the translation address to use for TranslateIPAddress().
      */
    void SetTranslationAddress(
      const PIPSocket::Address & address
    );

    /**Return the NAT method to use.
       Returns NULL if address is a local address as per IsLocalAddress().
       Always returns the NAT method if address is zero.
       Note, the pointer is NOT to be deleted by the user.
      */
    virtual PNatMethod * GetNatMethod(
      const PIPSocket::Address & remoteAddress = PIPSocket::GetDefaultIpAny()
    ) const;

    /**Set the STUN server address, is of the form host[:port]
       Note that if the STUN server is found then the translationAddress
       is automatically set to the router address as determined by STUN.
      */
    PSTUNClient::NatTypes SetSTUNServer(
      const PString & server
    );

    /**Get the current host name and optional port for the STUN server.
      */
    const PString & GetSTUNServer() const { return stunServer; }

    /**Return the STUN client instance in use.
      */
    PSTUNClient * GetSTUNClient() const { return stun; }

    /**Get the TCP port number base for H.245 channels
     */
    WORD GetTCPPortBase() const { return tcpPorts.base; }

    /**Get the TCP port number base for H.245 channels.
     */
    WORD GetTCPPortMax() const { return tcpPorts.max; }

    /**Set the TCP port number base and max for H.245 channels.
     */
    void SetTCPPorts(unsigned tcpBase, unsigned tcpMax);

    /**Get the next TCP port number for H.245 channels
     */
    WORD GetNextTCPPort();

    /**Get the UDP port number base for RAS channels
     */
    WORD GetUDPPortBase() const { return udpPorts.base; }

    /**Get the UDP port number base for RAS channels.
     */
    WORD GetUDPPortMax() const { return udpPorts.max; }

    /**Set the TCP port number base and max for H.245 channels.
     */
    void SetUDPPorts(unsigned udpBase, unsigned udpMax);

    /**Get the next UDP port number for RAS channels
     */
    WORD GetNextUDPPort();

    /**Get the UDP port number base for RTP channels.
     */
    WORD GetRtpIpPortBase() const { return rtpIpPorts.base; }

    /**Get the max UDP port number for RTP channels.
     */
    WORD GetRtpIpPortMax() const { return rtpIpPorts.max; }

    /**Set the UDP port number base and max for RTP channels.
     */
    void SetRtpIpPorts(unsigned udpBase, unsigned udpMax);

    /**Get the UDP port number pair for RTP channels.
     */
    WORD GetRtpIpPortPair();

    /**Get the IP Type Of Service byte for media (eg RTP) channels.
     */
    BYTE GetMediaTypeOfService() const { return m_defaultMediaTypeOfService; }

    /**Set the IP Type Of Service byte for media (eg RTP) channels.
     */
    void SetMediaTypeOfService(unsigned tos) { m_defaultMediaTypeOfService = (BYTE)tos; }

    // For backward compatibility
    BYTE P_DEPRECATED GetRtpIpTypeofService() const { return m_defaultMediaTypeOfService; }
    void P_DEPRECATED SetRtpIpTypeofService(unsigned tos) { m_defaultMediaTypeOfService = (BYTE)tos; }

    /**Get the IP Type Of Service byte for media (eg RTP) channels.
     */
    BYTE GetMediaTypeOfService(const OpalMediaType & type) const;

    /**Set the IP Type Of Service byte for media (eg RTP) channels.
     */
    void SetMediaTypeOfService(const OpalMediaType & type, unsigned tos);

    /**Get the maximum transmitted RTP payload size.
       Defaults to maximum safe MTU size (576 bytes as per RFC879) minus the
       typical size of the IP, UDP an RTP headers.
      */
    PINDEX GetMaxRtpPayloadSize() const { return rtpPayloadSizeMax; }

    /**Get the maximum transmitted RTP payload size.
       Defaults to maximum safe MTU size (576 bytes as per RFC879) minus the
       typical size of the IP, UDP an RTP headers.
      */
    void SetMaxRtpPayloadSize(
      PINDEX size,
      bool mtu = false
    ) { rtpPayloadSizeMax = size - (mtu ? (20+16+12) : 0); }

    /**Get the maximum received RTP packet size.
       Defaults to 2048.
      */
    PINDEX GetMaxRtpPacketSize() const { return rtpPacketSizeMax; }

    /**Get the maximum transmitted RTP payload size.
       Defaults to 2048.
      */
    void SetMaxRtpPacketSize(
      PINDEX size
    ) { rtpPacketSizeMax = size; }

    /**Get the default maximum audio jitter delay parameter.
       Defaults to 50ms
     */
    unsigned GetMinAudioJitterDelay() const { return minAudioJitterDelay; }

    /**Get the default maximum audio jitter delay parameter.
       Defaults to 250ms.
     */
    unsigned GetMaxAudioJitterDelay() const { return maxAudioJitterDelay; }

    /**Set the maximum audio jitter delay parameter.

       If minDelay is set to zero then both the minimum and maximum will be
       set to zero which will disable the jitter buffer entirely.

       If maxDelay is zero, or just less that minDelay, then the maximum
       jitter is set to the minimum and this disables the adaptive jitter, a
       fixed value is used.
     */
    void SetAudioJitterDelay(
      unsigned minDelay,   ///<  New minimum jitter buffer delay in milliseconds
      unsigned maxDelay    ///<  New maximum jitter buffer delay in milliseconds
    );

    /**Get the default media format order.
     */
    const PStringArray & GetMediaFormatOrder() const { return mediaFormatOrder; }

    /**Set the default media format order.
     */
    void SetMediaFormatOrder(
      const PStringArray & order   ///< New order
    );

    /**Get the default media format mask.
       The is the default list of media format names to be removed from media
       format lists bfeore use by a connection.
       See OpalMediaFormatList::Remove() for more information.
     */
    const PStringArray & GetMediaFormatMask() const { return mediaFormatMask; }

    /**Set the default media format mask.
       The is the default list of media format names to be removed from media
       format lists bfeore use by a connection.
       See OpalMediaFormatList::Remove() for more information.
     */
    void SetMediaFormatMask(
      const PStringArray & mask   //< New mask
    );

    /**Set the default parameters for the silence detector.
     */
    virtual void SetSilenceDetectParams(
      const OpalSilenceDetector::Params & params
    ) { silenceDetectParams = params; }

    /**Get the default parameters for the silence detector.
     */
    const OpalSilenceDetector::Params & GetSilenceDetectParams() const { return silenceDetectParams; }
    
#if OPAL_AEC
    /**Set the default parameters for the echo cancelation.
     */
    virtual void SetEchoCancelParams(
      const OpalEchoCanceler::Params & params
    ) { echoCancelParams = params; }

    /**Get the default parameters for the silence detector.
     */
    const OpalEchoCanceler::Params & GetEchoCancelParams() const { return echoCancelParams; }
#endif

#if OPAL_VIDEO

    /**Set the parameters for the video device to be used for input.
       If the name is not suitable for use with the PVideoInputDevice class
       then the function will return false and not change the device.

       This defaults to the value of the PVideoInputDevice::GetInputDeviceNames()
       function.
     */
    virtual PBoolean SetVideoInputDevice(
      const PVideoDevice::OpenArgs & deviceArgs ///<  Full description of device
    );

    /**Get the parameters for the video device to be used for input.
       This defaults to the value of the PSoundChannel::GetInputDeviceNames()[0].
     */
    const PVideoDevice::OpenArgs & GetVideoInputDevice() const { return videoInputDevice; }

    /**Set the parameters for the video device to be used to preview input.
       If the name is not suitable for use with the PVideoOutputDevice class
       then the function will return false and not change the device.

       This defaults to the value of the PVideoInputDevice::GetOutputDeviceNames()
       function.
     */
    virtual PBoolean SetVideoPreviewDevice(
      const PVideoDevice::OpenArgs & deviceArgs ///<  Full description of device
    );

    /**Get the parameters for the video device to be used for input.
       This defaults to the value of the PSoundChannel::GetInputDeviceNames()[0].
     */
    const PVideoDevice::OpenArgs & GetVideoPreviewDevice() const { return videoPreviewDevice; }

    /**Set the parameters for the video device to be used for output.
       If the name is not suitable for use with the PVideoOutputDevice class
       then the function will return false and not change the device.

       This defaults to the value of the PVideoInputDevice::GetOutputDeviceNames()
       function.
     */
    virtual PBoolean SetVideoOutputDevice(
      const PVideoDevice::OpenArgs & deviceArgs ///<  Full description of device
    );

    /**Get the parameters for the video device to be used for input.
       This defaults to the value of the PSoundChannel::GetOutputDeviceNames()[0].
     */
    const PVideoDevice::OpenArgs & GetVideoOutputDevice() const { return videoOutputDevice; }

#endif

    PBoolean DetectInBandDTMFDisabled() const
      { return disableDetectInBandDTMF; }

    /**Set the default H.245 tunneling mode.
      */
    void DisableDetectInBandDTMF(
      PBoolean mode ///<  New default mode
    ) { disableDetectInBandDTMF = mode; } 

    /**Get the amount of time with no media that should cause a call to clear
     */
    const PTimeInterval & GetNoMediaTimeout() const { return noMediaTimeout; }

    /**Set the amount of time with no media that should cause a call to clear
     */
    PBoolean SetNoMediaTimeout(
      const PTimeInterval & newInterval  ///<  New timeout for media
    );

    /**Get the default ILS server to use for user lookup.
      */
    const PString & GetDefaultILSServer() const { return ilsServer; }

    /**Set the default ILS server to use for user lookup.
      */
    void SetDefaultILSServer(
      const PString & server
    ) { ilsServer = server; }
  //@}

    // needs to be public for gcc 3.4
    void GarbageCollection();

#ifdef OPAL_ZRTP
    virtual bool GetZRTPEnabled() const;
#endif

    virtual void OnApplyStringOptions(
      OpalConnection & conn,
      OpalConnection::StringOptions & stringOptions
    );

  protected:
    // Configuration variables
    OpalProductInfo productInfo;

    PString       defaultUserName;
    PString       defaultDisplayName;

    BYTE                     m_defaultMediaTypeOfService;
    map<OpalMediaType, BYTE> m_mediaTypeOfService;

    PINDEX        rtpPayloadSizeMax;
    PINDEX        rtpPacketSizeMax;
    unsigned      minAudioJitterDelay;
    unsigned      maxAudioJitterDelay;
    PStringArray  mediaFormatOrder;
    PStringArray  mediaFormatMask;
    PBoolean          disableDetectInBandDTMF;
    PTimeInterval noMediaTimeout;
    PString       ilsServer;

    OpalSilenceDetector::Params silenceDetectParams;
#if OPAL_AEC
    OpalEchoCanceler::Params echoCancelParams;
#endif

#if OPAL_VIDEO
    PVideoDevice::OpenArgs videoInputDevice;
    PVideoDevice::OpenArgs videoPreviewDevice;
    PVideoDevice::OpenArgs videoOutputDevice;
#endif

    struct PortInfo {
      void Set(
        unsigned base,
        unsigned max,
        unsigned range,
        unsigned dflt
      );
      WORD GetNext(
        unsigned increment
      );

      PMutex mutex;
      WORD   base;
      WORD   max;
      WORD   current;
    } tcpPorts, udpPorts, rtpIpPorts;
    
    class InterfaceMonitor : public PInterfaceMonitorClient
    {
      PCLASSINFO(InterfaceMonitor, PInterfaceMonitorClient);
      
      enum {
        OpalManagerInterfaceMonitorClientPriority = 100,
      };
      public:
        InterfaceMonitor(OpalManager & manager);
        
      protected:
        virtual void OnAddInterface(const PIPSocket::InterfaceEntry & entry);
        virtual void OnRemoveInterface(const PIPSocket::InterfaceEntry & entry);
        
        OpalManager & m_manager;
    };

    PString            translationHost;
    PIPSocket::Address translationAddress;
    PString            stunServer;
    PSTUNClient      * stun;
    InterfaceMonitor * interfaceMonitor;

    RouteTable m_routeTable;
    PMutex     m_routeMutex;

    // Dynamic variables
    PReadWriteMutex     endpointsMutex;
    PList<OpalEndPoint> endpointList;
    std::map<PString, OpalEndPoint *> endpointMap;

    PAtomicInteger lastCallTokenID;

    class CallDict : public PSafeDictionary<PString, OpalCall>
    {
      public:
        CallDict(OpalManager & mgr) : manager(mgr) { }
        virtual void DeleteObject(PObject * object) const;
        OpalManager & manager;
    } activeCalls;

    PSafeDictionary<PString, OpalPresentity> m_presentities;

    PAtomicInteger m_clearingAllCallsCount;
    PMutex         m_clearingAllCallsMutex;
    PSyncPoint     m_allCallsCleared;
    void InternalClearAllCalls(OpalConnection::CallEndReason reason, bool wait, bool first);

    PThread    * garbageCollector;
    PSyncPoint   garbageCollectExit;
    bool         garbageCollectSkip;
    PDECLARE_NOTIFIER(PThread, OpalManager, GarbageMain);

#ifdef OPAL_ZRTP
    bool zrtpEnabled;
#endif

    friend OpalCall::OpalCall(OpalManager & mgr);
    friend void OpalCall::InternalOnClear();

  private:
    P_REMOVE_VIRTUAL(OpalCall *,CreateCall(), 0);
    P_REMOVE_VIRTUAL(PBoolean, OnIncomingConnection(OpalConnection &, unsigned), false);
    P_REMOVE_VIRTUAL(PBoolean, OnIncomingConnection(OpalConnection &), false);
    P_REMOVE_VIRTUAL(PBoolean, OnStartMediaPatch(const OpalMediaPatch &), false);
    P_REMOVE_VIRTUAL_VOID(AdjustMediaFormats(const OpalConnection &, OpalMediaFormatList &) const);
    P_REMOVE_VIRTUAL_VOID(OnMessageReceived(const PURL&,const PString&,const PURL&,const PString&,const PString&,const PString&));


#ifdef OPAL_HAS_IM
  public:
    OpalIMManager & GetIMManager() { return *m_imManager; }

  protected:
    OpalIMManager * m_imManager;
#endif
};


PString  OpalGetVersion();
unsigned OpalGetMajorVersion();
unsigned OpalGetMinorVersion();
unsigned OpalGetBuildNumber();


#endif // OPAL_OPAL_MANAGER_H


// End of File ///////////////////////////////////////////////////////////////